diff options
Diffstat (limited to 'src/ast')
-rw-r--r-- | src/ast/fwd.hpp | 12 | ||||
-rw-r--r-- | src/ast/item.cpp | 27 | ||||
-rw-r--r-- | src/ast/item.hpp | 24 | ||||
-rw-r--r-- | src/ast/item_test.cpp | 16 | ||||
-rw-r--r-- | src/ast/npc.cpp | 260 | ||||
-rw-r--r-- | src/ast/npc.hpp | 69 | ||||
-rw-r--r-- | src/ast/npc_test.cpp | 214 | ||||
-rw-r--r-- | src/ast/script.hpp | 4 |
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); |