summaryrefslogtreecommitdiff
path: root/src/map/magic-interpreter-parser.ypp
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/magic-interpreter-parser.ypp')
-rw-r--r--src/map/magic-interpreter-parser.ypp1089
1 files changed, 1089 insertions, 0 deletions
diff --git a/src/map/magic-interpreter-parser.ypp b/src/map/magic-interpreter-parser.ypp
new file mode 100644
index 0000000..2648be9
--- /dev/null
+++ b/src/map/magic-interpreter-parser.ypp
@@ -0,0 +1,1089 @@
+%{
+#include "magic-interpreter.hpp"
+#include "magic-expr.hpp"
+#include <stdarg.h>
+
+magic_conf_t magic_conf;
+
+static int
+intern_id(const char *id_name);
+
+
+static expr_t *
+fun_expr(const char *name, int args_nr, expr_t **args, int line, int column);
+
+static expr_t *
+dot_expr(expr_t *lhs, int id);
+
+#define BIN_EXPR(x, name, arg1, arg2, line, column) { expr_t *e[2]; e[0] = arg1; e[1] = arg2; x = fun_expr(name, 2, e, line, column); }
+
+static int failed_flag = 0;
+
+static void
+magic_frontend_error(const char *msg);
+
+static void
+fail(int line, int column, const char *fmt, ...);
+
+static spell_t *
+new_spell(spellguard_t *guard);
+
+static spellguard_t *
+spellguard_implication(spellguard_t *a, spellguard_t *b);
+
+static spellguard_t *
+new_spellguard(int ty);
+
+static effect_t *
+new_effect(int ty);
+
+static effect_t *
+set_effect_continuation(effect_t *src, effect_t *continuation);
+
+static void
+add_spell(spell_t *spell, int line_nr);
+
+static void
+add_teleport_anchor(teleport_anchor_t *anchor, int line_nr);
+
+static effect_t *
+op_effect(char *name, int args_nr, expr_t **args, int line, int column);
+
+int
+magic_frontend_lex(void);
+
+static void
+install_proc(proc_t *proc);
+
+static effect_t *
+call_proc(char *name, int args_nr, expr_t **args, int line_nr, int column);
+
+static void
+bind_constant(char *name, val_t *val, int line_nr);
+
+static val_t *
+find_constant(char *name);
+
+
+%}
+
+%name-prefix="magic_frontend_"
+
+%locations
+
+%union {
+ int i;
+ char *s;
+ int op;
+ magic_conf_t *magic_conf;
+ val_t value;
+ expr_t *expr;
+ e_location_t location;
+ e_area_t area;
+ args_rec_t arg_list;
+ struct { int letdefs_nr; letdef_t *letdefs; } letdefs;
+ spell_t *spell;
+ struct { int id, ty; } spellarg_def;
+ letdef_t vardef;
+ spellguard_t *spellguard;
+ component_t *components;
+ struct {int id, count; } component;
+ effect_t *effect;
+ proc_t *proc;
+};
+
+%expect 7
+
+%token <i> INT
+%token <s> STRING
+%token <s> ID
+%token <i> 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 <value> value
+%type <location> location
+%type <area> area
+%type <arg_list> arg_list
+%type <arg_list> arg_list_ne
+%type <letdefs> defs
+%type <spell> spelldef
+%type <spellarg_def> argopt
+%type <vardef> def
+%type <spellguard> spellbody_list
+%type <spellguard> spellbody
+%type <spellguard> spellguard
+%type <spellguard> spellguard_list
+%type <spellguard> prereq
+%type <component> item
+%type <components> items
+%type <components> item_list
+%type <i> item_name
+%type <i> selection;
+%type <effect> effect
+%type <effect> effect_list
+%type <effect> maybe_trigger
+%type <effect> maybe_end
+%type <i> spell_flags;
+
+%type <expr> expr
+%type <i> arg_ty
+%type <proc> proc_formals_list
+%type <proc> proc_formals_list_ne
+
+%left OROR
+%left ANDAND
+%left '<' '>' GTE LTE NEQ EQ
+%left '+' '-'
+%left '*' '/' '%'
+%left SHL SHR '&' '^' '|'
+%right '='
+%left OR
+%left DARROW
+%left '.'
+
+%%
+
+spellconf : /* empty */
+ {}
+ | spellconf_option semicolons spellconf
+ {}
+ ;
+
+
+semicolons : /* empty */
+ {}
+ | semicolons ';'
+ {}
+ ;
+
+
+proc_formals_list : /* empty */
+ { CREATE ($$, proc_t, 1); }
+ | proc_formals_list_ne
+ { $$ = $1; }
+ ;
+
+proc_formals_list_ne : ID
+ { CREATE ($$, proc_t, 1);
+ $$->args_nr = 1;
+ $$->args = (int*)malloc(sizeof(int));
+ $$->args[0] = intern_id($1);
+ }
+ | proc_formals_list_ne ',' ID
+ { $$ = $1;
+ $$->args = (int*)realloc($$->args, sizeof(int) * (1 + $$->args_nr));
+ $$->args[$$->args_nr++] = intern_id($3);
+ }
+ ;
+
+spellconf_option : ID '=' expr
+ {
+ int var_id;
+ if (find_constant($1)) {
+ fail(@1.first_line, 0, "Attempt to redefine constant `%s' as global\n", $1);
+ free($1);
+ } else {
+ var_id = intern_id($1);
+ magic_eval(&magic_default_env, &magic_conf.vars[var_id], $3);
+ }
+ }
+ | CONST ID '=' expr
+ {
+ val_t var;
+ magic_eval(&magic_default_env, &var, $4);
+ bind_constant($2, &var, @1.first_line);
+ }
+ | TELEPORT_ANCHOR ID ':' expr '=' expr
+ {
+ teleport_anchor_t *anchor;
+ CREATE (anchor, teleport_anchor_t, 1);
+ anchor->name = $2;
+ anchor->invocation = magic_eval_str(&magic_default_env, $4);
+ anchor->location = $6;
+
+ if (!failed_flag)
+ add_teleport_anchor(anchor, @1.first_line);
+ else
+ free(anchor);
+ failed_flag = 0;
+ }
+ | PROCEDURE ID '(' proc_formals_list ')' '=' effect_list
+ {
+ proc_t *proc = $4;
+ proc->name = $2;
+ proc->body = $7;
+ if (!failed_flag)
+ install_proc(proc);
+ failed_flag = 0;
+ }
+ | spell_flags SPELL ID argopt ':' expr '=' spelldef
+ { spell_t *spell = $8;
+ spell->name = $3;
+ spell->invocation = magic_eval_str(&magic_default_env, $6);
+ spell->arg = $4.id;
+ spell->spellarg_ty = $4.ty;
+ spell->flags = $1;
+ if (!failed_flag)
+ add_spell(spell, @1.first_line);
+ failed_flag = 0;
+ }
+
+spell_flags : /* empty */
+ { $$ = 0; }
+ | LOCAL spell_flags
+ { if ($2 & SPELL_FLAG_LOCAL)
+ fail(@1.first_line, @1.first_column, "`LOCAL' specified more than once");
+ $$ = $2 | SPELL_FLAG_LOCAL;
+ }
+ | NONMAGIC spell_flags
+ { if ($2 & SPELL_FLAG_NONMAGIC)
+ fail(@1.first_line, @1.first_column, "`NONMAGIC' specified more than once");
+ $$ = $2 | SPELL_FLAG_NONMAGIC;
+ }
+ | SILENT spell_flags
+ { if ($2 & SPELL_FLAG_SILENT)
+ fail(@1.first_line, @1.first_column, "`SILENT' specified more than once");
+ $$ = $2 | SPELL_FLAG_SILENT;
+ }
+
+
+argopt : /* empty */
+ { $$.ty = SPELLARG_NONE; }
+ | '(' ID ':' arg_ty ')'
+ { $$.id = intern_id($2);
+ $$.ty = $4; }
+ ;
+
+
+arg_ty : PC_F
+ { $$ = SPELLARG_PC; }
+ | STRING_TY
+ { $$ = SPELLARG_STRING; }
+ ;
+
+
+value : DIR
+ { $$.ty = TY_DIR;
+ $$.v.v_int = $1; }
+ | INT
+ { $$.ty = TY_INT;
+ $$.v.v_int = $1; }
+ | STRING
+ { $$.ty = TY_STRING;
+ $$.v.v_string = $1; }
+ ;
+
+
+expr : value
+ { $$ = magic_new_expr(EXPR_VAL);
+ $$->e.e_val = $1; }
+ | ID
+ {
+ val_t *val;
+ if ((val = find_constant($1))) {
+ $$ = magic_new_expr(EXPR_VAL);
+ $$->e.e_val = *val;
+ } else {
+ $$ = magic_new_expr(EXPR_ID);
+ $$->e.e_id = intern_id($1);
+ }
+ }
+ | area
+ { $$ = magic_new_expr(EXPR_AREA);
+ $$->e.e_area = $1; }
+ | expr '+' expr
+ { BIN_EXPR($$, "+", $1, $3, @1.first_line, @1.first_column); }
+ | expr '-' expr
+ { BIN_EXPR($$, "-", $1, $3, @1.first_line, @1.first_column); }
+ | expr '*' expr
+ { BIN_EXPR($$, "*", $1, $3, @1.first_line, @1.first_column); }
+ | expr '%' expr
+ { BIN_EXPR($$, "%", $1, $3, @1.first_line, @1.first_column); }
+ | expr '/' expr
+ { BIN_EXPR($$, "/", $1, $3, @1.first_line, @1.first_column); }
+ | expr '<' expr
+ { BIN_EXPR($$, ">", $3, $1, @1.first_line, @1.first_column); }
+ | expr '>' expr
+ { BIN_EXPR($$, ">", $1, $3, @1.first_line, @1.first_column); }
+ | expr '&' expr
+ { BIN_EXPR($$, "&", $1, $3, @1.first_line, @1.first_column); }
+ | expr '^' expr
+ { BIN_EXPR($$, "^", $1, $3, @1.first_line, @1.first_column); }
+ | expr '|' expr
+ { BIN_EXPR($$, "|", $1, $3, @1.first_line, @1.first_column); }
+ | expr SHL expr
+ { BIN_EXPR($$, "<<", $1, $3, @1.first_line, @1.first_column); }
+ | expr SHR expr
+ { BIN_EXPR($$, ">>", $1, $3, @1.first_line, @1.first_column); }
+ | expr LTE expr
+ { BIN_EXPR($$, ">=", $3, $1, @1.first_line, @1.first_column); }
+ | expr GTE expr
+ { BIN_EXPR($$, ">=", $1, $3, @1.first_line, @1.first_column); }
+ | expr ANDAND expr
+ { BIN_EXPR($$, "&&", $1, $3, @1.first_line, @1.first_column); }
+ | expr OROR expr
+ { BIN_EXPR($$, "||", $1, $3, @1.first_line, @1.first_column); }
+ | expr EQ expr
+ { BIN_EXPR($$, "=", $1, $3, @1.first_line, @1.first_column); }
+ | expr '=' expr
+ { BIN_EXPR($$, "=", $1, $3, @1.first_line, @1.first_column); }
+ | expr NEQ expr
+ { BIN_EXPR($$, "=", $1, $3, @1.first_line, @1.first_column);
+ $$ = fun_expr("not", 1, &$$, @1.first_line, @1.first_column); }
+ | ID '(' arg_list ')'
+ { $$ = fun_expr($1, $3.args_nr, $3.args, @1.first_line, @1.first_column);
+ if ($3.args)
+ free($3.args);
+ free($1); }
+ | '(' expr ')'
+ { $$ = $2; }
+ | expr '.' ID
+ { $$ = dot_expr($1, intern_id($3)); }
+ ;
+
+arg_list : /* empty */
+ { $$.args_nr = 0; }
+ | arg_list_ne
+ { $$ = $1; }
+ ;
+
+
+arg_list_ne : expr
+ { CREATE($$.args, expr_t *, 1);
+ $$.args_nr = 1;
+ $$.args[0] = $1;
+ }
+ | arg_list_ne ',' expr
+ { RECREATE($$.args, expr_t *, 1 + $$.args_nr);
+ $$.args[$$.args_nr++] = $3;
+ }
+ ;
+
+
+location : '@' '(' expr ',' expr ',' expr ')'
+ { $$.m = $3; $$.x = $5; $$.y = $7; }
+ ;
+
+area : location
+ { $$.ty = AREA_LOCATION;
+ $$.a.a_loc = $1;
+ }
+ | location '@' '+' '(' expr ',' expr ')'
+ { $$.ty = AREA_RECT;
+ $$.a.a_rect.loc = $1;
+ $$.a.a_rect.width = $5;
+ $$.a.a_rect.height = $7;
+ }
+ | location TOWARDS expr ':' '(' expr ',' expr ')'
+ { $$.ty = AREA_BAR;
+ $$.a.a_bar.loc = $1;
+ $$.a.a_bar.width = $6;
+ $$.a.a_bar.depth = $8;
+ $$.a.a_bar.dir = $3;
+ }
+ ;
+
+
+spelldef : spellbody_list
+ { $$ = new_spell($1); }
+ | LET defs IN spellbody_list
+ { $$ = new_spell($4);
+ $$->letdefs_nr = $2.letdefs_nr;
+ $$->letdefs = $2.letdefs;
+ $$->spellguard = $4;
+ }
+ ;
+
+
+defs : semicolons
+ { $$.letdefs_nr = 0;
+ CREATE($$.letdefs, letdef_t, 1);
+ }
+ | defs def semicolons
+ { $$ = $1;
+ $$.letdefs_nr++;
+ RECREATE ($$.letdefs, letdef_t, $$.letdefs_nr);
+ $$.letdefs[$1.letdefs_nr] = $2;
+ }
+ ;
+
+
+def : ID '=' expr
+ {
+ if (find_constant($1)) {
+ fail(@1.first_line, @1.first_column, "Attempt to re-define constant `%s' as LET-bound variable.\n", $1);
+ free($1);
+ } else {
+ $$.id = intern_id($1);
+ $$.expr = $3;
+ }
+ }
+ ;
+
+
+spellbody_list : spellbody
+ { $$ = $1; }
+ | spellbody '|' spellbody_list
+ { spellguard_t *sg = new_spellguard(SPELLGUARD_CHOICE);
+ sg->next = $1;
+ sg->s.s_alt = $3;
+ $$ = sg;
+ }
+ ;
+
+
+spellbody : spellguard DARROW spellbody
+ { $$ = spellguard_implication($1, $3); }
+ | '(' spellbody_list ')'
+ { $$ = $2; }
+ | EFFECT effect_list maybe_trigger maybe_end
+ { spellguard_t *sg = new_spellguard(SPELLGUARD_EFFECT);
+ sg->s.s_effect.effect = $2;
+ sg->s.s_effect.at_trigger = $3;
+ sg->s.s_effect.at_end = $4;
+ $$ = sg;
+ }
+ ;
+
+
+maybe_trigger : /* empty */
+ { $$ = NULL; }
+ | ATTRIGGER effect_list
+ { $$ = $2; }
+ ;
+
+
+maybe_end : /* empty */
+ { $$ = NULL; }
+ | ATEND effect_list
+ { $$ = $2; }
+ ;
+
+
+spellguard : prereq
+ { $$ = $1; }
+ | spellguard OR spellguard
+ { spellguard_t *sg = new_spellguard(SPELLGUARD_CHOICE);
+ sg->next = $1;
+ sg->s.s_alt = $3;
+ $$ = sg;
+ }
+ | '(' spellguard_list ')'
+ { $$ = $2; }
+ ;
+
+
+spellguard_list : spellguard
+ { $$ = $1; }
+ | spellguard ',' spellguard_list
+ { $$ = spellguard_implication ($1, $3); }
+ ;
+
+
+prereq : REQUIRE expr
+ { $$ = new_spellguard(SPELLGUARD_CONDITION);
+ $$->s.s_condition = $2;
+ }
+ | CATALYSTS items
+ { $$ = new_spellguard(SPELLGUARD_CATALYSTS);
+ $$->s.s_catalysts = $2;
+ }
+ | COMPONENTS items
+ { $$ = new_spellguard(SPELLGUARD_COMPONENTS);
+ $$->s.s_components = $2;
+ }
+ | MANA expr
+ { $$ = new_spellguard(SPELLGUARD_MANA);
+ $$->s.s_mana = $2;
+ }
+ | CASTTIME expr
+ { $$ = new_spellguard(SPELLGUARD_CASTTIME);
+ $$->s.s_casttime = $2;
+ }
+ ;
+
+
+items : '[' item_list ']'
+ { $$ = $2; }
+ ;
+
+
+item_list : item
+ { $$ = NULL;
+ magic_add_component(&$$, $1.id, $1.count);
+ }
+ | item_list ',' item
+ { $$ = $1;
+ magic_add_component(&$$, $3.id, $3.count);
+ }
+ ;
+
+
+item : INT '*' item_name
+ { $$.id = $3; $$.count = $1; }
+ | item_name
+ { $$.id = $1; $$.count = 1; }
+ ;
+
+
+item_name : STRING
+ { struct item_data *item = itemdb_searchname($1);
+ if (!item) {
+ fail (@1.first_line, @1.first_column, "Unknown item `%s'\n", $1);
+ $$ = 0;
+ } else
+ $$ = item->nameid;
+ free ($1);
+ }
+ | INT
+ { $$ = $1; }
+ ;
+
+
+selection : PC_F
+ { $$ = FOREACH_FILTER_PC; }
+ | MOB_F
+ { $$ = FOREACH_FILTER_MOB; }
+ | ENTITY_F
+ { $$ = FOREACH_FILTER_ENTITY; }
+ | SPELL
+ { $$ = FOREACH_FILTER_SPELL; }
+ | TARGET_F
+ { $$ = FOREACH_FILTER_TARGET; }
+ | NPC_F
+ { $$ = FOREACH_FILTER_NPC; }
+ ;
+
+
+effect : '(' effect_list ')'
+ { $$ = $2; }
+ | SKIP ';'
+ { $$ = new_effect(EFFECT_SKIP); }
+ | ABORT ';'
+ { $$ = new_effect(EFFECT_ABORT); }
+ | END ';'
+ { $$ = new_effect(EFFECT_END); }
+ | BREAK ';'
+ { $$ = new_effect(EFFECT_BREAK); }
+ | ID '=' expr ';'
+ {
+ if (find_constant($1)) {
+ fail(@1.first_line, @1.first_column, "Attempt to re-define constant `%s' in assignment.", $1);
+ free($1);
+ } else {
+ $$ = new_effect(EFFECT_ASSIGN);
+ $$->e.e_assign.id = intern_id($1);
+ $$->e.e_assign.expr = $3;
+ }
+ }
+ | FOREACH selection ID IN expr DO effect
+ { $$ = new_effect(EFFECT_FOREACH);
+ $$->e.e_foreach.id = intern_id($3);
+ $$->e.e_foreach.area = $5;
+ $$->e.e_foreach.body = $7;
+ $$->e.e_foreach.filter = $2;
+ }
+ | FOR ID '=' expr TO expr DO effect
+ { $$ = new_effect(EFFECT_FOR);
+ $$->e.e_for.id = intern_id($2);
+ $$->e.e_for.start = $4;
+ $$->e.e_for.stop = $6;
+ $$->e.e_for.body = $8;
+ }
+ | IF expr THEN effect ELSE effect
+ { $$ = new_effect(EFFECT_IF);
+ $$->e.e_if.cond = $2;
+ $$->e.e_if.true_branch = $4;
+ $$->e.e_if.false_branch = $6;
+ }
+ | IF expr THEN effect
+ { $$ = new_effect(EFFECT_IF);
+ $$->e.e_if.cond = $2;
+ $$->e.e_if.true_branch = $4;
+ $$->e.e_if.false_branch = new_effect(EFFECT_SKIP);
+ }
+ | SLEEP expr ';'
+ { $$ = new_effect(EFFECT_SLEEP);
+ $$->e.e_sleep = $2;
+ }
+ | ID '(' arg_list ')' ';'
+ { $$ = op_effect($1, $3.args_nr, $3.args, @1.first_line, @1.first_column);
+ free($1);
+ }
+ | SCRIPT_DATA
+ { $$ = new_effect(EFFECT_SCRIPT);
+ $$->e.e_script = parse_script((unsigned char *) $1, @1.first_line);
+ free($1);
+ if ($$->e.e_script == NULL)
+ fail(@1.first_line, @1.first_column, "Failed to compile script\n");
+ }
+ | CALL ID '(' arg_list ')' ';'
+ { $$ = call_proc($2, $4.args_nr, $4.args, @1.first_line, @1.first_column);
+ free($2);
+ }
+ ;
+
+effect_list : /* empty */
+ { $$ = new_effect(EFFECT_SKIP); }
+ | effect semicolons effect_list
+ { $$ = set_effect_continuation($1, $3); }
+ ;
+
+
+%%
+
+/* We do incremental realloc here to store our results. Since this happens only once
+ * during startup for a relatively manageable set of configs, it should be fine. */
+
+static int
+intern_id(const char *id_name)
+{
+ int i;
+
+ for (i = 0; i < magic_conf.vars_nr; i++)
+ if (!strcmp(id_name, magic_conf.var_name[i])) {
+ free((char*)id_name);
+ return i;
+ }
+
+ /* Must add new */
+ i = magic_conf.vars_nr++;
+ RECREATE(magic_conf.var_name, const char *, magic_conf.vars_nr);
+ magic_conf.var_name[i] = id_name;
+ RECREATE(magic_conf.vars, val_t, magic_conf.vars_nr);
+ magic_conf.vars[i].ty = TY_UNDEF;
+
+ return i;
+}
+
+static void
+add_spell(spell_t *spell, int line_nr)
+{
+ int index = magic_conf.spells_nr;
+ int i;
+
+ for (i = 0; i < index; i++) {
+ if (!strcmp(magic_conf.spells[i]->name, spell->name)) {
+ fail(line_nr, 0, "Attempt to redefine spell `%s'\n", spell->name);
+ return;
+ }
+ if (!strcmp(magic_conf.spells[i]->invocation, spell->invocation)) {
+ fail(line_nr, 0, "Attempt to redefine spell invocation `%s' between spells `%s' and `%s'\n",
+ spell->invocation, magic_conf.spells[i]->name, spell->name);
+ return;
+ }
+ }
+ magic_conf.spells_nr++;
+
+ RECREATE(magic_conf.spells, spell_t *, magic_conf.spells_nr);
+ magic_conf.spells[index] = spell;
+
+
+}
+
+static void
+add_teleport_anchor(teleport_anchor_t *anchor, int line_nr)
+{
+ int index = magic_conf.anchors_nr;
+ int i;
+
+ for (i = 0; i < index; i++) {
+ if (!strcmp(magic_conf.anchors[i]->name, anchor->name)) {
+ fail(line_nr, 0, "Attempt to redefine teleport anchor `%s'\n", anchor->name);
+ return;
+ }
+ if (!strcmp(magic_conf.anchors[i]->invocation, anchor->invocation)) {
+ fail(line_nr, 0, "Attempt to redefine anchor invocation `%s' between anchors `%s' and `%s'\n",
+ anchor->invocation, magic_conf.anchors[i]->name, anchor->name);
+ return;
+ }
+ }
+ magic_conf.anchors_nr++;
+
+ RECREATE(magic_conf.anchors, teleport_anchor_t *, magic_conf.anchors_nr);
+ magic_conf.anchors[index] = anchor;
+}
+
+
+static void
+fail(int line, int column, const char *fmt, ...)
+{
+ va_list ap;
+ fprintf(stderr, "[magic-init] L%d:%d: ", line, column);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ failed_flag = 1;
+}
+
+static expr_t *
+dot_expr(expr_t *expr, int id)
+{
+ expr_t *retval = magic_new_expr(EXPR_SPELLFIELD);
+ retval->e.e_field.id = id;
+ retval->e.e_field.expr = expr;
+
+ return retval;
+}
+
+static expr_t *
+fun_expr(const char *name, int args_nr, expr_t **args, int line, int column)
+{
+ int id;
+ expr_t *expr;
+ fun_t *fun = magic_get_fun(name, &id);
+
+ if (!fun) {
+ fail(line, column, "Unknown function `%s'\n", name);
+ } else if (strlen(fun->signature) != args_nr) {
+ fail(line, column, "Incorrect number of arguments to function `%s': Expected %d, found %d\n", name, strlen(fun->signature), args_nr);
+ fun = NULL;
+ }
+
+ if (fun) {
+ int i;
+
+ expr = magic_new_expr(EXPR_FUNAPP);
+ expr->e.e_funapp.line_nr = line;
+ expr->e.e_funapp.column = column;
+ expr->e.e_funapp.id = id;
+ expr->e.e_funapp.args_nr = args_nr;
+
+ for (i = 0; i < args_nr; i++)
+ expr->e.e_funapp.args[i] = args[i];
+ } else { /* failure */
+ expr = magic_new_expr(EXPR_VAL);
+ expr->e.e_val.ty = TY_FAIL;
+ }
+
+ return expr;
+}
+
+static spell_t *
+new_spell(spellguard_t *guard)
+{
+ static int spell_counter = 0;
+
+ spell_t *retval = (spell_t*)calloc(1, sizeof(spell_t));
+ retval->index = ++spell_counter;
+ retval->spellguard = guard;
+ return retval;
+}
+
+static spellguard_t *
+new_spellguard(int ty)
+{
+ spellguard_t *retval = (spellguard_t *)calloc(1, sizeof(spellguard_t));
+ retval->ty = ty;
+ return retval;
+}
+
+static spellguard_t *
+spellguard_implication(spellguard_t *a, spellguard_t *b)
+{
+ spellguard_t *retval = a;
+
+ if (a == b) /* This can happen due to reference sharing:
+ * e.g.,
+ * (R0 -> (R1 | R2)) => (R3)
+ * yields
+ * (R0 -> (R1 -> R3 | R2 -> R3))
+ *
+ * So if we now add => R4 to that, we want
+ * (R0 -> (R1 -> R3 -> R4 | R2 -> R3 -> R4))
+ *
+ * but we only need to add it once, because the R3 reference is shared.
+ */
+ return retval;
+
+ /* 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);
+ if (a->next)
+ spellguard_implication(a->next, b);
+ else
+ a->next = b;
+
+
+ return retval;
+}
+
+static effect_t *
+new_effect(int ty)
+{
+ effect_t *effect = (effect_t *) calloc(1, sizeof(effect_t));
+ effect->ty = ty;
+ return effect;
+}
+
+static effect_t *
+set_effect_continuation(effect_t *src, effect_t *continuation)
+{
+ effect_t *retval = src;
+ /* This function is completely analogous to `spellguard_implication' above; read the control flow implications above first before pondering it. */
+
+ if (src == continuation)
+ return retval;
+
+ /* 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) {
+ set_effect_continuation(src->e.e_if.true_branch, continuation);
+ set_effect_continuation(src->e.e_if.false_branch, continuation);
+ }
+ if (src->next)
+ set_effect_continuation(src->next, continuation);
+ else
+ src->next = continuation;
+
+ return retval;
+}
+
+static effect_t *
+op_effect(char *name, int args_nr, expr_t **args, int line, int column)
+{
+ int id;
+ effect_t *effect;
+ op_t *op = magic_get_op(name, &id);
+
+ if (!op)
+ fail(line, column, "Unknown operation `%s'\n", name);
+ else if (strlen(op->signature) != args_nr) {
+ fail(line, column, "Incorrect number of arguments to operation `%s': Expected %d, found %d\n", name, strlen(op->signature), args_nr);
+ op = NULL;
+ }
+
+ if (op) {
+ int i;
+
+ effect = new_effect(EFFECT_OP);
+ effect->e.e_op.line_nr = line;
+ effect->e.e_op.column = column;
+ effect->e.e_op.id = id;
+ effect->e.e_op.args_nr = args_nr;
+
+ for (i = 0; i < args_nr; i++)
+ effect->e.e_op.args[i] = args[i];
+ } else /* failure */
+ effect = new_effect(EFFECT_SKIP);
+
+ return effect;
+}
+
+
+proc_t *procs = NULL;
+int procs_nr = 0;
+
+// I think this is a memory leak, or undefined behavior
+static void
+install_proc(proc_t *proc)
+{
+ if (!procs) {
+ procs = proc;
+ procs_nr = 1;
+ } else {
+ RECREATE (procs, proc_t, 1 + procs_nr);
+ procs[procs_nr++] = *proc;
+ }
+}
+
+static effect_t *
+call_proc(char *name, int args_nr, expr_t **args, int line_nr, int column)
+{
+ proc_t *p = NULL;
+ int i;
+ effect_t *retval;
+
+ for (i = 0; i < procs_nr; i++)
+ if (!strcmp(procs[i].name, name)) {
+ p = &procs[i];
+ break;
+ }
+
+ if (!p) {
+ fail(line_nr, column, "Unknown procedure `%s'\n", name);
+ return new_effect(EFFECT_SKIP);
+ }
+
+ if (p->args_nr != args_nr) {
+ fail(line_nr, column, "Procedure %s/%d invoked with %d parameters\n", name, p->args_nr, args_nr);
+ return new_effect(EFFECT_SKIP);
+ }
+
+ retval = new_effect(EFFECT_CALL);
+ retval->e.e_call.body = p->body;
+ retval->e.e_call.args_nr = args_nr;
+ retval->e.e_call.formals = p->args;
+ retval->e.e_call.actuals = args;
+ return retval;
+}
+
+struct const_def_rec {
+ char *name;
+ val_t val;
+} *const_defs = NULL;
+
+int const_defs_nr = 0;
+
+static void
+bind_constant(char *name, val_t *val, int line_nr)
+{
+ if (find_constant(name)) {
+ fail(line_nr, 0, "Redefinition of constant `%s'\n", name);
+ return;
+ }
+
+ if (!const_defs)
+ const_defs = (struct const_def_rec *)malloc(sizeof(struct const_def_rec));
+ else
+ const_defs = (struct const_def_rec *)realloc(const_defs,
+ (const_defs_nr + 1) * sizeof(struct const_def_rec));
+
+ const_defs[const_defs_nr].name = name;
+ const_defs[const_defs_nr].val = *val;
+ ++const_defs_nr;
+}
+
+static val_t *
+find_constant(char *name)
+{
+ int i;
+ for (i = 0; i < const_defs_nr; i++) {
+ if (!strcmp(const_defs[i].name, name)) {
+ free(name);
+ return &const_defs[i].val;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+
+#define INTERN_ASSERT(name, id) { int zid = intern_id(name); 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); error_flag = 1; }
+
+extern FILE *magic_frontend_in;
+
+int
+magic_init(char *conffile) // must be called after itemdb initialisation
+{
+ int error_flag = 0;
+
+ magic_conf.vars_nr = 0;
+ magic_conf.var_name = (char **)malloc(1);
+ magic_conf.vars = (val_t *)malloc(1);
+
+ magic_conf.obscure_chance = 95;
+ magic_conf.min_casttime = 100;
+
+ magic_conf.spells_nr = 0;
+ CREATE(magic_conf.spells, spell_t *, 1);
+
+ magic_conf.anchors_nr = 0;
+ CREATE(magic_conf.anchors, teleport_anchor_t *, 1);
+
+ INTERN_ASSERT("min_casttime", VAR_MIN_CASTTIME);
+ INTERN_ASSERT("obscure_chance", VAR_OBSCURE_CHANCE);
+ INTERN_ASSERT("caster", VAR_CASTER);
+ INTERN_ASSERT("spellpower", VAR_SPELLPOWER);
+ INTERN_ASSERT("self_spell", VAR_SPELL);
+ INTERN_ASSERT("self_invocation", VAR_INVOCATION);
+ INTERN_ASSERT("target", VAR_TARGET);
+ INTERN_ASSERT("script_target", VAR_SCRIPTTARGET);
+ INTERN_ASSERT("location", VAR_LOCATION);
+
+ magic_frontend_in = fopen(conffile, "r");
+ if (!magic_frontend_in) {
+ fprintf(stderr, "[magic-conf] Magic configuration file `%s' not found -> no magic.\n", conffile);
+ return 0;
+ }
+ magic_frontend_parse();
+
+ if (magic_conf.vars[VAR_MIN_CASTTIME].ty == TY_INT)
+ magic_conf.min_casttime = magic_conf.vars[VAR_MIN_CASTTIME].v.v_int;
+
+ if (magic_conf.vars[VAR_OBSCURE_CHANCE].ty == TY_INT)
+ magic_conf.obscure_chance = magic_conf.vars[VAR_OBSCURE_CHANCE].v.v_int;
+
+ printf("[magic-conf] Magic initialised; obscure at %d%%. %d spells, %d teleport anchors.\n",
+ magic_conf.obscure_chance, magic_conf.spells_nr, magic_conf.anchors_nr);
+
+ if (procs)
+ free(procs);
+ return error_flag;
+}
+
+extern int magic_frontend_lineno;
+
+static void
+magic_frontend_error(const char *msg)
+{
+ fprintf(stderr, "[magic-conf] Parse error: %s at line %d\n", msg, magic_frontend_lineno);
+ failed_flag = 1;
+}