diff options
Diffstat (limited to 'src/map/magic-v2.cpp')
-rw-r--r-- | src/map/magic-v2.cpp | 625 |
1 files changed, 328 insertions, 297 deletions
diff --git a/src/map/magic-v2.cpp b/src/map/magic-v2.cpp index 41d29cd..73b7534 100644 --- a/src/map/magic-v2.cpp +++ b/src/map/magic-v2.cpp @@ -18,17 +18,35 @@ // 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 <cstddef> + +#include <algorithm> +#include <map> #include <set> -#include "../sexpr/parser.hpp" +#include "../strings/rstring.hpp" +#include "../strings/literal.hpp" -#include "../mmo/dumb_ptr.hpp" +#include "../generic/dumb_ptr.hpp" + +#include "../io/cxxstdio.hpp" +#include "../io/line.hpp" + +#include "../sexpr/parser.hpp" #include "itemdb.hpp" #include "magic-expr.hpp" +#include "magic-interpreter.hpp" +#include "magic-interpreter-base.hpp" +#include "magic-stmt.hpp" #include "../poison.hpp" + +namespace tmwa +{ +namespace magic +{ namespace magic_v2 { static @@ -49,8 +67,8 @@ namespace magic_v2 /* Must add new */ magic_conf_t::mcvar new_var {}; new_var.name = id_name; - new_var.val.ty = TYPE::UNDEF; - magic_conf.varv.push_back(new_var); + new_var.val = ValUndef(); + magic_conf.varv.push_back(std::move(new_var)); return i; } @@ -61,8 +79,8 @@ namespace magic_v2 if (zid != id) { FPRINTF(stderr, - "[magic-conf] INTERNAL ERROR: Builtin special var %s interned to %d, not %d as it should be!\n", - name, zid, id); + "[magic-conf] INTERNAL ERROR: Builtin special var %s interned to %d, not %d as it should be!\n"_fmt, + name, zid, id); } return zid == id; } @@ -72,45 +90,38 @@ namespace magic_v2 { bool ok = true; - ok &= INTERN_ASSERT("min_casttime", VAR_MIN_CASTTIME); - ok &= INTERN_ASSERT("obscure_chance", VAR_OBSCURE_CHANCE); - ok &= INTERN_ASSERT("caster", VAR_CASTER); - ok &= INTERN_ASSERT("spellpower", VAR_SPELLPOWER); - ok &= INTERN_ASSERT("self_spell", VAR_SPELL); - ok &= INTERN_ASSERT("self_invocation", VAR_INVOCATION); - ok &= INTERN_ASSERT("target", VAR_TARGET); - ok &= INTERN_ASSERT("script_target", VAR_SCRIPTTARGET); - ok &= INTERN_ASSERT("location", VAR_LOCATION); + ok &= INTERN_ASSERT("min_casttime"_s, VAR_MIN_CASTTIME); + ok &= INTERN_ASSERT("obscure_chance"_s, VAR_OBSCURE_CHANCE); + ok &= INTERN_ASSERT("caster"_s, VAR_CASTER); + ok &= INTERN_ASSERT("spellpower"_s, VAR_SPELLPOWER); + ok &= INTERN_ASSERT("self_spell"_s, VAR_SPELL); + ok &= INTERN_ASSERT("self_invocation"_s, VAR_INVOCATION); + ok &= INTERN_ASSERT("target"_s, VAR_TARGET); + ok &= INTERN_ASSERT("script_target"_s, VAR_SCRIPTTARGET); + ok &= INTERN_ASSERT("location"_s, VAR_LOCATION); return ok; } static - bool bind_constant(io::LineSpan span, RString name, val_t *val) + bool bind_constant(io::LineSpan span, RString name, val_t val) { - if (!const_defm.insert({name, *val}).second) + if (!const_defm.insert(std::make_pair(name, std::move(val))).second) { - span.error(STRPRINTF("Redefinition of constant '%s'", name)); + span.error(STRPRINTF("Redefinition of constant '%s'"_fmt, name)); return false; } return true; } static - val_t *find_constant(RString name) + const val_t *find_constant(RString name) { auto it = const_defm.find(name); if (it != const_defm.end()) return &it->second; - return NULL; - } - static - dumb_ptr<effect_t> new_effect(EFFECT ty) - { - auto effect = dumb_ptr<effect_t>::make(); - effect->ty = ty; - return effect; + return nullptr; } static dumb_ptr<effect_t> set_effect_continuation(dumb_ptr<effect_t> src, dumb_ptr<effect_t> continuation) @@ -123,10 +134,13 @@ namespace magic_v2 /* For FOR and FOREACH, we use special stack handlers and thus don't have to set * the continuation. It's only IF that we need to handle in this fashion. */ - if (src->ty == EFFECT::IF) + MATCH (*src) { - set_effect_continuation(src->e.e_if.true_branch, continuation); - set_effect_continuation(src->e.e_if.false_branch, continuation); + CASE (EffectIf&, e_if) + { + set_effect_continuation(e_if.true_branch, continuation); + set_effect_continuation(e_if.false_branch, continuation); + } } if (src->next) @@ -137,13 +151,6 @@ namespace magic_v2 return retval; } static - dumb_ptr<spellguard_t> new_spellguard(SPELLGUARD ty) - { - dumb_ptr<spellguard_t> retval = dumb_ptr<spellguard_t>::make(); - retval->ty = ty; - return retval; - } - static dumb_ptr<spellguard_t> spellguard_implication(dumb_ptr<spellguard_t> a, dumb_ptr<spellguard_t> b) { dumb_ptr<spellguard_t> retval = a; @@ -165,8 +172,13 @@ namespace magic_v2 } /* If the premise is a disjunction, b is the continuation of _all_ branches */ - if (a->ty == SPELLGUARD::CHOICE) - spellguard_implication(a->s.s_alt, b); + MATCH (*a) + { + CASE(const GuardChoice&, s) + { + spellguard_implication(s.s_alt, b); + } + } if (a->next) spellguard_implication(a->next, b); @@ -184,14 +196,14 @@ namespace magic_v2 auto pair1 = magic_conf.spells_by_name.insert({spell->name, spell}); if (!pair1.second) { - span.error(STRPRINTF("Attempt to redefine spell '%s'", spell->name)); + span.error(STRPRINTF("Attempt to redefine spell '%s'"_fmt, spell->name)); return false; } auto pair2 = magic_conf.spells_by_invocation.insert({spell->invocation, spell}); if (!pair2.second) { - span.error(STRPRINTF("Attempt to redefine spell invocation '%s'", spell->invocation)); + span.error(STRPRINTF("Attempt to redefine spell invocation '%s'"_fmt, spell->invocation)); magic_conf.spells_by_name.erase(pair1.first); return false; } @@ -203,14 +215,14 @@ namespace magic_v2 auto pair1 = magic_conf.anchors_by_name.insert({anchor->name, anchor}); if (!pair1.second) { - span.error(STRPRINTF("Attempt to redefine teleport anchor '%s'", anchor->name)); + span.error(STRPRINTF("Attempt to redefine teleport anchor '%s'"_fmt, anchor->name)); return false; } auto pair2 = magic_conf.anchors_by_invocation.insert({anchor->name, anchor}); if (!pair2.second) { - span.error(STRPRINTF("Attempt to redefine anchor invocation '%s'", anchor->invocation)); + span.error(STRPRINTF("Attempt to redefine anchor invocation '%s'"_fmt, anchor->invocation)); magic_conf.anchors_by_name.erase(pair1.first); return false; } @@ -223,7 +235,7 @@ namespace magic_v2 RString name = proc->name; if (!procs.insert({name, std::move(*proc)}).second) { - span.error("procedure already exists"); + span.error("procedure already exists"_s); return false; } return true; @@ -234,7 +246,7 @@ namespace magic_v2 auto pi = procs.find(name); if (pi == procs.end()) { - span.error(STRPRINTF("Unknown procedure '%s'", name)); + span.error(STRPRINTF("Unknown procedure '%s'"_fmt, name)); return false; } @@ -242,15 +254,16 @@ namespace magic_v2 if (p->argv.size() != argvp->size()) { - span.error(STRPRINTF("Procedure %s/%zu invoked with %zu parameters", + span.error(STRPRINTF("Procedure %s/%zu invoked with %zu parameters"_fmt, name, p->argv.size(), argvp->size())); return false; } - retval = new_effect(EFFECT::CALL); - retval->e.e_call.body = p->body; - retval->e.e_call.formalv = &p->argv; - retval->e.e_call.actualvp = argvp; + EffectCall e_call; + e_call.body = p->body; + e_call.formalv = &p->argv; + e_call.actualvp = argvp; + retval = dumb_ptr<effect_t>::make(e_call, nullptr); return true; } static @@ -259,33 +272,35 @@ namespace magic_v2 op_t *op = magic_get_op(name); if (!op) { - span.error(STRPRINTF("Unknown operation '%s'", name)); + span.error(STRPRINTF("Unknown operation '%s'"_fmt, name)); return false; } if (op->signature.size() != argv.size()) { - span.error(STRPRINTF("Incorrect number of arguments to operation '%s': Expected %zu, found %zu", - name, op->signature.size(), argv.size())); + span.error(STRPRINTF("Incorrect number of arguments to operation '%s': Expected %zu, found %zu"_fmt, + name, op->signature.size(), argv.size())); return false; } - effect = new_effect(EFFECT::OP); - effect->e.e_op.line_nr = span.begin.line; - effect->e.e_op.column = span.begin.column; - effect->e.e_op.opp = op; + EffectOp e_op; + e_op.line_nr = span.begin.line; + e_op.column = span.begin.column; + e_op.opp = op; assert (argv.size() <= MAX_ARGS); - effect->e.e_op.args_nr = argv.size(); + e_op.args_nr = argv.size(); - std::copy(argv.begin(), argv.end(), effect->e.e_op.args); + std::copy(argv.begin(), argv.end(), e_op.args); + effect = dumb_ptr<effect_t>::make(e_op, nullptr); return true; } static dumb_ptr<expr_t> dot_expr(dumb_ptr<expr_t> expr, int id) { - dumb_ptr<expr_t> retval = magic_new_expr(EXPR::SPELLFIELD); - retval->e.e_field.id = id; - retval->e.e_field.expr = expr; + ExprField e_field; + e_field.id = id; + e_field.expr = expr; + dumb_ptr<expr_t> retval = dumb_ptr<expr_t>::make(e_field); return retval; } @@ -295,24 +310,25 @@ namespace magic_v2 fun_t *fun = magic_get_fun(name); if (!fun) { - span.error(STRPRINTF("Unknown function '%s'", name)); + span.error(STRPRINTF("Unknown function '%s'"_fmt, name)); return false; } if (fun->signature.size() != argv.size()) { - span.error(STRPRINTF("Incorrect number of arguments to function '%s': Expected %zu, found %zu", - name, fun->signature.size(), argv.size())); + span.error(STRPRINTF("Incorrect number of arguments to function '%s': Expected %zu, found %zu"_fmt, + name, fun->signature.size(), argv.size())); return false; } - expr = magic_new_expr(EXPR::FUNAPP); - expr->e.e_funapp.line_nr = span.begin.line; - expr->e.e_funapp.column = span.begin.column; - expr->e.e_funapp.funp = fun; + ExprFunApp e_funapp; + e_funapp.line_nr = span.begin.line; + e_funapp.column = span.begin.column; + e_funapp.funp = fun; assert (argv.size() <= MAX_ARGS); - expr->e.e_funapp.args_nr = argv.size(); + e_funapp.args_nr = argv.size(); - std::copy(argv.begin(), argv.end(), expr->e.e_funapp.args); + std::copy(argv.begin(), argv.end(), e_funapp.args); + expr = dumb_ptr<expr_t>::make(e_funapp); return true; } static @@ -362,20 +378,20 @@ namespace magic_v2 return false; if (s._list[0]._type != sexpr::TOKEN) return false; - return s._list[0]._str == "DISABLED"; + return s._list[0]._str == "DISABLED"_s; } static bool parse_loc(const SExpr& s, e_location_t& loc) { if (s._type != sexpr::LIST) - return fail(s, "loc not list"); + return fail(s, "loc not list"_s); if (s._list.size() != 4) - return fail(s, "loc not 3 args"); + return fail(s, "loc not 3 args"_s); if (s._list[0]._type != sexpr::TOKEN) - return fail(s._list[0], "loc cmd not tok"); - if (s._list[0]._str != "@") - return fail(s._list[0], "loc cmd not cmd"); + return fail(s._list[0], "loc cmd not tok"_s); + if (s._list[0]._str != "@"_s) + return fail(s._list[0], "loc cmd not cmd"_s); return parse_expression(s._list[1], loc.m) && parse_expression(s._list[2], loc.x) && parse_expression(s._list[3], loc.y); @@ -389,78 +405,74 @@ namespace magic_v2 case sexpr::INT: { val_t val; - val.ty = TYPE::INT; - val.v.v_int = x._int; - if (val.v.v_int != x._int) - return fail(x, "integer too large"); + val = ValInt{static_cast<int32_t>(x._int)}; + if (val.get_if<ValInt>()->v_int != x._int) + return fail(x, "integer too large"_s); - out = magic_new_expr(EXPR::VAL); - out->e.e_val = val; + out = dumb_ptr<expr_t>::make(std::move(val)); return true; } case sexpr::STRING: { val_t val; - val.ty = TYPE::STRING; - val.v.v_string = dumb_string::copys(x._str); + val = ValString{dumb_string::copys(x._str)}; - out = magic_new_expr(EXPR::VAL); - out->e.e_val = val; + out = dumb_ptr<expr_t>::make(std::move(val)); return true; } case sexpr::TOKEN: { - ZString dirs[8] = { - ZString("S"), ZString("SW"), ZString("W"), ZString("NW"), ZString("N"), ZString("NE"), ZString("E"), ZString("SE"), - }; + earray<LString, DIR, DIR::COUNT> dirs //= + {{ + "S"_s, "SW"_s, "W"_s, "NW"_s, + "N"_s, "NE"_s, "E"_s, "SE"_s, + }}; auto begin = std::begin(dirs); auto end = std::end(dirs); auto it = std::find(begin, end, x._str); if (it != end) { val_t val; - val.ty = TYPE::DIR; - val.v.v_dir = static_cast<DIR>(it - begin); + val = ValDir{static_cast<DIR>(it - begin)}; - out = magic_new_expr(EXPR::VAL); - out->e.e_val = val; + out = dumb_ptr<expr_t>::make(std::move(val)); return true; } } { - if (val_t *val = find_constant(x._str)) + if (const val_t *val = find_constant(x._str)) { - out = magic_new_expr(EXPR::VAL); - out->e.e_val = *val; + val_t copy; + magic_copy_var(©, val); + out = dumb_ptr<expr_t>::make(std::move(copy)); return true; } else { - out = magic_new_expr(EXPR::ID); - out->e.e_id = intern_id(x._str); + ExprId e; + e.e_id = intern_id(x._str); + out = dumb_ptr<expr_t>::make(e); return true; } } break; case sexpr::LIST: if (x._list.empty()) - return fail(x, "empty list"); + return fail(x, "empty list"_s); { if (x._list[0]._type != sexpr::TOKEN) - return fail(x._list[0], "op not token"); + return fail(x._list[0], "op not token"_s); ZString op = x._list[0]._str; // area - if (op == "@") + if (op == "@"_s) { e_location_t loc; if (!parse_loc(x, loc)) return false; - out = magic_new_expr(EXPR::AREA); - out->e.e_area.ty = AREA::LOCATION; - out->e.e_area.a.a_loc = loc; + out = dumb_ptr<expr_t>::make(loc); return true; } - if (op == "@+") + if (op == "@+"_s) { e_location_t loc; dumb_ptr<expr_t> width; @@ -471,14 +483,14 @@ namespace magic_v2 return false; if (!parse_expression(x._list[3], height)) return false; - out = magic_new_expr(EXPR::AREA); - out->e.e_area.ty = AREA::RECT; - out->e.e_area.a.a_rect.loc = loc; - out->e.e_area.a.a_rect.width = width; - out->e.e_area.a.a_rect.height = height; + ExprAreaRect a_rect; + a_rect.loc = loc; + a_rect.width = width; + a_rect.height = height; + out = dumb_ptr<expr_t>::make(a_rect); return true; } - if (op == "TOWARDS") + if (op == "TOWARDS"_s) { e_location_t loc; dumb_ptr<expr_t> dir; @@ -492,41 +504,41 @@ namespace magic_v2 return false; if (!parse_expression(x._list[4], depth)) return false; - out = magic_new_expr(EXPR::AREA); - out->e.e_area.ty = AREA::BAR; - out->e.e_area.a.a_bar.loc = loc; - out->e.e_area.a.a_bar.dir = dir; - out->e.e_area.a.a_bar.width = width; - out->e.e_area.a.a_bar.depth = depth; + ExprAreaBar a_bar; + a_bar.loc = loc; + a_bar.dir = dir; + a_bar.width = width; + a_bar.depth = depth; + out = dumb_ptr<expr_t>::make(a_bar); return true; } - if (op == ".") + if (op == "."_s) { if (x._list.size() != 3) - return fail(x, ". not 2"); + return fail(x, ". not 2"_s); dumb_ptr<expr_t> expr; if (!parse_expression(x._list[1], expr)) return false; if (x._list[2]._type != sexpr::TOKEN) - return fail(x._list[2], ".elem not name"); + return fail(x._list[2], ".elem not name"_s); ZString elem = x._list[2]._str; out = dot_expr(expr, intern_id(elem)); return true; } - static + static // TODO LString std::set<ZString> ops = { - "<", ">", "<=", ">=", "==", "!=", - "+", "-", "*", "%", "/", - "&", "^", "|", "<<", ">>", - "&&", "||", + "<"_s, ">"_s, "<="_s, ">="_s, "=="_s, "!="_s, + "+"_s, "-"_s, "*"_s, "%"_s, "/"_s, + "&"_s, "^"_s, "|"_s, "<<"_s, ">>"_s, + "&&"_s, "||"_s, }; // TODO implement unary operators if (ops.count(op)) { // operators are n-ary and left-associative if (x._list.size() < 3) - return fail(x, "operator not at least 2 args"); + return fail(x, "operator not at least 2 args"_s); auto begin = x._list.begin() + 1; auto end = x._list.end(); if (!parse_expression(*begin, out)) @@ -557,7 +569,7 @@ namespace magic_v2 } static - bool parse_item(const SExpr& s, int& id, int& count) + bool parse_item(const SExpr& s, ItemNameId& id, int& count) { if (s._type == sexpr::STRING) { @@ -565,23 +577,23 @@ namespace magic_v2 item_data *item = itemdb_searchname(s._str); if (!item) - return fail(s, "no such item"); + return fail(s, "no such item"_s); id = item->nameid; return true; } if (s._type != sexpr::LIST) - return fail(s, "item not string or list"); + return fail(s, "item not string or list"_s); if (s._list.size() != 2) - return fail(s, "item list is not pair"); + return fail(s, "item list is not pair"_s); if (s._list[0]._type != sexpr::INT) - return fail(s._list[0], "item pair first not int"); + return fail(s._list[0], "item pair first not int"_s); count = s._list[0]._int; if (s._list[1]._type != sexpr::STRING) - return fail(s._list[1], "item pair second not name"); + return fail(s._list[1], "item pair second not name"_s); item_data *item = itemdb_searchname(s._list[1]._str); if (!item) - return fail(s, "no such item"); + return fail(s, "no such item"_s); id = item->nameid; return true; } @@ -590,18 +602,18 @@ namespace magic_v2 bool parse_spellguard(const SExpr& s, dumb_ptr<spellguard_t>& out) { if (s._type != sexpr::LIST) - return fail(s, "not list"); + return fail(s, "not list"_s); if (s._list.empty()) - return fail(s, "empty list"); + return fail(s, "empty list"_s); if (s._list[0]._type != sexpr::TOKEN) - return fail(s._list[0], "not token"); + return fail(s._list[0], "not token"_s); ZString cmd = s._list[0]._str; - if (cmd == "OR") + if (cmd == "OR"_s) { auto begin = s._list.begin() + 1; auto end = s._list.end(); if (begin == end) - return fail(s, "missing arguments"); + return fail(s, "missing arguments"_s); if (!parse_spellguard(*begin, out)) return false; ++begin; @@ -610,21 +622,21 @@ namespace magic_v2 dumb_ptr<spellguard_t> alt; if (!parse_spellguard(*begin, alt)) return false; - dumb_ptr<spellguard_t> choice = new_spellguard(SPELLGUARD::CHOICE); - choice->next = out; - choice->s.s_alt = alt; - out = choice; + GuardChoice choice; + auto next = out; + choice.s_alt = alt; + out = dumb_ptr<spellguard_t>::make(choice, next); } return true; } - if (cmd == "GUARD") + if (cmd == "GUARD"_s) { auto begin = s._list.begin() + 1; auto end = s._list.end(); while (is_comment(end[-1])) --end; if (begin == end) - return fail(s, "missing arguments"); + return fail(s, "missing arguments"_s); if (!parse_spellguard(end[-1], out)) return false; --end; @@ -639,68 +651,75 @@ namespace magic_v2 } return true; } - if (cmd == "REQUIRE") + if (cmd == "REQUIRE"_s) { if (s._list.size() != 2) - return fail(s, "not one argument"); + return fail(s, "not one argument"_s); dumb_ptr<expr_t> condition; if (!parse_expression(s._list[1], condition)) return false; - out = new_spellguard(SPELLGUARD::CONDITION); - out->s.s_condition = condition; + GuardCondition cond; + cond.s_condition = condition; + out = dumb_ptr<spellguard_t>::make(cond, nullptr); return true; } - if (cmd == "MANA") + if (cmd == "MANA"_s) { if (s._list.size() != 2) - return fail(s, "not one argument"); + return fail(s, "not one argument"_s); dumb_ptr<expr_t> mana; if (!parse_expression(s._list[1], mana)) return false; - out = new_spellguard(SPELLGUARD::MANA); - out->s.s_mana = mana; + GuardMana sp; + sp.s_mana = mana; + out = dumb_ptr<spellguard_t>::make(sp, nullptr); return true; } - if (cmd == "CASTTIME") + if (cmd == "CASTTIME"_s) { if (s._list.size() != 2) - return fail(s, "not one argument"); + return fail(s, "not one argument"_s); dumb_ptr<expr_t> casttime; if (!parse_expression(s._list[1], casttime)) return false; - out = new_spellguard(SPELLGUARD::CASTTIME); - out->s.s_casttime = casttime; + GuardCastTime ct; + ct.s_casttime = casttime; + out = dumb_ptr<spellguard_t>::make(ct, nullptr); return true; } - if (cmd == "CATALYSTS") + if (cmd == "CATALYSTS"_s) { dumb_ptr<component_t> items = nullptr; for (auto it = s._list.begin() + 1, end = s._list.end(); it != end; ++it) { - int id, count; + ItemNameId id; + int count; if (!parse_item(*it, id, count)) return false; magic_add_component(&items, id, count); } - out = new_spellguard(SPELLGUARD::CATALYSTS); - out->s.s_catalysts = items; + GuardCatalysts cat; + cat.s_catalysts = items; + out = dumb_ptr<spellguard_t>::make(cat, nullptr); return true; } - if (cmd == "COMPONENTS") + if (cmd == "COMPONENTS"_s) { dumb_ptr<component_t> items = nullptr; for (auto it = s._list.begin() + 1, end = s._list.end(); it != end; ++it) { - int id, count; + ItemNameId id; + int count; if (!parse_item(*it, id, count)) return false; magic_add_component(&items, id, count); } - out = new_spellguard(SPELLGUARD::COMPONENTS); - out->s.s_components = items; + GuardComponents comp; + comp.s_components = items; + out = dumb_ptr<spellguard_t>::make(comp, nullptr); return true; } - return fail(s._list[0], "unknown guard"); + return fail(s._list[0], "unknown guard"_s); } static @@ -709,7 +728,7 @@ namespace magic_v2 { // these backward lists could be forward by keeping the reference // I know this is true because Linus said so - out = new_effect(EFFECT::SKIP); + out = dumb_ptr<effect_t>::make(EffectSkip{}, nullptr); while (end != begin) { const SExpr& s = *--end; @@ -727,100 +746,102 @@ namespace magic_v2 bool parse_effect(const SExpr& s, dumb_ptr<effect_t>& out) { if (s._type != sexpr::LIST) - return fail(s, "not list"); + return fail(s, "not list"_s); if (s._list.empty()) - return fail(s, "empty list"); + return fail(s, "empty list"_s); if (s._list[0]._type != sexpr::TOKEN) - return fail(s._list[0], "not token"); + return fail(s._list[0], "not token"_s); ZString cmd = s._list[0]._str; - if (cmd == "BLOCK") + if (cmd == "BLOCK"_s) { return build_effect_list(s._list.begin() + 1, s._list.end(), out); } - if (cmd == "SET") + if (cmd == "SET"_s) { if (s._list.size() != 3) - return fail(s, "not 2 args"); + return fail(s, "not 2 args"_s); if (s._list[1]._type != sexpr::TOKEN) - return fail(s._list[1], "not token"); + return fail(s._list[1], "not token"_s); ZString name = s._list[1]._str; if (find_constant(name)) - return fail(s._list[1], "assigning to constant"); + return fail(s._list[1], "assigning to constant"_s); dumb_ptr<expr_t> expr; if (!parse_expression(s._list[2], expr)) return false; - out = new_effect(EFFECT::ASSIGN); - out->e.e_assign.id = intern_id(name); - out->e.e_assign.expr = expr; + EffectAssign e_assign; + e_assign.id = intern_id(name); + e_assign.expr = expr; + out = dumb_ptr<effect_t>::make(e_assign, nullptr); return true; } - if (cmd == "SCRIPT") + if (cmd == "SCRIPT"_s) { if (s._list.size() != 2) - return fail(s, "not 1 arg"); + return fail(s, "not 1 arg"_s); if (s._list[1]._type != sexpr::STRING) - return fail(s._list[1], "not string"); + return fail(s._list[1], "not string"_s); ZString body = s._list[1]._str; std::unique_ptr<const ScriptBuffer> script = parse_script(body, s._list[1]._span.begin.line, true); if (!script) - return fail(s._list[1], "script does not compile"); - out = new_effect(EFFECT::SCRIPT); - out->e.e_script = dumb_ptr<const ScriptBuffer>(script.release()); + return fail(s._list[1], "script does not compile"_s); + EffectScript e; + e.e_script = dumb_ptr<const ScriptBuffer>(script.release()); + out = dumb_ptr<effect_t>::make(e, nullptr); return true; } - if (cmd == "SKIP") + if (cmd == "SKIP"_s) { if (s._list.size() != 1) - return fail(s, "not 0 arg"); - out = new_effect(EFFECT::SKIP); + return fail(s, "not 0 arg"_s); + out = dumb_ptr<effect_t>::make(EffectSkip{}, nullptr); return true; } - if (cmd == "ABORT") + if (cmd == "ABORT"_s) { if (s._list.size() != 1) - return fail(s, "not 0 arg"); - out = new_effect(EFFECT::ABORT); + return fail(s, "not 0 arg"_s); + out = dumb_ptr<effect_t>::make(EffectAbort{}, nullptr); return true; } - if (cmd == "END") + if (cmd == "END"_s) { if (s._list.size() != 1) - return fail(s, "not 0 arg"); - out = new_effect(EFFECT::END); + return fail(s, "not 0 arg"_s); + out = dumb_ptr<effect_t>::make(EffectEnd{}, nullptr); return true; } - if (cmd == "BREAK") + if (cmd == "BREAK"_s) { if (s._list.size() != 1) - return fail(s, "not 0 arg"); - out = new_effect(EFFECT::BREAK); + return fail(s, "not 0 arg"_s); + out = dumb_ptr<effect_t>::make(EffectBreak{}, nullptr); return true; } - if (cmd == "FOREACH") + if (cmd == "FOREACH"_s) { if (s._list.size() != 5) - return fail(s, "not 4 arg"); + return fail(s, "not 4 arg"_s); if (s._list[1]._type != sexpr::TOKEN) - return fail(s._list[1], "foreach type not token"); + return fail(s._list[1], "foreach type not token"_s); ZString type = s._list[1]._str; FOREACH_FILTER filter; - if (type == "PC") + if (type == "PC"_s) filter = FOREACH_FILTER::PC; - else if (type == "MOB") + else if (type == "MOB"_s) filter = FOREACH_FILTER::MOB; - else if (type == "ENTITY") + else if (type == "ENTITY"_s) filter = FOREACH_FILTER::ENTITY; - else if (type == "SPELL") + else if (type == "SPELL"_s) filter = FOREACH_FILTER::SPELL; - else if (type == "TARGET") + else if (type == "TARGET"_s) filter = FOREACH_FILTER::TARGET; - else if (type == "NPC") + else if (type == "NPC"_s) filter = FOREACH_FILTER::NPC; else - return fail(s._list[1], "unknown foreach filter"); + return fail(s._list[1], "unknown foreach filter"_s); if (s._list[2]._type != sexpr::TOKEN) - return fail(s._list[2], "foreach var not token"); + return fail(s._list[2], "foreach var not token"_s); ZString var = s._list[2]._str; dumb_ptr<expr_t> area; dumb_ptr<effect_t> effect; @@ -828,19 +849,21 @@ namespace magic_v2 return false; if (!parse_effect(s._list[4], effect)) return false; - out = new_effect(EFFECT::FOREACH); - out->e.e_foreach.id = intern_id(var); - out->e.e_foreach.area = area; - out->e.e_foreach.body = effect; - out->e.e_foreach.filter = filter; + + EffectForEach e_foreach; + e_foreach.id = intern_id(var); + e_foreach.area = area; + e_foreach.body = effect; + e_foreach.filter = filter; + out = dumb_ptr<effect_t>::make(e_foreach, nullptr); return true; } - if (cmd == "FOR") + if (cmd == "FOR"_s) { if (s._list.size() != 5) - return fail(s, "not 4 arg"); + return fail(s, "not 4 arg"_s); if (s._list[1]._type != sexpr::TOKEN) - return fail(s._list[1], "for var not token"); + return fail(s._list[1], "for var not token"_s); ZString var = s._list[1]._str; dumb_ptr<expr_t> low; dumb_ptr<expr_t> high; @@ -851,17 +874,19 @@ namespace magic_v2 return false; if (!parse_effect(s._list[4], effect)) return false; - out = new_effect(EFFECT::FOR); - out->e.e_for.id = intern_id(var); - out->e.e_for.start = low; - out->e.e_for.stop = high; - out->e.e_for.body = effect; + + EffectFor e_for; + e_for.id = intern_id(var); + e_for.start = low; + e_for.stop = high; + e_for.body = effect; + out = dumb_ptr<effect_t>::make(e_for, nullptr); return true; } - if (cmd == "IF") + if (cmd == "IF"_s) { if (s._list.size() != 3 && s._list.size() != 4) - return fail(s, "not 2 or 3 args"); + return fail(s, "not 2 or 3 args"_s); dumb_ptr<expr_t> cond; dumb_ptr<effect_t> if_true; dumb_ptr<effect_t> if_false; @@ -875,30 +900,33 @@ namespace magic_v2 return false; } else - if_false = new_effect(EFFECT::SKIP); - out = new_effect(EFFECT::IF); - out->e.e_if.cond = cond; - out->e.e_if.true_branch = if_true; - out->e.e_if.false_branch = if_false; + if_false = dumb_ptr<effect_t>::make(EffectSkip{}, nullptr); + + EffectIf e_if; + e_if.cond = cond; + e_if.true_branch = if_true; + e_if.false_branch = if_false; + out = dumb_ptr<effect_t>::make(e_if, nullptr); return true; } - if (cmd == "WAIT") + if (cmd == "WAIT"_s) { if (s._list.size() != 2) - return fail(s, "not 1 arg"); + return fail(s, "not 1 arg"_s); dumb_ptr<expr_t> expr; if (!parse_expression(s._list[1], expr)) return false; - out = new_effect(EFFECT::SLEEP); - out->e.e_sleep = expr; + EffectSleep e; + e.e_sleep = expr; + out = dumb_ptr<effect_t>::make(e, nullptr); return true; } - if (cmd == "CALL") + if (cmd == "CALL"_s) { if (s._list.size() < 2) - return fail(s, "call what?"); + return fail(s, "call what?"_s); if (s._list[1]._type != sexpr::TOKEN) - return fail(s._list[1], "call token please"); + return fail(s._list[1], "call token please"_s); ZString func = s._list[1]._str; auto argvp = dumb_ptr<std::vector<dumb_ptr<expr_t>>>::make(); for (auto it = s._list.begin() + 2, end = s._list.end(); it != end; ++it) @@ -925,16 +953,16 @@ namespace magic_v2 bool parse_spellbody(const SExpr& s, dumb_ptr<spellguard_t>& out) { if (s._type != sexpr::LIST) - return fail(s, "not list"); + return fail(s, "not list"_s); if (s._list.empty()) - return fail(s, "empty list"); + return fail(s, "empty list"_s); if (s._list[0]._type != sexpr::TOKEN) - return fail(s._list[0], "not token"); + return fail(s._list[0], "not token"_s); ZString cmd = s._list[0]._str; - if (cmd == "=>") + if (cmd == "=>"_s) { if (s._list.size() != 3) - return fail(s, "list does not have exactly 2 arguments"); + return fail(s, "list does not have exactly 2 arguments"_s); dumb_ptr<spellguard_t> guard; if (!parse_spellguard(s._list[1], guard)) return false; @@ -944,10 +972,10 @@ namespace magic_v2 out = spellguard_implication(guard, body); return true; } - if (cmd == "|") + if (cmd == "|"_s) { if (s._list.size() == 1) - return fail(s, "spellbody choice empty"); + return fail(s, "spellbody choice empty"_s); auto begin = s._list.begin() + 1; auto end = s._list.end(); if (!parse_spellbody(*begin, out)) @@ -959,13 +987,13 @@ namespace magic_v2 if (!parse_spellbody(*begin, alt)) return false; auto tmp = out; - out = new_spellguard(SPELLGUARD::CHOICE); - out->next = tmp; - out->s.s_alt = alt; + GuardChoice choice; + choice.s_alt = alt; + out = dumb_ptr<spellguard_t>::make(choice, tmp); } return true; } - if (cmd == "EFFECT") + if (cmd == "EFFECT"_s) { auto begin = s._list.begin() + 1; auto end = s._list.end(); @@ -978,7 +1006,7 @@ namespace magic_v2 --end; if (end[-1]._type == sexpr::LIST && !end[-1]._list.empty() && end[-1]._list[0]._type == sexpr::TOKEN - && end[-1]._list[0]._str == "ATEND") + && end[-1]._list[0]._str == "ATEND"_s) { auto atb = end[-1]._list.begin() + 1; auto ate = end[-1]._list.end(); @@ -995,7 +1023,7 @@ namespace magic_v2 } if (end[-1]._type == sexpr::LIST && !end[-1]._list.empty() && end[-1]._list[0]._type == sexpr::TOKEN - && end[-1]._list[0]._str == "ATTRIGGER") + && end[-1]._list[0]._str == "ATTRIGGER"_s) { auto atb = end[-1]._list.begin() + 1; auto ate = end[-1]._list.end(); @@ -1009,26 +1037,27 @@ namespace magic_v2 } if (!build_effect_list(begin, end, effect)) return false; - out = new_spellguard(SPELLGUARD::EFFECT); - out->s.s_effect.effect = effect; - out->s.s_effect.at_trigger = attrig; - out->s.s_effect.at_end = atend; + effect_set_t s_effect; + s_effect.effect = effect; + s_effect.at_trigger = attrig; + s_effect.at_end = atend; + out = dumb_ptr<spellguard_t>::make(s_effect, nullptr); return true; } - return fail(s._list[0], "unknown spellbody"); + return fail(s._list[0], "unknown spellbody"_s); } static bool parse_top_set(const std::vector<SExpr>& in) { if (in.size() != 3) - return fail(in[0], "not 2 arguments"); + return fail(in[0], "not 2 arguments"_s); ZString name = in[1]._str; dumb_ptr<expr_t> expr; if (!parse_expression(in[2], expr)) return false; if (find_constant(name)) - return fail(in[1], "assign constant"); + return fail(in[1], "assign constant"_s); size_t var_id = intern_id(name); magic_eval(dumb_ptr<env_t>(&magic_default_env), &magic_conf.varv[var_id].val, expr); return true; @@ -1037,28 +1066,28 @@ namespace magic_v2 bool parse_const(io::LineSpan span, const std::vector<SExpr>& in) { if (in.size() != 3) - return fail(in[0], "not 2 arguments"); + return fail(in[0], "not 2 arguments"_s); if (in[1]._type != sexpr::TOKEN) - return fail(in[1], "not token"); + return fail(in[1], "not token"_s); ZString name = in[1]._str; dumb_ptr<expr_t> expr; if (!parse_expression(in[2], expr)) return false; val_t tmp; magic_eval(dumb_ptr<env_t>(&magic_default_env), &tmp, expr); - return bind_constant(span, name, &tmp); + return bind_constant(span, name, std::move(tmp)); } static bool parse_anchor(io::LineSpan span, const std::vector<SExpr>& in) { if (in.size() != 4) - return fail(in[0], "not 3 arguments"); + return fail(in[0], "not 3 arguments"_s); auto anchor = dumb_ptr<teleport_anchor_t>::make(); if (in[1]._type != sexpr::TOKEN) - return fail(in[1], "not token"); + return fail(in[1], "not token"_s); anchor->name = in[1]._str; if (in[2]._type != sexpr::STRING) - return fail(in[2], "not string"); + return fail(in[2], "not string"_s); anchor->invocation = in[2]._str; dumb_ptr<expr_t> expr; if (!parse_expression(in[3], expr)) @@ -1070,17 +1099,17 @@ namespace magic_v2 bool parse_proc(io::LineSpan span, const std::vector<SExpr>& in) { if (in.size() < 4) - return fail(in[0], "not at least 3 arguments"); + return fail(in[0], "not at least 3 arguments"_s); auto proc = dumb_ptr<proc_t>::make(); if (in[1]._type != sexpr::TOKEN) - return fail(in[1], "name not token"); + return fail(in[1], "name not token"_s); proc->name = in[1]._str; if (in[2]._type != sexpr::LIST) - return fail(in[2], "args not list"); + return fail(in[2], "args not list"_s); for (const SExpr& arg : in[2]._list) { if (arg._type != sexpr::TOKEN) - return fail(arg, "arg not token"); + return fail(arg, "arg not token"_s); proc->argv.push_back(intern_id(arg._str)); } if (!build_effect_list(in.begin() + 3, in.end(), proc->body)) @@ -1091,37 +1120,37 @@ namespace magic_v2 bool parse_spell(io::LineSpan span, const std::vector<SExpr>& in) { if (in.size() < 6) - return fail(in[0], "not at least 5 arguments"); + return fail(in[0], "not at least 5 arguments"_s); if (in[1]._type != sexpr::LIST) - return fail(in[1], "flags not list"); + return fail(in[1], "flags not list"_s); auto spell = dumb_ptr<spell_t>::make(); for (const SExpr& s : in[1]._list) { if (s._type != sexpr::TOKEN) - return fail(s, "flag not token"); + return fail(s, "flag not token"_s); SPELL_FLAG flag = SPELL_FLAG::ZERO; - if (s._str == "LOCAL") + if (s._str == "LOCAL"_s) flag = SPELL_FLAG::LOCAL; - else if (s._str == "NONMAGIC") + else if (s._str == "NONMAGIC"_s) flag = SPELL_FLAG::NONMAGIC; - else if (s._str == "SILENT") + else if (s._str == "SILENT"_s) flag = SPELL_FLAG::SILENT; else - return fail(s, "unknown flag"); + return fail(s, "unknown flag"_s); if (bool(spell->flags & flag)) - return fail(s, "duplicate flag"); + return fail(s, "duplicate flag"_s); spell->flags |= flag; } if (in[2]._type != sexpr::TOKEN) - return fail(in[2], "name not token"); + return fail(in[2], "name not token"_s); spell->name = in[2]._str; if (in[3]._type != sexpr::STRING) - return fail(in[3], "invoc not string"); + return fail(in[3], "invoc not string"_s); spell->invocation = in[3]._str; if (in[4]._type != sexpr::LIST) - return fail(in[4], "spellarg not list"); + return fail(in[4], "spellarg not list"_s); if (in[4]._list.size() == 0) { spell->spellarg_ty = SPELLARG::NONE; @@ -1129,18 +1158,18 @@ namespace magic_v2 else { if (in[4]._list.size() != 2) - return fail(in[4], "spellarg not empty list or pair"); + return fail(in[4], "spellarg not empty list or pair"_s); if (in[4]._list[0]._type != sexpr::TOKEN) - return fail(in[4]._list[0], "spellarg type not token"); + return fail(in[4]._list[0], "spellarg type not token"_s); if (in[4]._list[1]._type != sexpr::TOKEN) - return fail(in[4]._list[1], "spellarg name not token"); + return fail(in[4]._list[1], "spellarg name not token"_s); ZString ty = in[4]._list[0]._str; - if (ty == "PC") + if (ty == "PC"_s) spell->spellarg_ty = SPELLARG::PC; - else if (ty == "STRING") + else if (ty == "STRING"_s) spell->spellarg_ty = SPELLARG::STRING; else - return fail(in[4]._list[0], "unknown spellarg type"); + return fail(in[4]._list[0], "unknown spellarg type"_s); ZString an = in[4]._list[1]._str; spell->arg = intern_id(an); } @@ -1148,19 +1177,19 @@ namespace magic_v2 for (;; ++it) { if (it == in.end()) - return fail(it[-1], "end of list scanning LET defs"); + return fail(it[-1], "end of list scanning LET defs"_s); if (is_comment(*it)) continue; if (it->_type != sexpr::LIST || it->_list.empty()) break; - if (it->_list[0]._type != sexpr::TOKEN || it->_list[0]._str != "LET") + if (it->_list[0]._type != sexpr::TOKEN || it->_list[0]._str != "LET"_s) break; if (it->_list[1]._type != sexpr::TOKEN) - return fail(it->_list[1], "let name not token"); + return fail(it->_list[1], "let name not token"_s); ZString name = it->_list[1]._str; if (find_constant(name)) - return fail(it->_list[1], "constant exists"); + return fail(it->_list[1], "constant exists"_s); dumb_ptr<expr_t> expr; if (!parse_expression(it->_list[2], expr)) return false; @@ -1170,7 +1199,7 @@ namespace magic_v2 spell->letdefv.push_back(let); } if (it + 1 != in.end()) - return fail(*it, "expected only one body entry besides LET"); + return fail(*it, "expected only one body entry besides LET"_s); // formally, 'guard' only refers to the first argument of '=>' // but internally, spellbodies use the same thing @@ -1186,23 +1215,23 @@ namespace magic_v2 { if (vs.empty()) { - span.error("Empty list at top"); + span.error("Empty list at top"_s); return false; } if (vs[0]._type != sexpr::TOKEN) - return fail(vs[0], "top not token"); + return fail(vs[0], "top not token"_s); ZString cmd = vs[0]._str; - if (cmd == "CONST") + if (cmd == "CONST"_s) return parse_const(span, vs); - if (cmd == "PROCEDURE") + if (cmd == "PROCEDURE"_s) return parse_proc(span, vs); - if (cmd == "SET") + if (cmd == "SET"_s) return parse_top_set(vs); - if (cmd == "SPELL") + if (cmd == "SPELL"_s) return parse_spell(span, vs); - if (cmd == "TELEPORT-ANCHOR") + if (cmd == "TELEPORT-ANCHOR"_s) return parse_anchor(span, vs); - return fail(vs[0], "Unknown top-level command"); + return fail(vs[0], "Unknown top-level command"_s); } static @@ -1214,14 +1243,14 @@ namespace magic_v2 if (is_comment(s)) continue; if (s._type != sexpr::LIST) - return fail(s, "top-level entity not a list or comment"); + return fail(s, "top-level entity not a list or comment"_s); if (!parse_top(s._span, s._list)) return false; } // handle low-level errors if (in.peek() != sexpr::TOK_EOF) { - in.span().error("parser gave up before end of file"); + in.span().error("parser gave up before end of file"_s); return false; } return true; @@ -1239,7 +1268,9 @@ bool load_magic_file_v2(ZString filename) bool rv = magic_v2::loop(in); if (!rv) { - in.span().error(STRPRINTF("next token: %s '%s'", sexpr::token_name(in.peek()), in.val_string())); + in.span().error(STRPRINTF("next token: %s '%s'"_fmt, sexpr::token_name(in.peek()), in.val_string())); } return rv; } +} // namespace magic +} // namespace tmwa |