summaryrefslogtreecommitdiff
path: root/src/spell-convert
diff options
context:
space:
mode:
authorBen Longbons <b.r.longbons@gmail.com>2014-03-16 14:55:57 -0700
committerBen Longbons <b.r.longbons@gmail.com>2014-03-24 19:27:09 -0700
commitfe3d4ce758822d65a0a5d617b7b77df2dbc972d8 (patch)
treed048a7e356ba542ebc5647b1aedfce4b8257daa2 /src/spell-convert
parentc812c92d1a1835f0bda783e709481188c8d92225 (diff)
downloadtmwa-fe3d4ce758822d65a0a5d617b7b77df2dbc972d8.tar.gz
tmwa-fe3d4ce758822d65a0a5d617b7b77df2dbc972d8.tar.bz2
tmwa-fe3d4ce758822d65a0a5d617b7b77df2dbc972d8.tar.xz
tmwa-fe3d4ce758822d65a0a5d617b7b77df2dbc972d8.zip
Implement new magic frontend using sexpr
Diffstat (limited to 'src/spell-convert')
-rw-r--r--src/spell-convert/ast.cpp260
-rw-r--r--src/spell-convert/ast.hpp432
-rw-r--r--src/spell-convert/lexer.lpp117
-rw-r--r--src/spell-convert/main.cpp7
-rw-r--r--src/spell-convert/parser.ypp882
5 files changed, 1698 insertions, 0 deletions
diff --git a/src/spell-convert/ast.cpp b/src/spell-convert/ast.cpp
new file mode 100644
index 0000000..eb66752
--- /dev/null
+++ b/src/spell-convert/ast.cpp
@@ -0,0 +1,260 @@
+#include "ast.hpp"
+
+#include "../io/cxxstdio.hpp"
+
+void Constant::dump()
+{
+ PRINTF("(CONST %s\n", name);
+ body->show();
+ PRINTF(")\n");
+}
+void Teleport::dump()
+{
+ PRINTF("(TELEPORT-ANCHOR %s\n", name);
+ ident->show();
+ body->show();
+ PRINTF(")\n");
+}
+void Procedure::dump()
+{
+ PRINTF("(PROCEDURE %s\n", name);
+ PRINTF("(");
+ for (RString& a : *args)
+ PRINTF(" %s ", a);
+ PRINTF(")\n");
+ for (Effect *f : *body)
+ {
+ f->print();
+ }
+ PRINTF(")\n");
+}
+void Spell::dump()
+{
+ PRINTF("(SPELL \n");
+ PRINTF("(");
+ for (RString fl : *flags)
+ PRINTF(" %s ", fl);
+ PRINTF(")");
+ PRINTF("%s", name);
+ ident->show();
+ PRINTF("( %s %s )", arg->vartype, arg->varname);
+ for (Assignment *a : *body->lets)
+ {
+ PRINTF("(LET %s ", a->name);
+ a->body->show();
+ PRINTF(")\n");
+ }
+ for (SpellBod *b : *body->body)
+ {
+ b->say();
+ }
+ PRINTF(")\n");
+}
+void Assignment::dump()
+{
+ PRINTF("(SET %s\n", name);
+ body->show();
+ PRINTF(")\n");
+}
+
+void EffectList::print()
+{
+ PRINTF("(BLOCK\n");
+ for (Effect *ef : *body)
+ ef->print();
+ PRINTF(")\n");
+}
+void SimpleEffect::print()
+{
+ PRINTF("( %s )", text);
+}
+void ScriptEffect::print()
+{
+ PRINTF("(SCRIPT %s )", text);
+}
+void Assignment::print()
+{
+ PRINTF("(SET %s\n", name);
+ body->show();
+ PRINTF(")\n");
+}
+void ForeachEffect::print()
+{
+ PRINTF("(FOREACH %s %s ", selection, var);
+ expr->show();
+ PRINTF("\n");
+ effect->print();
+ PRINTF(")");
+}
+void ForEffect::print()
+{
+ PRINTF("(FOR %s", var);
+ low->show();
+ high->show();
+ effect->print();
+ PRINTF(")");
+}
+void IfEffect::print()
+{
+ PRINTF("(IF ");
+ cond->show();
+ if_true->print();
+ if (if_false_maybe)
+ if_false_maybe->print();
+ PRINTF(")");
+}
+void ExplicitCallEffect::print()
+{
+ PRINTF("(CALL %s ", userfunc);
+ for (Expression *x : *args)
+ x->show();
+ PRINTF(")");
+}
+void SleepEffect::print()
+{
+ PRINTF("(WAIT ");
+ time->show();
+ PRINTF(")");
+}
+void CallExpr::print()
+{
+ PRINTF("(%s ", func);
+ for (Expression *x : *args)
+ x->show();
+ PRINTF(")");
+}
+
+void SimpleExpr::show()
+{
+ PRINTF(" %s ", content);
+}
+void BinExpr::show()
+{
+ PRINTF("(%s ", op);
+ left->show();
+ right->show();
+ PRINTF(")");
+}
+void CallExpr::show()
+{
+ PRINTF("(%s ", func);
+ for (Expression *x : *args)
+ x->show();
+ PRINTF(")");
+}
+void AreaLoc::show()
+{
+ PRINTF("(@ ");
+ loc->map->show();
+ loc->x->show();
+ loc->y->show();
+ PRINTF(")");
+}
+void AreaRect::show()
+{
+ PRINTF("(@+ ");
+ AreaLoc{loc}.show();
+ width->show();
+ height->show();
+ PRINTF(")");
+}
+void AreaBar::show()
+{
+ PRINTF("(TOWARD ");
+ AreaLoc{loc}.show();
+ dir->show();
+ width->show();
+ depth->show();
+ PRINTF(")");
+}
+
+void SpellBodGuarded::say()
+{
+ PRINTF("(=> ");
+ guard->declare();
+ body->say();
+ PRINTF(")");
+}
+void SpellBodList::say()
+{
+ PRINTF("(|\n");
+ for (SpellBod *b : *body)
+ b->say();
+ PRINTF(")");
+}
+void SpellBodEffect::say()
+{
+ PRINTF("(EFFECT\n");
+ for (Effect *f : *body)
+ f->print();
+ if (maybe_trigger)
+ {
+ PRINTF("(ATTRIGGER\n");
+ for (Effect *f : *maybe_trigger)
+ f->print();
+ PRINTF(")");
+ }
+ if (maybe_end)
+ {
+ PRINTF("(ATEND\n");
+ for (Effect *f : *maybe_end)
+ f->print();
+ PRINTF(")");
+ }
+ PRINTF(")");
+}
+
+void SpellGuardOr::declare()
+{
+ PRINTF("(OR\n");
+ for (SpellGuard *sg : *any)
+ sg->declare();
+ PRINTF(")");
+}
+void SpellGuardList::declare()
+{
+ PRINTF("(GUARD\n");
+ for (SpellGuard *sg : *all)
+ sg->declare();
+ PRINTF(")");
+}
+void SpellGuardRequire::declare()
+{
+ PRINTF("(REQUIRE ");
+ expr->show();
+ PRINTF(")");
+}
+static
+void do_item(Item *itm)
+{
+ if (itm->count)
+ PRINTF("( %s %s )", itm->count, itm->item);
+ else
+ PRINTF(" %s ", itm->item);
+}
+void SpellGuardCatalysts::declare()
+{
+ PRINTF("(CATALYSTS ");
+ for (Item *itm : *items)
+ do_item(itm);
+ PRINTF(")");
+}
+void SpellGuardComponents::declare()
+{
+ PRINTF("(COMPONENTS ");
+ for (Item *itm : *items)
+ do_item(itm);
+ PRINTF(")");
+}
+void SpellGuardMana::declare()
+{
+ PRINTF("(MANA ");
+ sp->show();
+ PRINTF(")");
+}
+void SpellGuardCasttime::declare()
+{
+ PRINTF("(CASTTIME ");
+ time->show();
+ PRINTF(")");
+}
diff --git a/src/spell-convert/ast.hpp b/src/spell-convert/ast.hpp
new file mode 100644
index 0000000..e5319fc
--- /dev/null
+++ b/src/spell-convert/ast.hpp
@@ -0,0 +1,432 @@
+#ifndef AST_HPP
+#define AST_HPP
+
+# include <deque>
+# include <vector>
+
+# include "../strings/rstring.hpp"
+
+// We just leak
+# pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+
+struct TopLevel;
+struct Constant;
+struct Teleport;
+struct Procedure;
+struct Spell;
+struct SpellArg;
+struct Effect;
+struct EffectList;
+struct SimpleEffect;
+struct ScriptEffect;
+struct Assignment;
+struct ForeachEffect;
+struct ForEffect;
+struct IfEffect;
+struct ExplicitCallEffect;
+struct SleepEffect;
+struct SpellDef;
+struct SpellBod;
+struct SpellBodGuarded;
+struct SpellBodList;
+struct SpellBodEffect;
+struct SpellGuard;
+struct SpellGuardOr;
+struct SpellGuardList;
+struct SpellGuardRequire;
+struct SpellGuardCatalysts;
+struct SpellGuardComponents;
+struct SpellGuardMana;
+struct SpellGuardCasttime;
+struct Item;
+struct Expression;
+struct SimpleExpr;
+struct BinExpr;
+struct CallExpr;
+struct Location;
+struct AreaLoc;
+struct AreaRect;
+struct AreaBar;
+
+
+struct TopLevel
+{
+ virtual void dump() = 0;
+};
+
+struct Constant : TopLevel
+{
+ RString name;
+ Expression *body;
+
+ Constant(RString n, Expression *b)
+ : name(n), body(b)
+ {}
+
+ virtual void dump() override;
+};
+
+struct Teleport : TopLevel
+{
+ RString name;
+ Expression *ident;
+ Expression *body;
+
+ Teleport(RString n, Expression *i, Expression *b)
+ : name(n), ident(i), body(b)
+ {}
+
+ virtual void dump() override;
+};
+
+struct Procedure : TopLevel
+{
+ RString name;
+ std::vector<RString> *args;
+ std::deque<Effect *> *body;
+
+ Procedure(RString n, std::vector<RString> *a, std::deque<Effect *> *b)
+ : name(n), args(a), body(b)
+ {}
+
+ virtual void dump() override;
+};
+
+struct Spell : TopLevel
+{
+ std::vector<RString> *flags;
+ RString name;
+ SpellArg *arg;
+ Expression *ident;
+ SpellDef *body;
+
+ Spell(std::vector<RString> *f, RString n, SpellArg *a, Expression *i, SpellDef *b)
+ : flags(f), name(n), arg(a), ident(i), body(b)
+ {}
+
+ virtual void dump() override;
+};
+
+struct SpellArg
+{
+ RString varname;
+ RString vartype;
+};
+
+struct Effect
+{
+ virtual void print() = 0;
+};
+
+struct EffectList : Effect
+{
+ std::deque<Effect *> *body;
+
+ EffectList(std::deque<Effect *> *b)
+ : body(b)
+ {}
+
+ virtual void print() override;
+};
+struct SimpleEffect : Effect
+{
+ RString text;
+
+ SimpleEffect(RString t) : text(t) {}
+
+ virtual void print() override;
+};
+struct ScriptEffect : Effect
+{
+ RString text;
+
+ ScriptEffect(RString t) : text(t) {}
+
+ virtual void print() override;
+};
+
+struct Assignment : TopLevel, Effect
+{
+ RString name;
+ Expression *body;
+
+ Assignment(RString n, Expression *b)
+ : name(n), body(b)
+ {}
+
+ // toplevel
+ virtual void dump() override;
+ // effect
+ virtual void print() override;
+};
+
+struct ForeachEffect : Effect
+{
+ RString selection;
+ RString var;
+ Expression *expr;
+ Effect *effect;
+
+ ForeachEffect(RString s, RString v, Expression *x, Effect *f)
+ : selection(s), var(v), expr(x), effect(f)
+ {}
+
+ virtual void print() override;
+};
+
+struct ForEffect : Effect
+{
+ RString var;
+ Expression *low;
+ Expression *high;
+ Effect *effect;
+
+ ForEffect(RString v, Expression *l, Expression *h, Effect *f)
+ : var(v), low(l), high(h), effect(f)
+ {}
+
+ virtual void print() override;
+};
+
+struct IfEffect : Effect
+{
+ Expression *cond;
+ Effect *if_true;
+ Effect *if_false_maybe;
+
+ IfEffect(Expression *c, Effect *t, Effect *f=nullptr)
+ : cond(c), if_true(t), if_false_maybe(f)
+ {}
+
+ virtual void print() override;
+};
+
+struct ExplicitCallEffect : Effect
+{
+ RString userfunc;
+ std::vector<Expression *> *args;
+
+ ExplicitCallEffect(RString f, std::vector<Expression *> *a)
+ : userfunc(f), args(a)
+ {}
+
+ virtual void print() override;
+};
+
+struct SleepEffect : Effect
+{
+ Expression *time;
+
+ SleepEffect(Expression *t)
+ : time(t)
+ {}
+
+ virtual void print() override;
+};
+
+struct SpellDef
+{
+ std::vector<Assignment *> *lets;
+ std::vector<SpellBod *> *body;
+};
+
+struct SpellBod
+{
+ virtual void say() = 0;
+};
+
+struct SpellBodGuarded : SpellBod
+{
+ SpellGuard *guard;
+ SpellBod *body;
+
+ SpellBodGuarded(SpellGuard *g, SpellBod *b)
+ : guard(g), body(b)
+ {}
+
+ virtual void say() override;
+};
+
+struct SpellBodList : SpellBod
+{
+ std::vector<SpellBod *> *body;
+
+ SpellBodList(std::vector<SpellBod *> *b)
+ : body(b)
+ {}
+
+ virtual void say() override;
+};
+
+struct SpellBodEffect : SpellBod
+{
+ std::deque<Effect *> *body;
+ std::deque<Effect *> *maybe_trigger;
+ std::deque<Effect *> *maybe_end;
+
+ SpellBodEffect(std::deque<Effect *> *b, std::deque<Effect *> *t, std::deque<Effect *> *e)
+ : body(b), maybe_trigger(t), maybe_end(e)
+ {}
+
+ virtual void say() override;
+};
+
+struct SpellGuard
+{
+ virtual void declare() = 0;
+};
+
+struct SpellGuardOr : SpellGuard
+{
+ std::vector<SpellGuard *> *any;
+
+ SpellGuardOr(std::vector<SpellGuard *> *a) : any(a) {}
+ SpellGuardOr(SpellGuard *left, SpellGuard *right)
+ : any(new std::vector<SpellGuard *>({left, right}))
+ {}
+
+ virtual void declare() override;
+};
+struct SpellGuardList : SpellGuard
+{
+ std::vector<SpellGuard *> *all;
+
+ SpellGuardList(std::vector<SpellGuard *> *a) : all(a) {}
+
+ virtual void declare() override;
+};
+struct SpellGuardRequire : SpellGuard
+{
+ Expression *expr;
+
+ SpellGuardRequire(Expression *x) : expr(x) {}
+
+ virtual void declare() override;
+};
+struct SpellGuardCatalysts : SpellGuard
+{
+ std::vector<Item *> *items;
+
+ SpellGuardCatalysts(std::vector<Item *> *i) : items(i) {}
+
+ virtual void declare() override;
+};
+struct SpellGuardComponents : SpellGuard
+{
+ std::vector<Item *> *items;
+
+ SpellGuardComponents(std::vector<Item *> *i) : items(i) {}
+
+ virtual void declare() override;
+};
+struct SpellGuardMana : SpellGuard
+{
+ Expression *sp;
+
+ SpellGuardMana(Expression *x) : sp(x) {}
+
+ virtual void declare() override;
+};
+struct SpellGuardCasttime : SpellGuard
+{
+ Expression *time;
+
+ SpellGuardCasttime(Expression *x) : time(x) {}
+
+ virtual void declare() override;
+};
+
+struct Item
+{
+ RString count;
+ RString item;
+};
+
+struct Expression
+{
+ virtual void show() = 0;
+};
+
+struct SimpleExpr : Expression
+{
+ RString content;
+
+ SimpleExpr(RString c) : content(c) {}
+
+ virtual void show() override;
+};
+
+struct BinExpr : Expression
+{
+ Expression *left;
+ RString op;
+ Expression *right;
+
+ BinExpr(Expression *l, RString o, Expression *r)
+ : left(l), op(o), right(r)
+ {}
+
+ virtual void show() override;
+};
+
+struct CallExpr : Expression, Effect
+{
+ RString func;
+ std::vector<Expression *> *args;
+
+ CallExpr(RString f, std::vector<Expression *> *a)
+ : func(f), args(a)
+ {}
+
+ // expression
+ virtual void show() override;
+ // effect
+ virtual void print() override;
+};
+
+struct Location
+{
+ Expression *map;
+ Expression *x;
+ Expression *y;
+};
+
+struct AreaLoc : Expression
+{
+ Location *loc;
+
+ AreaLoc(Location *l)
+ : loc(l)
+ {}
+
+ virtual void show() override;
+};
+
+struct AreaRect : Expression
+{
+ Location *loc;
+ Expression *width;
+ Expression *height;
+
+ AreaRect(Location *l, Expression *w, Expression *h)
+ : loc(l), width(w), height(h)
+ {}
+
+ virtual void show() override;
+};
+
+struct AreaBar : Expression
+{
+ Location *loc;
+ Expression *dir;
+ Expression *width;
+ Expression *depth;
+
+ AreaBar(Location *l, Expression *a, Expression *w, Expression *d)
+ : loc(l), dir(a), width(w), depth(d)
+ {}
+
+ virtual void show() override;
+};
+
+#endif // AST_HPP
diff --git a/src/spell-convert/lexer.lpp b/src/spell-convert/lexer.lpp
new file mode 100644
index 0000000..92acf48
--- /dev/null
+++ b/src/spell-convert/lexer.lpp
@@ -0,0 +1,117 @@
+%{
+/* vim: set ft=lex: */
+//#include "lexer.hpp"
+
+#include "../strings/rstring.hpp"
+#include "../strings/zstring.hpp"
+
+#include "../io/cxxstdio.hpp"
+
+#include "../sexpr/lexer.hpp"
+
+#include "parser.hpp"
+
+#define yylval spell_converterlval
+
+RString *str(const char *s)
+{
+ return new RString(ZString(strings::really_construct_from_a_pointer, s, nullptr));
+}
+%}
+
+%option noyywrap
+%option prefix="spell_converter"
+%option nounput
+%option noinput
+
+%%
+
+"S" { yylval.s = str(yytext); return DIR; }
+"SW" { yylval.s = str(yytext); return DIR; }
+"W" { yylval.s = str(yytext); return DIR; }
+"NW" { yylval.s = str(yytext); return DIR; }
+"N" { yylval.s = str(yytext); return DIR; }
+"NE" { yylval.s = str(yytext); return DIR; }
+"E" { yylval.s = str(yytext); return DIR; }
+"SE" { yylval.s = str(yytext); return DIR; }
+"=" { return '='; }
+"==" { return EQ; }
+"<>" { return NEQ; }
+"!=" { return NEQ; }
+">" { return '>'; }
+"<" { return '<'; }
+">=" { return GTE; }
+"<=" { return LTE; }
+"(" { return '('; }
+")" { return ')'; }
+"+" { return '+'; }
+"-" { return '-'; }
+"*" { return '*'; }
+"/" { return '/'; }
+"%" { return '%'; }
+"&&" { return ANDAND; }
+"||" { return OROR; }
+";" { return ';'; }
+":" { return ':'; }
+"," { return ','; }
+"@" { return '@'; }
+"|" { return '|'; }
+"[" { return '['; }
+"]" { return ']'; }
+"&" { return '&'; }
+"^" { return '^'; }
+"." { return '.'; }
+"<<" { return SHL; }
+">>" { return SHR; }
+"PROCEDURE" { return PROCEDURE; }
+"CALL" { return CALL; }
+"OR" { return OR; }
+"TO" { return TO; }
+"TOWARDS" { return TOWARDS; }
+"TELEPORT-ANCHOR" { return TELEPORT_ANCHOR; }
+"SILENT" { return SILENT; }
+"LOCAL" { return LOCAL; }
+"NONMAGIC" { return NONMAGIC; }
+"SPELL" { return SPELL; }
+"LET" { return LET; }
+"IN" { return IN; }
+"END" { return END; }
+"=>" { return DARROW; }
+"STRING" { return STRING_TY; }
+"REQUIRE" { return REQUIRE; }
+"CATALYSTS" { return CATALYSTS; }
+"COMPONENTS" { return COMPONENTS; }
+"MANA" { return MANA; }
+"CASTTIME" { return CASTTIME; }
+"SKIP" { return SKIP; }
+"ABORT" { return ABORT; }
+"BREAK" { return BREAK; }
+"EFFECT" { return EFFECT_; }
+"ATEND" { return ATEND; }
+"ATTRIGGER" { return ATTRIGGER; }
+"CONST" { return CONST; }
+"PC" { return PC_F; }
+"NPC" { return NPC_F; }
+"MOB" { return MOB_F; }
+"ENTITY" { return ENTITY_F; }
+"TARGET" { return TARGET_F; }
+"IF" { return IF; }
+"THEN" { return THEN; }
+"ELSE" { return ELSE; }
+"FOREACH" { return FOREACH; }
+"FOR" { return FOR; }
+"DO" { return DO; }
+"WAIT" { return SLEEP; }
+
+\{([^\}]|\\.)*\} { yylval.s = str(yytext); return SCRIPT_DATA; }
+\"([^\"]|\\.)*\" { yylval.s = str(yytext); return STRING; }
+"-"?[0-9]+ { yylval.s = str(yytext); return INT; }
+"0x"[0-9a-fA-F]+ { yylval.s = str(yytext); return INT; }
+[a-zA-Z][-_a-zA-Z0-9]* { yylval.s = str(yytext); return ID; }
+"#".*$ { PRINTF("%s\n", sexpr::escape(*str(yytext + 1))); }
+"//".*$ { PRINTF("%s\n", sexpr::escape(*str(yytext + 2))); }
+[ \n\t\r] /* ignore whitespace */
+. { abort(); }
+
+%%
+// nothing to see here, move along
diff --git a/src/spell-convert/main.cpp b/src/spell-convert/main.cpp
new file mode 100644
index 0000000..a6f0d76
--- /dev/null
+++ b/src/spell-convert/main.cpp
@@ -0,0 +1,7 @@
+#include "src/spell-convert/lexer.hpp"
+#include "src/spell-convert/parser.hpp"
+
+int main()
+{
+ spell_converterparse();
+}
diff --git a/src/spell-convert/parser.ypp b/src/spell-convert/parser.ypp
new file mode 100644
index 0000000..822727d
--- /dev/null
+++ b/src/spell-convert/parser.ypp
@@ -0,0 +1,882 @@
+%code requires
+{
+/* vim: set ft=yacc: */
+#include "../strings/rstring.hpp"
+
+#include "ast.hpp"
+
+#undef YYERROR_VERBOSE
+#define YYERROR_VERBOSE 1
+} // %code requires
+
+%code
+{
+//#include "parser.hpp"
+#include "lexer.hpp"
+
+#include "../io/cxxstdio.hpp"
+
+#include "../sexpr/lexer.hpp"
+
+void yyerror(const char *msg) { FPRINTF(stderr, "Fatal: %s\n", msg); abort(); }
+} // %code
+
+%name-prefix "spell_converter"
+
+%union
+{
+ RString *s;
+ std::vector<RString> *vs;
+ Effect *e;
+ std::deque<Effect *> *ve;
+ SpellDef *spelldef;
+ SpellArg *spellarg;
+ TopLevel *top;
+ Expression *expr;
+ std::vector<Expression *> *vx;
+ Location *loc;
+ Item *it;
+ std::vector<Item *> *vit;
+ Assignment *a;
+ std::vector<Assignment *> *va;
+ SpellBod *b;
+ std::vector<SpellBod *> *vb;
+ SpellGuard *g;
+ std::vector<SpellGuard *> *vg;
+} // %union
+
+%expect 7
+
+%token <s> INT
+%token <s> STRING
+%token <s> ID
+%token <s> DIR
+
+%token '='
+%token '<'
+%token '>'
+%token '+'
+%token '-'
+%token '*'
+%token '/'
+%token '%'
+%token '@'
+%token ','
+%token '.'
+%token ':'
+%token ';'
+%token '|'
+%token '['
+%token ']'
+%token '&'
+%token '^'
+
+%token CONST
+%token PROCEDURE
+%token CALL
+%token SILENT
+%token LOCAL
+%token NONMAGIC
+%token SHL
+%token SHR
+%token EQ
+%token NEQ
+%token GTE
+%token LTE
+%token ANDAND
+%token OROR
+%token <s> SCRIPT_DATA
+%token TO
+%token TOWARDS
+%token TELEPORT_ANCHOR
+%token SPELL
+%token LET
+%token IN
+%token END
+%token DARROW
+%token STRING_TY
+%token REQUIRE
+%token CATALYSTS
+%token COMPONENTS
+%token MANA
+%token CASTTIME
+%token SKIP
+%token ABORT
+%token BREAK
+%token EFFECT_
+%token ATEND
+%token ATTRIGGER
+%token PC_F
+%token NPC_F
+%token MOB_F
+%token ENTITY_F
+%token TARGET_F
+%token IF
+%token THEN
+%token ELSE
+%token FOREACH
+%token FOR
+%token DO
+%token SLEEP
+
+%type <s> value
+%type <loc> location
+%type <expr> area
+%type <vx> arg_list
+%type <vx> arg_list_ne
+%type <va> defs
+%type <spelldef> spelldef
+%type <spellarg> argopt
+%type <a> def
+%type <vb> spellbody_list
+%type <b> spellbody
+%type <g> spellguard
+%type <vg> spellguard_list
+%type <g> prereq
+%type <it> item
+%type <vit> items
+%type <vit> item_list
+%type <s> item_name
+%type <s> selection;
+%type <e> effect
+%type <ve> effect_list
+%type <ve> maybe_trigger
+%type <ve> maybe_end
+%type <vs> spell_flags;
+
+%type <expr> expr
+%type <s> arg_ty
+%type <vs> proc_formals_list
+%type <vs> proc_formals_list_ne
+
+%type <top> spellconf_option
+
+%left OROR
+%left ANDAND
+%left '<' '>' GTE LTE NEQ EQ
+%left '+' '-'
+%left '*' '/' '%'
+%left SHL SHR '&' '^' '|'
+%right '='
+%left OR
+%left DARROW
+%left '.'
+
+%%
+
+spellconf
+
+: /* empty */
+
+| spellconf semicolons spellconf_option
+{
+ $3->dump();
+}
+
+;
+
+
+semicolons
+
+: /* empty */
+
+| semicolons ';'
+
+;
+
+
+proc_formals_list
+
+: /* empty */
+{
+ $$ = new std::vector<RString>();
+}
+
+| proc_formals_list_ne
+{
+ $$ = $1;
+}
+
+;
+
+
+proc_formals_list_ne
+
+: ID
+{
+ $$ = new std::vector<RString>();
+ $$->push_back(*$1);
+}
+
+| proc_formals_list_ne ',' ID
+{
+ $$ = $1;
+ $$->push_back(*$3);
+}
+
+;
+
+
+spellconf_option
+
+: ID '=' expr
+{
+ $$ = new Assignment{*$1, $3};
+}
+
+| CONST ID '=' expr
+{
+ $$ = new Constant{*$2, $4};
+}
+
+| TELEPORT_ANCHOR ID ':' expr '=' expr
+{
+ $$ = new Teleport{*$2, $4, $6};
+}
+
+| PROCEDURE ID '(' proc_formals_list ')' '=' effect_list
+{
+ $$ = new Procedure{*$2, $4, $7};
+}
+
+| spell_flags SPELL ID argopt ':' expr '=' spelldef
+{
+ $$ = new Spell{$1, *$3, $4, $6, $8};
+}
+
+;
+
+
+spell_flags
+
+: /* empty */
+{
+ $$ = new std::vector<RString>();
+}
+
+| spell_flags LOCAL
+{
+ $$ = $1;
+ $$->push_back("LOCAL");
+}
+
+| spell_flags NONMAGIC
+{
+ $$ = $1;
+ $$->push_back("NONMAGIC");
+}
+
+| spell_flags SILENT
+{
+ $$ = $1;
+ $$->push_back("SILENT");
+}
+
+;
+
+
+argopt
+
+: /* empty */
+{
+ $$ = new SpellArg{};
+}
+
+| '(' ID ':' arg_ty ')'
+{
+ $$ = new SpellArg{*$2, *$4};
+}
+
+;
+
+
+arg_ty
+
+: PC_F
+{
+ $$ = new RString("PC");
+}
+
+| STRING_TY
+{
+ $$ = new RString("STRING");
+}
+
+;
+
+
+value
+
+: DIR
+{
+ $$ = $1;
+}
+
+| INT
+{
+ $$ = $1;
+}
+
+| STRING
+{
+ $$ = $1;
+}
+
+;
+
+
+expr
+
+: value
+{
+ $$ = new SimpleExpr{*$1};
+}
+
+| ID
+{
+ $$ = new SimpleExpr{*$1};
+}
+
+| area
+{
+ $$ = $1;
+}
+
+| expr '+' expr
+{
+ $$ = new BinExpr{$1, "+", $3};
+}
+
+| expr '-' expr
+{
+ $$ = new BinExpr{$1, "-", $3};
+}
+
+| expr '*' expr
+{
+ $$ = new BinExpr{$1, "*", $3};
+}
+
+| expr '%' expr
+{
+ $$ = new BinExpr{$1, "%", $3};
+}
+
+| expr '/' expr
+{
+ $$ = new BinExpr{$1, "/", $3};
+}
+
+| expr '<' expr
+{
+ $$ = new BinExpr{$1, "<", $3};
+}
+
+| expr '>' expr
+{
+ $$ = new BinExpr{$1, ">", $3};
+}
+
+| expr '&' expr
+{
+ $$ = new BinExpr{$1, "&", $3};
+}
+
+| expr '^' expr
+{
+ $$ = new BinExpr{$1, "^", $3};
+}
+
+| expr '|' expr
+{
+ $$ = new BinExpr{$1, "|", $3};
+}
+
+| expr SHL expr
+{
+ $$ = new BinExpr{$1, "<<", $3};
+}
+
+| expr SHR expr
+{
+ $$ = new BinExpr{$1, ">>", $3};
+}
+
+| expr LTE expr
+{
+ $$ = new BinExpr{$1, "<=", $3};
+}
+
+| expr GTE expr
+{
+ $$ = new BinExpr{$1, ">=", $3};
+}
+
+| expr ANDAND expr
+{
+ $$ = new BinExpr{$1, "&&", $3};
+}
+
+| expr OROR expr
+{
+ $$ = new BinExpr{$1, "||", $3};
+}
+
+| expr EQ expr
+{
+ $$ = new BinExpr{$1, "==", $3};
+}
+
+| expr '=' expr
+{
+ // convert to ==
+ $$ = new BinExpr{$1, "==", $3};
+}
+
+| expr NEQ expr
+{
+ $$ = new BinExpr{$1, "!=", $3};
+}
+
+| ID '(' arg_list ')'
+{
+ $$ = new CallExpr{*$1, $3};
+}
+
+| '(' expr ')'
+{
+ $$ = $2;
+}
+
+| expr '.' ID
+{
+ $$ = new BinExpr{$1, ".", new SimpleExpr(*$3)};
+}
+
+;
+
+
+arg_list
+
+: /* empty */
+{
+ $$ = new std::vector<Expression *>();
+}
+
+| arg_list_ne
+{
+ $$ = $1;
+}
+
+;
+
+
+arg_list_ne
+
+: expr
+{
+ $$ = new std::vector<Expression *>();
+ $$->push_back($1);
+}
+
+| arg_list_ne ',' expr
+{
+ $$ = $1;
+ $$->push_back($3);
+}
+
+;
+
+
+location
+
+: '@' '(' expr ',' expr ',' expr ')'
+{
+ $$ = new Location{$3, $5, $7};
+}
+
+;
+
+
+area
+
+: location
+{
+ $$ = new AreaLoc{$1};
+}
+
+| location '@' '+' '(' expr ',' expr ')'
+{
+ $$ = new AreaRect{$1, $5, $7};
+}
+
+| location TOWARDS expr ':' '(' expr ',' expr ')'
+{
+ $$ = new AreaBar{$1, $3, $6, $8};
+}
+
+;
+
+
+spelldef
+
+: spellbody_list
+{
+ $$ = new SpellDef{new std::vector<Assignment *>{}, $1};
+}
+
+| LET defs IN spellbody_list
+{
+ $$ = new SpellDef{$2, $4};
+}
+
+;
+
+
+defs
+
+: semicolons
+{
+ $$ = new std::vector<Assignment *>();
+}
+
+| defs def semicolons
+{
+ $$ = $1;
+ $$->push_back($2);
+}
+
+;
+
+
+def
+
+: ID '=' expr
+{
+ $$ = new Assignment{*$1, $3};
+}
+
+;
+
+
+spellbody_list
+
+: spellbody
+{
+ $$ = new std::vector<SpellBod *>();
+ $$->push_back($1);
+}
+
+| spellbody_list '|' spellbody
+{
+ $$ = $1;
+ $$->push_back($3);
+}
+
+;
+
+
+spellbody
+
+: spellguard DARROW spellbody
+{
+ $$ = new SpellBodGuarded{$1, $3};
+}
+
+| '(' spellbody_list ')'
+{
+ $$ = new SpellBodList{$2};
+}
+
+| EFFECT_ effect_list maybe_trigger maybe_end
+{
+ $$ = new SpellBodEffect{$2, $3, $4};
+}
+
+;
+
+
+maybe_trigger
+
+: /* empty */
+{
+ $$ = nullptr;
+}
+
+| ATTRIGGER effect_list
+{
+ $$ = $2;
+}
+
+;
+
+
+maybe_end
+
+: /* empty */
+{
+ $$ = nullptr;
+}
+
+| ATEND effect_list
+{
+ $$ = $2;
+}
+
+;
+
+
+spellguard
+
+: prereq
+{
+ $$ = $1;
+}
+
+| spellguard OR spellguard
+{
+ $$ = new SpellGuardOr($1, $3);
+}
+
+| '(' spellguard_list ')'
+{
+ $$ = new SpellGuardList{$2};
+}
+
+;
+
+
+spellguard_list
+
+: spellguard
+{
+ $$ = new std::vector<SpellGuard *>();
+ $$->push_back($1);
+}
+
+| spellguard_list ',' spellguard
+{
+ $$ = $1;
+ $$->push_back($3);
+}
+
+;
+
+
+prereq
+
+: REQUIRE expr
+{
+ $$ = new SpellGuardRequire{$2};
+}
+
+| CATALYSTS items
+{
+ $$ = new SpellGuardCatalysts{$2};
+}
+
+| COMPONENTS items
+{
+ $$ = new SpellGuardComponents{$2};
+}
+
+| MANA expr
+{
+ $$ = new SpellGuardMana{$2};
+}
+
+| CASTTIME expr
+{
+ $$ = new SpellGuardCasttime{$2};
+}
+
+;
+
+
+items
+
+: '[' item_list ']'
+{
+ $$ = $2;
+}
+
+;
+
+
+item_list
+
+: item
+{
+ $$ = new std::vector<Item *>();
+ $$->push_back($1);
+}
+
+| item_list ',' item
+{
+ $$ = $1;
+ $$->push_back($3);
+}
+
+;
+
+
+item
+
+: INT '*' item_name
+{
+ $$ = new Item{*$1, *$3};
+}
+
+| item_name
+{
+ $$ = new Item{RString(), *$1};
+}
+
+;
+
+
+item_name
+
+: STRING
+{
+ $$ = $1;
+}
+
+| INT
+{
+ $$ = $1;
+}
+
+;
+
+
+selection
+
+: PC_F
+{
+ $$ = new RString{"PC"};
+}
+
+| MOB_F
+{
+ $$ = new RString{"MOB"};
+}
+
+| ENTITY_F
+{
+ $$ = new RString{"ENTITY"};
+}
+
+| SPELL
+{
+ $$ = new RString{"SPELL"};
+}
+
+| TARGET_F
+{
+ $$ = new RString{"TARGET"};
+}
+
+| NPC_F
+{
+ $$ = new RString{"NPC"};
+}
+
+;
+
+
+effect
+
+: '(' effect_list ')'
+{
+ $$ = new EffectList{$2};
+}
+
+| SKIP ';'
+{
+ $$ = new SimpleEffect{"SKIP"};
+}
+
+| ABORT ';'
+{
+ $$ = new SimpleEffect{"ABORT"};
+}
+
+| END ';'
+{
+ $$ = new SimpleEffect{"END"};
+}
+
+| BREAK ';'
+{
+ $$ = new SimpleEffect{"BREAK"};
+}
+
+| ID '=' expr ';'
+{
+ $$ = new Assignment(*$1, $3);
+}
+
+| FOREACH selection ID IN expr DO effect
+{
+ $$ = new ForeachEffect{*$2, *$3, $5, $7};
+}
+
+| FOR ID '=' expr TO expr DO effect
+{
+ $$ = new ForEffect{*$2, $4, $6, $8};
+}
+
+| IF expr THEN effect ELSE effect
+{
+ $$ = new IfEffect{$2, $4, $6};
+}
+
+| IF expr THEN effect
+{
+ $$ = new IfEffect{$2, $4};
+}
+
+| SLEEP expr ';'
+{
+ $$ = new SleepEffect{$2};
+}
+
+| ID '(' arg_list ')' ';'
+{
+ $$ = new CallExpr{*$1, $3};
+}
+
+| SCRIPT_DATA
+{
+ AString tmp = sexpr::escape(*$1);
+ $$ = new ScriptEffect{RString(tmp)};
+}
+
+| CALL ID '(' arg_list ')' ';'
+{
+ $$ = new ExplicitCallEffect{*$2, $4};
+}
+
+;
+
+
+effect_list
+
+: /* empty */
+{
+ $$ = new std::deque<Effect *>();
+}
+
+| effect semicolons effect_list
+{
+ // because of grammar problems, doing this right generates reduce/reduce conflicts
+ $$ = $3;
+ $$->push_front($1);
+}
+
+;
+
+
+%%
+// Nothing to see here, move along