diff options
author | Ben Longbons <b.r.longbons@gmail.com> | 2012-08-30 16:16:25 -0700 |
---|---|---|
committer | Ben Longbons <b.r.longbons@gmail.com> | 2012-08-30 17:03:31 -0700 |
commit | 41974ae5265fbc23a06f276f9e008d5dad020e0b (patch) | |
tree | 9d595215172e87e2d83b74f7bf3430b3040e780e /src/map/magic-interpreter-parser.ypp | |
parent | 21742909143df9159b2401c3e2a39cc0b2bad620 (diff) | |
download | tmwa-41974ae5265fbc23a06f276f9e008d5dad020e0b.tar.gz tmwa-41974ae5265fbc23a06f276f9e008d5dad020e0b.tar.bz2 tmwa-41974ae5265fbc23a06f276f9e008d5dad020e0b.tar.xz tmwa-41974ae5265fbc23a06f276f9e008d5dad020e0b.zip |
Rename files for C++ conversion. Does not compile.
After updating, you can remove these files, as shown in 'git status':
Untracked files:
(use "git add <file>..." to include in what will be committed)
src/map/magic-interpreter-lexer.c
src/map/magic-interpreter-parser.c
src/map/magic-interpreter-parser.h
Diffstat (limited to 'src/map/magic-interpreter-parser.ypp')
-rw-r--r-- | src/map/magic-interpreter-parser.ypp | 1089 |
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; +} |