summaryrefslogtreecommitdiff
path: root/src/ast
diff options
context:
space:
mode:
Diffstat (limited to 'src/ast')
-rw-r--r--src/ast/fwd.hpp12
-rw-r--r--src/ast/item.cpp27
-rw-r--r--src/ast/item.hpp24
-rw-r--r--src/ast/item_test.cpp16
-rw-r--r--src/ast/npc.cpp260
-rw-r--r--src/ast/npc.hpp69
-rw-r--r--src/ast/npc_test.cpp214
-rw-r--r--src/ast/script.hpp4
8 files changed, 372 insertions, 254 deletions
diff --git a/src/ast/fwd.hpp b/src/ast/fwd.hpp
index 4ab4490..24bf545 100644
--- a/src/ast/fwd.hpp
+++ b/src/ast/fwd.hpp
@@ -23,11 +23,23 @@
#include "../compat/fwd.hpp" // rank 2
#include "../io/fwd.hpp" // rank 4
#include "../net/fwd.hpp" // rank 5
+#include "../sexpr/fwd.hpp" // rank 5
#include "../mmo/fwd.hpp" // rank 6
#include "../high/fwd.hpp" // rank 9
// ast/fwd.hpp is rank 10
namespace tmwa
{
+namespace ast
+{
+namespace npc
+{
+class Warp;
+} // namespace npc
+namespace script
+{
+class ScriptBody;
+} // namespace script
+} // namespace ast
// meh, add more when I feel like it
} // namespace tmwa
diff --git a/src/ast/item.cpp b/src/ast/item.cpp
index 99417d8..bd4f295 100644
--- a/src/ast/item.cpp
+++ b/src/ast/item.cpp
@@ -18,8 +18,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
-#include "../compat/memory.hpp"
-
#include "../io/extract.hpp"
#include "../io/line.hpp"
@@ -36,9 +34,6 @@ namespace item
{
using io::respan;
- // separate file because virtual
- ItemOrComment::~ItemOrComment() {}
-
static
void skip_comma_space(io::LineCharReader& lr)
{
@@ -107,6 +102,7 @@ namespace item
opt.implicit_start = true;
opt.implicit_end = true;
opt.one_line = true;
+ opt.no_event = true;
auto rv = ast::script::parse_script_body(lr, opt);
if (rv.get_success().is_some())
{
@@ -115,20 +111,19 @@ namespace item
return rv;
}
-#define SPAN_EXTRACT(bitexpr, var) ({ auto bit = bitexpr; if (!extract(bit.data, &var.data)) return Err(bit.span.error_str("failed to extract"_s #var)); var.span = bit.span; })
+#define SPAN_EXTRACT(bitexpr, var) ({ auto bit = bitexpr; if (!extract(bit.data, &var.data)) return Err(bit.span.error_str("failed to extract "_s #var)); var.span = bit.span; })
#define EOL_ERROR(lr) ({ io::LineChar c; lr.get(c) ? Err(c.error_str("unexpected EOL"_s)) : Err("unexpected EOF before unexpected EOL"_s); })
- Result<std::unique_ptr<ItemOrComment>> parse_item(io::LineCharReader& lr)
+ Option<Result<ItemOrComment>> parse_item(io::LineCharReader& lr)
{
- std::unique_ptr<ItemOrComment> rv = nullptr;
- Spanned<RString> first = TRY_UNWRAP(lex_nonscript(lr, true), return Ok(std::move(rv)));
+ Spanned<RString> first = TRY_UNWRAP(lex_nonscript(lr, true), return None);
if (first.data.startswith("//"_s))
{
Comment comment;
- comment.span = first.span;
comment.comment = first.data;
- rv = make_unique<Comment>(std::move(comment));
- return Ok(std::move(rv));
+ ItemOrComment rv = std::move(comment);
+ rv.span = first.span;
+ return Some(Ok(std::move(rv)));
}
Item item;
SPAN_EXTRACT(first, item.id);
@@ -150,10 +145,10 @@ namespace item
SPAN_EXTRACT(TRY_UNWRAP(lex_nonscript(lr, false), return EOL_ERROR(lr)), item.view);
item.use_script = TRY(lex_script(lr));
item.equip_script = TRY(lex_script(lr));
- item.span.begin = item.id.span.begin;
- item.span.end = item.equip_script.span.end;
- rv = make_unique<Item>(std::move(item));
- return Ok(std::move(rv));
+ ItemOrComment rv = std::move(item);
+ rv.span.begin = item.id.span.begin;
+ rv.span.end = item.equip_script.span.end;
+ return Some(Ok(std::move(rv)));
}
} // namespace item
} // namespace ast
diff --git a/src/ast/item.hpp b/src/ast/item.hpp
index b54e55c..a8fe908 100644
--- a/src/ast/item.hpp
+++ b/src/ast/item.hpp
@@ -20,12 +20,12 @@
#include "fwd.hpp"
-#include <memory>
-
#include "../compat/result.hpp"
#include "../io/span.hpp"
+#include "../sexpr/variant.hpp"
+
#include "../mmo/clif.t.hpp"
#include "../mmo/ids.hpp"
#include "../mmo/strs.hpp"
@@ -41,17 +41,11 @@ namespace item
{
using io::Spanned;
- struct ItemOrComment
- {
- io::LineSpan span;
-
- virtual ~ItemOrComment();
- };
- struct Comment : ItemOrComment
+ struct Comment
{
RString comment;
};
- struct Item : ItemOrComment
+ struct Item
{
Spanned<ItemNameId> id;
Spanned<ItemName> name;
@@ -74,7 +68,15 @@ namespace item
ast::script::ScriptBody equip_script;
};
- Result<std::unique_ptr<ItemOrComment>> parse_item(io::LineCharReader& lr);
+ using ItemOrCommentBase = Variant<Comment, Item>;
+ struct ItemOrComment : ItemOrCommentBase
+ {
+ ItemOrComment(Comment o) : ItemOrCommentBase(std::move(o)) {}
+ ItemOrComment(Item o) : ItemOrCommentBase(std::move(o)) {}
+ io::LineSpan span;
+ };
+
+ Option<Result<ItemOrComment>> parse_item(io::LineCharReader& lr);
} // namespace item
} // namespace ast
} // namespace tmwa
diff --git a/src/ast/item_test.cpp b/src/ast/item_test.cpp
index 3b5fd07..a77662e 100644
--- a/src/ast/item_test.cpp
+++ b/src/ast/item_test.cpp
@@ -24,7 +24,7 @@
#include "../tests/fdhack.hpp"
-//#include "../poison.hpp"
+#include "../poison.hpp"
namespace tmwa
@@ -54,7 +54,7 @@ namespace item
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
auto res = parse_item(lr);
- EXPECT_EQ(res.get_success(), Some(std::unique_ptr<ItemOrComment>(nullptr)));
+ EXPECT_TRUE(res.is_none());
}
}
TEST(itemast, comment)
@@ -70,11 +70,11 @@ namespace item
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_item(lr);
+ auto res = TRY_UNWRAP(parse_item(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- EXPECT_SPAN(top->span, 1,1, 1,8);
- auto p = dynamic_cast<Comment *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,8);
+ auto p = top.get_if<Comment>();
EXPECT_TRUE(p);
if (p)
{
@@ -96,11 +96,11 @@ namespace item
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_item(lr);
+ auto res = TRY_UNWRAP(parse_item(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- EXPECT_SPAN(top->span, 1,1, 1,58);
- auto p = dynamic_cast<Item *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,58);
+ auto p = top.get_if<Item>();
EXPECT_TRUE(p);
if (p)
{
diff --git a/src/ast/npc.cpp b/src/ast/npc.cpp
index ceb381d..6b75503 100644
--- a/src/ast/npc.cpp
+++ b/src/ast/npc.cpp
@@ -18,8 +18,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
-#include "../compat/memory.hpp"
-
#include "../io/cxxstdio.hpp"
#include "../io/extract.hpp"
#include "../io/line.hpp"
@@ -39,10 +37,7 @@ namespace npc
{
using io::respan;
- // separate file because virtual
- TopLevel::~TopLevel() {}
-
-#define TRY_EXTRACT(bit, var) ({ if (!extract(bit.data, &var.data)) return Err(bit.span.error_str("failed to extract"_s #var)); var.span = bit.span; })
+#define TRY_EXTRACT(bit, var) ({ if (!extract(bit.data, &var.data)) return Err(bit.span.error_str("failed to extract "_s #var)); var.span = bit.span; })
static
Result<Warp> parse_warp(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
@@ -53,28 +48,33 @@ namespace npc
}
if (bits[0].data.size() != 3)
{
- return Err(bits[0].span.error_str("expect 3 ,component,s"_s));
+ return Err(bits[0].span.error_str("in |component 1| expect 3 ,component,s"_s));
}
assert(bits[1].data.size() == 1);
assert(bits[1].data[0].data == "warp"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
if (bits[3].data.size() != 5)
{
- return Err(bits[3].span.error_str("expect 5 ,component,s"_s));
+ return Err(bits[3].span.error_str("in |component 4| expect 5 ,component,s"_s));
}
Warp warp;
- warp.span = span;
TRY_EXTRACT(bits[0].data[0], warp.m);
TRY_EXTRACT(bits[0].data[1], warp.x);
TRY_EXTRACT(bits[0].data[2], warp.y);
warp.key_span = bits[1].data[0].span;
TRY_EXTRACT(bits[2].data[0], warp.name);
+ if (bits[3].data[0].data == "-1"_s)
+ bits[3].data[0].data = "4294967295"_s;
TRY_EXTRACT(bits[3].data[0], warp.xs);
+ warp.xs.data += 2;
+ if (bits[3].data[1].data == "-1"_s)
+ bits[3].data[1].data = "4294967295"_s;
TRY_EXTRACT(bits[3].data[1], warp.ys);
+ warp.ys.data += 2;
TRY_EXTRACT(bits[3].data[2], warp.to_m);
TRY_EXTRACT(bits[3].data[3], warp.to_x);
TRY_EXTRACT(bits[3].data[4], warp.to_y);
@@ -89,21 +89,20 @@ namespace npc
}
if (bits[0].data.size() != 4)
{
- return Err(bits[0].span.error_str("expect 4 ,component,s"_s));
+ return Err(bits[0].span.error_str("in |component 1| expect 4 ,component,s"_s));
}
assert(bits[1].data.size() == 1);
assert(bits[1].data[0].data == "shop"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
if (bits[3].data.size() < 2)
{
- return Err(bits[3].span.error_str("expect at least 2 ,component,s"_s));
+ return Err(bits[3].span.error_str("in |component 4| expect at least 2 ,component,s"_s));
}
Shop shop;
- shop.span = span;
TRY_EXTRACT(bits[0].data[0], shop.m);
TRY_EXTRACT(bits[0].data[1], shop.x);
TRY_EXTRACT(bits[0].data[2], shop.y);
@@ -121,8 +120,8 @@ namespace npc
assert(data.span.begin.line == data.span.end.line);
item.span = data.span;
- XString name;
- if (!extract(data.data, record<':'>(&name, &item.data.value.data)))
+ XString name, value;
+ if (!extract(data.data, record<':'>(&name, &value)))
return Err(data.span.error_str("Failed to split item:value"_s));
item.data.name.span = item.span;
item.data.name.span.end.column = item.data.name.span.begin.column + name.size() - 1;
@@ -138,7 +137,22 @@ namespace npc
item.data.name.span.warning("Shop item is an id; should be a name"_s);
}
if (!extract(name, &item.data.name.data))
- return Err("item name problem (too long?)"_s);
+ {
+ return Err(item.data.name.span.error_str("item name problem (too long?)"_s));
+ }
+ if (value.startswith('-'))
+ {
+ item.data.value.span.begin.warning("Shop value multiplier should use '*' instead of '-' now"_s);
+ value = value.xslice_t(1);
+ }
+ else if (value.startswith('*'))
+ {
+ value = value.xslice_t(1);
+ }
+ if (!extract(value, &item.data.value.data))
+ {
+ return Err(item.data.value.span.error_str("invalid item value"_s));
+ }
}
return Ok(std::move(shop));
}
@@ -151,21 +165,20 @@ namespace npc
}
if (bits[0].data.size() != 3 && bits[0].data.size() != 5)
{
- return Err(bits[0].span.error_str("expect 3 or 5 ,component,s"_s));
+ return Err(bits[0].span.error_str("in |component 1| expect 3 or 5 ,component,s"_s));
}
assert(bits[1].data.size() == 1);
assert(bits[1].data[0].data == "monster"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
if (bits[3].data.size() != 2 && bits[3].data.size() != 4 && bits[3].data.size() != 5)
{
- return Err(bits[3].span.error_str("expect 2, 4, or 5 ,component,s"_s));
+ return Err(bits[3].span.error_str("in |component 4| expect 2, 4, or 5 ,component,s"_s));
}
Monster mob;
- mob.span = span;
TRY_EXTRACT(bits[0].data[0], mob.m);
TRY_EXTRACT(bits[0].data[1], mob.x);
TRY_EXTRACT(bits[0].data[2], mob.y);
@@ -231,42 +244,39 @@ namespace npc
}
if (bits[0].data.size() != 1)
{
- return Err(bits[0].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[0].span.error_str("in |component 1| expect 1 ,component,s"_s));
}
assert(bits[1].data.size() == 1);
assert(bits[1].data[0].data == "mapflag"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
- }
- if (bits.size() >= 4)
- {
- if (bits[3].data.size() != 1)
- {
- return Err(bits[3].span.error_str("expect 1 ,component,s"_s));
- }
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
MapFlag mapflag;
- mapflag.span = span;
TRY_EXTRACT(bits[0].data[0], mapflag.m);
mapflag.key_span = bits[1].data[0].span;
TRY_EXTRACT(bits[2].data[0], mapflag.name);
if (bits.size() >= 4)
{
- TRY_EXTRACT(bits[3].data[0], mapflag.opt_extra);
+ mapflag.vec_extra.span = bits[3].span;
+ for (auto& bit : bits[3].data)
+ {
+ mapflag.vec_extra.data.emplace_back();
+ TRY_EXTRACT(bit, mapflag.vec_extra.data.back());
+ }
}
else
{
- mapflag.opt_extra.data = ""_s;
- mapflag.opt_extra.span = bits[2].span;
- mapflag.opt_extra.span.end.column++;
- mapflag.opt_extra.span.begin.column = mapflag.opt_extra.span.end.column;
+ mapflag.vec_extra.data = {};
+ mapflag.vec_extra.span = bits[2].span;
+ mapflag.vec_extra.span.end.column++;
+ mapflag.vec_extra.span.begin.column = mapflag.vec_extra.span.end.column;
}
return Ok(std::move(mapflag));
}
static
- Result<ScriptFunction> parse_script_function(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
+ Result<ScriptFunction> parse_script_function_head(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
{
// ScriptFunction: function|script|Fun Name{code}
if (bits.size() != 3)
@@ -279,23 +289,17 @@ namespace npc
assert(bits[1].data[0].data == "script"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
ScriptFunction script_function;
- script_function.span = span;
script_function.key1_span = bits[0].data[0].span;
- script_function.key_span = bits[1].data[0].span;
TRY_EXTRACT(bits[2].data[0], script_function.name);
- // also expect '{' and parse real script
- ast::script::ScriptOptions opt;
- opt.implicit_start = true;
- opt.default_label = "OnCall"_s;
- script_function.body = TRY(ast::script::parse_script_body(lr, opt));
+ // also expect '{' and parse real script (in caller)
return Ok(std::move(script_function));
}
static
- Result<ScriptNone> parse_script_none(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
+ Result<ScriptNone> parse_script_none_head(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
{
// ScriptNone: -|script|script name|-1{code}
if (bits.size() != 4)
@@ -308,29 +312,23 @@ namespace npc
assert(bits[1].data[0].data == "script"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
assert(bits[3].data[0].data == "-1"_s);
if (bits[3].data.size() != 1)
{
- return Err(bits[2].span.error_str("last |component| should be just -1"_s));
+ return Err(bits[3].span.error_str("in |component 4| should be just -1"_s));
}
ScriptNone script_none;
- script_none.span = span;
script_none.key1_span = bits[0].data[0].span;
- script_none.key_span = bits[1].data[0].span;
TRY_EXTRACT(bits[2].data[0], script_none.name);
script_none.key4_span = bits[3].data[0].span;
- // also expect '{' and parse real script
- ast::script::ScriptOptions opt;
- opt.implicit_start = true;
- opt.no_start = true;
- script_none.body = TRY(ast::script::parse_script_body(lr, opt));
+ // also expect '{' and parse real script (in caller)
return Ok(std::move(script_none));
}
static
- Result<ScriptMapNone> parse_script_map_none(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
+ Result<ScriptMapNone> parse_script_map_none_head(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
{
// ScriptMapNone: m,x,y,d|script|script name|-1{code}
if (bits.size() != 4)
@@ -339,37 +337,31 @@ namespace npc
}
if (bits[0].data.size() != 4)
{
- return Err(bits[0].span.error_str("expect 3 ,component,s"_s));
+ return Err(bits[0].span.error_str("in |component 1| expect 3 ,component,s"_s));
}
assert(bits[1].data.size() == 1);
assert(bits[1].data[0].data == "script"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
if (bits[3].data.size() != 1 || bits[3].data[0].data != "-1"_s)
{
- return Err(bits[2].span.error_str("last |component| should be just -1"_s));
+ return Err(bits[3].span.error_str("in |component 4| should be just -1"_s));
}
ScriptMapNone script_map_none;
- script_map_none.span = span;
TRY_EXTRACT(bits[0].data[0], script_map_none.m);
TRY_EXTRACT(bits[0].data[1], script_map_none.x);
TRY_EXTRACT(bits[0].data[2], script_map_none.y);
TRY_EXTRACT(bits[0].data[3], script_map_none.d);
- script_map_none.key_span = bits[1].data[0].span;
TRY_EXTRACT(bits[2].data[0], script_map_none.name);
script_map_none.key4_span = bits[3].data[0].span;
- // also expect '{' and parse real script
- ast::script::ScriptOptions opt;
- opt.implicit_start = true;
- opt.no_start = true;
- script_map_none.body = TRY(ast::script::parse_script_body(lr, opt));
+ // also expect '{' and parse real script (in caller)
return Ok(std::move(script_map_none));
}
static
- Result<ScriptMap> parse_script_map(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
+ Result<ScriptMap> parse_script_map_head(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits)
{
// ScriptMap: m,x,y,d|script|script name|class,xs,ys{code}
if (bits.size() != 4)
@@ -378,41 +370,48 @@ namespace npc
}
if (bits[0].data.size() != 4)
{
- return Err(bits[0].span.error_str("expect 3 ,component,s"_s));
+ return Err(bits[0].span.error_str("in |component 1| expect 3 ,component,s"_s));
}
assert(bits[1].data.size() == 1);
assert(bits[1].data[0].data == "script"_s);
if (bits[2].data.size() != 1)
{
- return Err(bits[2].span.error_str("expect 1 ,component,s"_s));
+ return Err(bits[2].span.error_str("in |component 3| expect 1 ,component,s"_s));
}
- if (bits[3].data.size() != 3)
+ if (bits[3].data.size() != 1 && bits[3].data.size() != 3)
{
- return Err(bits[3].span.error_str("expect 3 ,component,s"_s));
+ return Err(bits[3].span.error_str("in |component 4| expect 1 or 3 ,component,s"_s));
}
ScriptMap script_map;
- script_map.span = span;
TRY_EXTRACT(bits[0].data[0], script_map.m);
TRY_EXTRACT(bits[0].data[1], script_map.x);
TRY_EXTRACT(bits[0].data[2], script_map.y);
TRY_EXTRACT(bits[0].data[3], script_map.d);
- script_map.key_span = bits[1].data[0].span;
TRY_EXTRACT(bits[2].data[0], script_map.name);
TRY_EXTRACT(bits[3].data[0], script_map.npc_class);
- TRY_EXTRACT(bits[3].data[1], script_map.xs);
- TRY_EXTRACT(bits[3].data[2], script_map.ys);
- // also expect '{' and parse real script
- ast::script::ScriptOptions opt;
- opt.implicit_start = true;
- opt.default_label = "OnClick"_s;
- script_map.body = TRY(ast::script::parse_script_body(lr, opt));
+ if (bits[3].data.size() >= 3)
+ {
+ TRY_EXTRACT(bits[3].data[1], script_map.xs);
+ script_map.xs.data = script_map.xs.data * 2 + 1;
+ TRY_EXTRACT(bits[3].data[2], script_map.ys);
+ script_map.ys.data = script_map.ys.data * 2 + 1;
+ }
+ else
+ {
+ script_map.xs.data = 0;
+ script_map.xs.span = script_map.npc_class.span;
+ script_map.xs.span.end.column++;
+ script_map.xs.span.begin = script_map.xs.span.end;
+ script_map.ys.data = 0;
+ script_map.ys.span = script_map.xs.span;
+ }
+ // also expect '{' and parse real script (in caller)
return Ok(std::move(script_map));
}
static
- Result<std::unique_ptr<Script>> parse_script_any(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
+ Result<Script> parse_script_any(io::LineSpan span, std::vector<Spanned<std::vector<Spanned<RString>>>>& bits, io::LineCharReader& lr)
{
- std::unique_ptr<Script> rv;
// 4 cases:
// ScriptFunction: function|script|Fun Name{code}
// ScriptNone: -|script|script name|-1{code}
@@ -420,21 +419,52 @@ namespace npc
// ScriptMap: m,x,y,d|script|script name|class,xs,ys{code}
if (bits[0].data[0].data == "function"_s)
{
- rv = make_unique<ScriptFunction>(TRY(parse_script_function(span, bits, lr)));
+ Script rv = TRY(parse_script_function_head(span, bits));
+ rv.key_span = bits[1].data[0].span;
+
+ ast::script::ScriptOptions opt;
+ opt.implicit_start = true;
+ opt.default_label = "OnCall"_s;
+ opt.no_event = true;
+ rv.body = TRY(ast::script::parse_script_body(lr, opt));
+ return Ok(std::move(rv));
}
else if (bits[0].data[0].data == "-"_s)
{
- rv = make_unique<ScriptNone>(TRY(parse_script_none(span, bits, lr)));
+ Script rv = TRY(parse_script_none_head(span, bits));
+ rv.key_span = bits[1].data[0].span;
+
+ ast::script::ScriptOptions opt;
+ opt.implicit_start = true;
+ opt.no_start = true;
+ rv.body = TRY(ast::script::parse_script_body(lr, opt));
+ return Ok(std::move(rv));
}
else if (bits.size() >= 4 && bits[3].data[0].data == "-1"_s)
{
- rv = make_unique<ScriptMapNone>(TRY(parse_script_map_none(span, bits, lr)));
+ Script rv = TRY(parse_script_map_none_head(span, bits));
+ rv.key_span = bits[1].data[0].span;
+
+ ast::script::ScriptOptions opt;
+ opt.implicit_start = true;
+ opt.no_start = true;
+ rv.body = TRY(ast::script::parse_script_body(lr, opt));
+ return Ok(std::move(rv));
}
else
{
- rv = make_unique<ScriptMap>(TRY(parse_script_map(span, bits, lr)));
+ ScriptMap script_map = TRY(parse_script_map_head(span, bits));
+ bool no_touch = !script_map.xs.data && !script_map.ys.data;
+ Script rv = std::move(script_map);
+ rv.key_span = bits[1].data[0].span;
+
+ ast::script::ScriptOptions opt;
+ opt.implicit_start = true;
+ opt.default_label = "OnClick"_s;
+ opt.no_touch = no_touch;
+ rv.body = TRY(ast::script::parse_script_body(lr, opt));
+ return Ok(std::move(rv));
}
- return Ok(std::move(rv));
}
/// Try to extract a top-level token
@@ -460,10 +490,29 @@ namespace npc
{
return None;
}
- // separators are simple
+ // separators are simple ... NOT.
+ // Reasonably, we should only ever eat a single char here.
+ // Unfortunately, we aren't dealing with reasonable people here.
if (c.ch() == '|' || c.ch() == ',')
{
lr.adv();
+ while (true)
+ {
+ io::LineChar c2;
+ if (!lr.get(c2) || c2.ch() == '\n' || c2.ch() == '{')
+ {
+ c.warning("Separator at EOL"_s);
+ return None;
+ }
+ if (c2.ch() == ',' || c2.ch() == '|')
+ {
+ lr.adv();
+ c = c2;
+ c.warning("Adjacent separators"_s);
+ continue;
+ }
+ break;
+ }
return Some(respan({c, c}, RString(VString<1>(c.ch()))));
}
io::LineSpan span;
@@ -488,7 +537,7 @@ namespace npc
// if first token on line, can get comment
if (first && c.ch() == '/')
{
- while (lr.get(c) && c.ch() != '\n' && c.ch() != '{')
+ while (lr.get(c) && c.ch() != '\n')
{
accum += c.ch();
span.end = c;
@@ -506,11 +555,11 @@ namespace npc
return Some(respan(span, RString(accum)));
}
- Result<std::unique_ptr<TopLevel>> parse_top(io::LineCharReader& in)
+ Option<Result<TopLevel>> parse_top(io::LineCharReader& in)
{
- std::unique_ptr<TopLevel> rv;
Spanned<std::vector<Spanned<std::vector<Spanned<RString>>>>> bits;
+ // special logic for the first 'bit'
{
Spanned<RString> mayc = TRY_UNWRAP(lex(in, true),
{
@@ -519,15 +568,15 @@ namespace npc
{
return Err(c.error_str("Unexpected script open"_s));
}
- return Ok(std::move(rv));
+ return None;
});
if (mayc.data.startswith("//"_s))
{
Comment com;
com.comment = std::move(mayc.data);
- com.span = std::move(mayc.span);
- rv = make_unique<Comment>(std::move(com));
- return Ok(std::move(rv));
+ TopLevel rv = std::move(com);
+ rv.span = std::move(mayc.span);
+ return Some(Ok(std::move(rv)));
}
if (mayc.data == "|"_s || mayc.data == ","_s)
@@ -573,29 +622,38 @@ namespace npc
Spanned<RString>& w2 = bits.data[1].data[0];
if (w2.data == "warp"_s)
{
- rv = make_unique<Warp>(TRY(parse_warp(bits.span, bits.data)));
+ TopLevel rv = TRY(parse_warp(bits.span, bits.data));
+ rv.span = bits.span;
+ return Some(Ok(std::move(rv)));
}
else if (w2.data == "shop"_s)
{
- rv = make_unique<Shop>(TRY(parse_shop(bits.span, bits.data)));
+ TopLevel rv = TRY(parse_shop(bits.span, bits.data));
+ rv.span = bits.span;
+ return Some(Ok(std::move(rv)));
}
else if (w2.data == "monster"_s)
{
- rv = make_unique<Monster>(TRY(parse_monster(bits.span, bits.data)));
+ TopLevel rv = TRY(parse_monster(bits.span, bits.data));
+ rv.span = bits.span;
+ return Some(Ok(std::move(rv)));
}
else if (w2.data == "mapflag"_s)
{
- rv = make_unique<MapFlag>(TRY(parse_mapflag(bits.span, bits.data)));
+ TopLevel rv = TRY(parse_mapflag(bits.span, bits.data));
+ rv.span = bits.span;
+ return Some(Ok(std::move(rv)));
}
else if (w2.data == "script"_s)
{
- rv = TRY_MOVE(parse_script_any(bits.span, bits.data, in));
+ TopLevel rv = TRY_MOVE(parse_script_any(bits.span, bits.data, in));
+ rv.span = bits.span;
+ return Some(Ok(std::move(rv)));
}
else
{
return Err(w2.span.error_str("Unknown type"_s));
}
- return Ok(std::move(rv));
}
} // namespace npc
} // namespace ast
diff --git a/src/ast/npc.hpp b/src/ast/npc.hpp
index dd323b6..a51acd3 100644
--- a/src/ast/npc.hpp
+++ b/src/ast/npc.hpp
@@ -20,18 +20,18 @@
#include "fwd.hpp"
-#include <memory>
-
#include "../compat/result.hpp"
#include "../io/span.hpp"
+#include "../net/timer.t.hpp"
+
+#include "../sexpr/variant.hpp"
+
#include "../mmo/clif.t.hpp"
#include "../mmo/ids.hpp"
#include "../mmo/strs.hpp"
-#include "../net/timer.t.hpp"
-
#include "script.hpp"
@@ -43,17 +43,11 @@ namespace npc
{
using io::Spanned;
- struct TopLevel
- {
- io::LineSpan span;
-
- virtual ~TopLevel();
- };
- struct Comment : TopLevel
+ struct Comment
{
RString comment;
};
- struct Warp : TopLevel
+ struct Warp
{
Spanned<MapName> m;
Spanned<unsigned> x, y;
@@ -66,9 +60,10 @@ namespace npc
struct ShopItem
{
Spanned<ItemName> name;
+ bool value_multiply;
Spanned<int> value;
};
- struct Shop : TopLevel
+ struct Shop
{
Spanned<MapName> m;
Spanned<unsigned> x, y;
@@ -78,7 +73,7 @@ namespace npc
Spanned<Species> npc_class;
Spanned<std::vector<Spanned<ShopItem>>> items;
};
- struct Monster : TopLevel
+ struct Monster
{
Spanned<MapName> m;
Spanned<unsigned> x, y;
@@ -90,32 +85,25 @@ namespace npc
Spanned<interval_t> delay1, delay2;
Spanned<NpcEvent> event;
};
- struct MapFlag : TopLevel
+ struct MapFlag
{
Spanned<MapName> m;
io::LineSpan key_span;
- // TODO should this extract all the way?
Spanned<RString> name;
- Spanned<RString> opt_extra;
- };
- struct Script : TopLevel
- {
- io::LineSpan key_span;
- // see src/script/parser.hpp
- ast::script::ScriptBody body;
+ Spanned<std::vector<Spanned<RString>>> vec_extra;
};
- struct ScriptFunction : Script
+ struct ScriptFunction
{
io::LineSpan key1_span;
Spanned<RString> name;
};
- struct ScriptNone : Script
+ struct ScriptNone
{
io::LineSpan key1_span;
Spanned<NpcName> name;
io::LineSpan key4_span;
};
- struct ScriptMapNone : Script
+ struct ScriptMapNone
{
Spanned<MapName> m;
Spanned<unsigned> x, y;
@@ -123,7 +111,7 @@ namespace npc
Spanned<NpcName> name;
io::LineSpan key4_span;
};
- struct ScriptMap : Script
+ struct ScriptMap
{
Spanned<MapName> m;
Spanned<unsigned> x, y;
@@ -132,9 +120,32 @@ namespace npc
Spanned<Species> npc_class;
Spanned<unsigned> xs, ys;
};
- // other Script subclasses elsewhere? (for item and magic scripts)
+ using ScriptBase = Variant<ScriptFunction, ScriptNone, ScriptMapNone, ScriptMap>;
+ struct Script : ScriptBase
+ {
+ Script() = default;
+ Script(ScriptFunction s) : ScriptBase(std::move(s)) {}
+ Script(ScriptNone s) : ScriptBase(std::move(s)) {}
+ Script(ScriptMapNone s) : ScriptBase(std::move(s)) {}
+ Script(ScriptMap s) : ScriptBase(std::move(s)) {}
+
+ io::LineSpan key_span;
+ ast::script::ScriptBody body;
+ };
+ using TopLevelBase = Variant<Comment, Warp, Shop, Monster, MapFlag, Script>;
+ struct TopLevel : TopLevelBase
+ {
+ TopLevel() = default;
+ TopLevel(Comment t) : TopLevelBase(std::move(t)) {}
+ TopLevel(Warp t) : TopLevelBase(std::move(t)) {}
+ TopLevel(Shop t) : TopLevelBase(std::move(t)) {}
+ TopLevel(Monster t) : TopLevelBase(std::move(t)) {}
+ TopLevel(MapFlag t) : TopLevelBase(std::move(t)) {}
+ TopLevel(Script t) : TopLevelBase(std::move(t)) {}
+ io::LineSpan span;
+ };
- Result<std::unique_ptr<TopLevel>> parse_top(io::LineCharReader& in);
+ Option<Result<TopLevel>> parse_top(io::LineCharReader& in);
} // namespace npc
} // namespace ast
} // namespace tmwa
diff --git a/src/ast/npc_test.cpp b/src/ast/npc_test.cpp
index ea4bdf3..7cced35 100644
--- a/src/ast/npc_test.cpp
+++ b/src/ast/npc_test.cpp
@@ -24,7 +24,7 @@
#include "../tests/fdhack.hpp"
-//#include "../poison.hpp"
+#include "../poison.hpp"
namespace tmwa
@@ -54,7 +54,7 @@ namespace npc
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
auto res = parse_top(lr);
- EXPECT_EQ(res.get_success(), Some(std::unique_ptr<TopLevel>(nullptr)));
+ EXPECT_TRUE(res.is_none());
}
}
TEST(npcast, comment)
@@ -70,13 +70,11 @@ namespace npc
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,8);
- auto p = dynamic_cast<Comment *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,8);
+ auto p = top.get_if<Comment>();
EXPECT_TRUE(p);
if (p)
{
@@ -91,21 +89,19 @@ namespace npc
{
// 1 2 3 4
//234567890123456789012345678901234567890123456789
- "map.gat,1,2|warp|To Other Map|3,4,other.gat,5,6"_s,
- "map.gat,1,2|warp|To Other Map|3,4,other.gat,5,6\n"_s,
- "map.gat,1,2|warp|To Other Map|3,4,other.gat,5,6{"_s,
+ "map.gat,1,2|warp|To Other Map|3,4,other.gat,7,8"_s,
+ "map.gat,1,2|warp|To Other Map|3,4,other.gat,7,8\n"_s,
+ "map.gat,1,2|warp|To Other Map|3,4,other.gat,7,8{"_s,
// no optional fields in warp
};
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,47);
- auto p = dynamic_cast<Warp *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,47);
+ auto p = top.get_if<Warp>();
EXPECT_TRUE(p);
if (p)
{
@@ -119,15 +115,15 @@ namespace npc
EXPECT_SPAN(p->name.span, 1,18, 1,29);
EXPECT_EQ(p->name.data, stringish<NpcName>("To Other Map"_s));
EXPECT_SPAN(p->xs.span, 1,31, 1,31);
- EXPECT_EQ(p->xs.data, 3);
+ EXPECT_EQ(p->xs.data, 5);
EXPECT_SPAN(p->ys.span, 1,33, 1,33);
- EXPECT_EQ(p->ys.data, 4);
+ EXPECT_EQ(p->ys.data, 6);
EXPECT_SPAN(p->to_m.span, 1,35, 1,43);
EXPECT_EQ(p->to_m.data, stringish<MapName>("other"_s));
EXPECT_SPAN(p->to_x.span, 1,45, 1,45);
- EXPECT_EQ(p->to_x.data, 5);
+ EXPECT_EQ(p->to_x.data, 7);
EXPECT_SPAN(p->to_y.span, 1,47, 1,47);
- EXPECT_EQ(p->to_y.data, 6);
+ EXPECT_EQ(p->to_y.data, 8);
}
}
}
@@ -146,13 +142,11 @@ namespace npc
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,54);
- auto p = dynamic_cast<Shop *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,54);
+ auto p = top.get_if<Shop>();
EXPECT_TRUE(p);
if (p)
{
@@ -213,13 +207,11 @@ namespace npc
bool third = input.startswith('n');
assert(first + second + third == 1);
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,first?65:54);
- auto p = dynamic_cast<Monster *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,first?65:54);
+ auto p = top.get_if<Monster>();
EXPECT_TRUE(p);
if (p)
{
@@ -309,42 +301,63 @@ namespace npc
"Map.gat|mapflag|flagname|optval"_s,
"Map.gat|mapflag|flagname|optval\n"_s,
"Map.gat|mapflag|flagname|optval{"_s,
+ "nap.gat|mapflag|flagname|aa,b,c"_s,
+ "nap.gat|mapflag|flagname|aa,b,c\n"_s,
+ "nap.gat|mapflag|flagname|aa,b,c{"_s,
};
for (auto input : inputs)
{
+ bool first = input.startswith('m');
bool second = input.startswith('M');
+ bool third = input.startswith('n');
+ EXPECT_EQ(first + second + third, 1);
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,!second?24:31);
- auto p = dynamic_cast<MapFlag *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,first?24:31);
+ auto p = top.get_if<MapFlag>();
EXPECT_TRUE(p);
if (p)
{
EXPECT_SPAN(p->m.span, 1,1, 1,7);
- if (!second)
+ if (first)
{
EXPECT_EQ(p->m.data, stringish<MapName>("map"_s));
}
- else
+ if (second)
{
EXPECT_EQ(p->m.data, stringish<MapName>("Map"_s));
}
+ if (third)
+ {
+ EXPECT_EQ(p->m.data, stringish<MapName>("nap"_s));
+ }
EXPECT_SPAN(p->key_span, 1,9, 1,15);
EXPECT_SPAN(p->name.span, 1,17, 1,24);
EXPECT_EQ(p->name.data, "flagname"_s);
- if (!second)
+ if (first)
{
- EXPECT_SPAN(p->opt_extra.span, 1,25, 1,25);
- EXPECT_EQ(p->opt_extra.data, ""_s);
+ EXPECT_SPAN(p->vec_extra.span, 1,25, 1,25);
+ EXPECT_EQ(p->vec_extra.data.size(), 0);
}
- else
+ if (second)
{
- EXPECT_SPAN(p->opt_extra.span, 1,26, 1,31);
- EXPECT_EQ(p->opt_extra.data, "optval"_s);
+ EXPECT_SPAN(p->vec_extra.span, 1,26, 1,31);
+ EXPECT_EQ(p->vec_extra.data.size(), 1);
+ EXPECT_SPAN(p->vec_extra.data[0].span, 1,26, 1,31);
+ EXPECT_EQ(p->vec_extra.data[0].data, "optval"_s);
+ }
+ if (third)
+ {
+ EXPECT_SPAN(p->vec_extra.span, 1,26, 1,31);
+ EXPECT_EQ(p->vec_extra.data.size(), 3);
+ EXPECT_SPAN(p->vec_extra.data[0].span, 1,26, 1,27);
+ EXPECT_EQ(p->vec_extra.data[0].data, "aa"_s);
+ EXPECT_SPAN(p->vec_extra.data[1].span, 1,29, 1,29);
+ EXPECT_EQ(p->vec_extra.data[1].data, "b"_s);
+ EXPECT_SPAN(p->vec_extra.data[2].span, 1,31, 1,31);
+ EXPECT_EQ(p->vec_extra.data[2].data, "c"_s);
}
}
}
@@ -366,37 +379,37 @@ namespace npc
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,24);
- auto p = dynamic_cast<ScriptFunction *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,24);
+ auto script = top.get_if<Script>();
+ EXPECT_TRUE(script);
+ auto p = script->get_if<ScriptFunction>();
EXPECT_TRUE(p);
if (p)
{
EXPECT_SPAN(p->key1_span, 1,1, 1,8);
- EXPECT_SPAN(p->key_span, 1,10, 1,15);
+ EXPECT_SPAN(script->key_span, 1,10, 1,15);
EXPECT_SPAN(p->name.span, 1,17, 1,24);
EXPECT_EQ(p->name.data, "Fun Name"_s);
if (input.endswith('}'))
{
- EXPECT_SPAN(p->body.span, 1,25, 1,30);
+ EXPECT_SPAN(script->body.span, 1,25, 1,30);
}
else if (input.endswith('\n'))
{
- EXPECT_SPAN(p->body.span, 2,1, 2,6);
+ EXPECT_SPAN(script->body.span, 2,1, 2,6);
}
else if (input.endswith(' '))
{
- EXPECT_SPAN(p->body.span, 3,2, 3,7);
+ EXPECT_SPAN(script->body.span, 3,2, 3,7);
}
else
{
FAIL();
}
- EXPECT_EQ(p->body.braced_body, "{end;}"_s);
+ EXPECT_EQ(script->body.braced_body, "{end;}"_s);
}
}
}
@@ -416,38 +429,38 @@ namespace npc
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,19);
- auto p = dynamic_cast<ScriptNone *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,19);
+ auto script = top.get_if<Script>();
+ EXPECT_TRUE(script);
+ auto p = script->get_if<ScriptNone>();
EXPECT_TRUE(p);
if (p)
{
EXPECT_SPAN(p->key1_span, 1,1, 1,1);
- EXPECT_SPAN(p->key_span, 1,3, 1,8);
+ EXPECT_SPAN(script->key_span, 1,3, 1,8);
EXPECT_SPAN(p->name.span, 1,10, 1,16);
EXPECT_EQ(p->name.data, stringish<NpcName>("#config"_s));
EXPECT_SPAN(p->key4_span, 1,18, 1,19);
if (input.endswith('}'))
{
- EXPECT_SPAN(p->body.span, 1,20, 1,25);
+ EXPECT_SPAN(script->body.span, 1,20, 1,25);
}
else if (input.endswith('\n'))
{
- EXPECT_SPAN(p->body.span, 2,1, 2,6);
+ EXPECT_SPAN(script->body.span, 2,1, 2,6);
}
else if (input.endswith(' '))
{
- EXPECT_SPAN(p->body.span, 3,2, 3,7);
+ EXPECT_SPAN(script->body.span, 3,2, 3,7);
}
else
{
FAIL();
}
- EXPECT_EQ(p->body.braced_body, "{end;}"_s);
+ EXPECT_EQ(script->body.braced_body, "{end;}"_s);
}
}
}
@@ -465,13 +478,13 @@ namespace npc
for (auto input : inputs)
{
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,28);
- auto p = dynamic_cast<ScriptMapNone *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,28);
+ auto script = top.get_if<Script>();
+ EXPECT_TRUE(script);
+ auto p = script->get_if<ScriptMapNone>();
EXPECT_TRUE(p);
if (p)
{
@@ -483,27 +496,27 @@ namespace npc
EXPECT_EQ(p->y.data, 2);
EXPECT_SPAN(p->d.span, 1,13, 1,13);
EXPECT_EQ(p->d.data, DIR::NW);
- EXPECT_SPAN(p->key_span, 1,15, 1,20);
+ EXPECT_SPAN(script->key_span, 1,15, 1,20);
EXPECT_SPAN(p->name.span, 1,22, 1,25);
EXPECT_EQ(p->name.data, stringish<NpcName>("Init"_s));
EXPECT_SPAN(p->key4_span, 1,27, 1,28);
if (input.endswith('}'))
{
- EXPECT_SPAN(p->body.span, 1,29, 1,34);
+ EXPECT_SPAN(script->body.span, 1,29, 1,34);
}
else if (input.endswith('\n'))
{
- EXPECT_SPAN(p->body.span, 2,1, 2,6);
+ EXPECT_SPAN(script->body.span, 2,1, 2,6);
}
else if (input.endswith(' '))
{
- EXPECT_SPAN(p->body.span, 3,2, 3,7);
+ EXPECT_SPAN(script->body.span, 3,2, 3,7);
}
else
{
FAIL();
}
- EXPECT_EQ(p->body.braced_body, "{end;}"_s);
+ EXPECT_EQ(script->body.braced_body, "{end;}"_s);
}
}
}
@@ -517,54 +530,77 @@ namespace npc
"map.gat,1,2,3|script|Asdf|4,5,6{end;}"_s,
"map.gat,1,2,3|script|Asdf|4,5,6\n{end;}\n"_s,
"map.gat,1,2,3|script|Asdf|4,5,6\n \n {end;} "_s,
+ "Map.gat,1,2,3|script|Asdf|40506{end;}"_s,
+ "Map.gat,1,2,3|script|Asdf|40506\n{end;}\n"_s,
+ "Map.gat,1,2,3|script|Asdf|40506\n \n {end;} "_s,
};
for (auto input : inputs)
{
+ bool second = input.startswith('M');
io::LineCharReader lr(io::from_string, "<string>"_s, input);
- auto res = parse_top(lr);
+ auto res = TRY_UNWRAP(parse_top(lr), FAIL());
EXPECT_TRUE(res.get_success().is_some());
auto top = TRY_UNWRAP(std::move(res.get_success()), FAIL());
- if (!top)
- FAIL();
- EXPECT_SPAN(top->span, 1,1, 1,31);
- auto p = dynamic_cast<ScriptMap *>(top.get());
+ EXPECT_SPAN(top.span, 1,1, 1,31);
+ auto script = top.get_if<Script>();
+ EXPECT_TRUE(script);
+ auto p = script->get_if<ScriptMap>();
EXPECT_TRUE(p);
if (p)
{
EXPECT_SPAN(p->m.span, 1,1, 1,7);
- EXPECT_EQ(p->m.data, stringish<MapName>("map"_s));
+ if (!second)
+ {
+ EXPECT_EQ(p->m.data, stringish<MapName>("map"_s));
+ }
+ else
+ {
+ EXPECT_EQ(p->m.data, stringish<MapName>("Map"_s));
+ }
EXPECT_SPAN(p->x.span, 1,9, 1,9);
EXPECT_EQ(p->x.data, 1);
EXPECT_SPAN(p->y.span, 1,11, 1,11);
EXPECT_EQ(p->y.data, 2);
EXPECT_SPAN(p->d.span, 1,13, 1,13);
EXPECT_EQ(p->d.data, DIR::NW);
- EXPECT_SPAN(p->key_span, 1,15, 1,20);
+ EXPECT_SPAN(script->key_span, 1,15, 1,20);
EXPECT_SPAN(p->name.span, 1,22, 1,25);
EXPECT_EQ(p->name.data, stringish<NpcName>("Asdf"_s));
- EXPECT_SPAN(p->npc_class.span, 1,27, 1,27);
- EXPECT_EQ(p->npc_class.data, wrap<Species>(4));
- EXPECT_SPAN(p->xs.span, 1,29, 1,29);
- EXPECT_EQ(p->xs.data, 5);
- EXPECT_SPAN(p->ys.span, 1,31, 1,31);
- EXPECT_EQ(p->ys.data, 6);
+ if (!second)
+ {
+ EXPECT_SPAN(p->npc_class.span, 1,27, 1,27);
+ EXPECT_EQ(p->npc_class.data, wrap<Species>(4));
+ EXPECT_SPAN(p->xs.span, 1,29, 1,29);
+ EXPECT_EQ(p->xs.data, 11);
+ EXPECT_SPAN(p->ys.span, 1,31, 1,31);
+ EXPECT_EQ(p->ys.data, 13);
+ }
+ else
+ {
+ EXPECT_SPAN(p->npc_class.span, 1,27, 1,31);
+ EXPECT_EQ(p->npc_class.data, wrap<Species>(40506));
+ EXPECT_SPAN(p->xs.span, 1,32, 1,32);
+ EXPECT_EQ(p->xs.data, 0);
+ EXPECT_SPAN(p->ys.span, 1,32, 1,32);
+ EXPECT_EQ(p->ys.data, 0);
+ }
if (input.endswith('}'))
{
- EXPECT_SPAN(p->body.span, 1,32, 1,37);
+ EXPECT_SPAN(script->body.span, 1,32, 1,37);
}
else if (input.endswith('\n'))
{
- EXPECT_SPAN(p->body.span, 2,1, 2,6);
+ EXPECT_SPAN(script->body.span, 2,1, 2,6);
}
else if (input.endswith(' '))
{
- EXPECT_SPAN(p->body.span, 3,2, 3,7);
+ EXPECT_SPAN(script->body.span, 3,2, 3,7);
}
else
{
FAIL();
}
- EXPECT_EQ(p->body.braced_body, "{end;}"_s);
+ EXPECT_EQ(script->body.braced_body, "{end;}"_s);
}
}
}
diff --git a/src/ast/script.hpp b/src/ast/script.hpp
index da43f90..74b11e1 100644
--- a/src/ast/script.hpp
+++ b/src/ast/script.hpp
@@ -51,6 +51,10 @@ namespace script
bool implicit_end = false;
// forbid newlines anywhere between { and }
bool one_line = false;
+ // forbid the OnTouch event
+ bool no_touch = false;
+ // forbid all events
+ bool no_event = false;
};
Result<ScriptBody> parse_script_body(io::LineCharReader& lr, ScriptOptions opt);