From e51389d68ef2e152054d987d7a99930bce180954 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 23 Mar 2010 17:35:53 -0400 Subject: Implement macro support. --- dimension/common.prologue | 1 + dimension/common.rules | 25 ++++-- dimension/directives.declarations | 3 +- dimension/directives.nonterminals | 5 ++ dimension/directives.rules | 125 ++++++++++++++++++++++---- dimension/grammar.epilogue | 90 +++++++++---------- dimension/parse.c | 7 +- dimension/parse.h | 5 +- dimension/tokenize.c | 180 +++++++++++++++++++++++++++++++++++++- tests/dimension/directives.pov | 22 +++-- tests/dimension/directives.sh | 20 +++-- 11 files changed, 398 insertions(+), 85 deletions(-) diff --git a/dimension/common.prologue b/dimension/common.prologue index e17bfa2..68f4f1a 100644 --- a/dimension/common.prologue +++ b/dimension/common.prologue @@ -56,6 +56,7 @@ dmnsn_new_astnode(dmnsn_astnode_type type, YYLTYPE lloc) .type = type, .children = dmnsn_new_array(sizeof(dmnsn_astnode)), .ptr = NULL, + .free_fn = NULL, .refcount = malloc(sizeof(unsigned int)), .filename = lloc.first_filename, .line = lloc.first_line, diff --git a/dimension/common.rules b/dimension/common.rules index e22995f..48b02c1 100644 --- a/dimension/common.rules +++ b/dimension/common.rules @@ -22,8 +22,18 @@ /* Fundamental language elements */ IDENTIFIER: "identifier" { + const char *id = $1; + dmnsn_astnode *symbol = dmnsn_find_symbol(symtable, id); + while (symbol && symbol->type == DMNSN_AST_IDENTIFIER) { + id = symbol->ptr; + symbol = dmnsn_find_symbol(symtable, id); + } $$ = dmnsn_new_astnode(DMNSN_AST_IDENTIFIER, @$); - $$.ptr = $1; + $$.ptr = strdup(id); + if (!$$.ptr) + dmnsn_error(DMNSN_SEVERITY_HIGH, + "Couldn't allocate room for identifier."); + free($1); } ; @@ -653,18 +663,19 @@ COLOR_KEYWORD_GROUP_INIT: /* empty */ { } ; -COLOR_KEYWORD_ITEM: "identifier" { - dmnsn_astnode *symbol = dmnsn_find_symbol(symtable, $1); +COLOR_KEYWORD_ITEM: IDENTIFIER { + dmnsn_astnode *symbol = dmnsn_find_symbol(symtable, $1.ptr); if (!symbol) { dmnsn_diagnostic(@1.first_filename, @1.first_line, @1.first_column, - "Unbound identifier '%s'", $1); - free($1); + "Unbound identifier '%s'", + (const char *)$1.ptr); + dmnsn_delete_astnode($1); YYERROR; } else { dmnsn_astnode eval = dmnsn_eval_vector(*symbol, symtable); if (eval.type == DMNSN_AST_NONE) { - free($1); + dmnsn_delete_astnode($1); YYERROR; } @@ -672,7 +683,7 @@ COLOR_KEYWORD_ITEM: "identifier" { dmnsn_delete_astnode(eval); } - free($1); + dmnsn_delete_astnode($1); } | "red" FLOAT { dmnsn_astnode old; diff --git a/dimension/directives.declarations b/dimension/directives.declarations index 59dd87e..541ae41 100644 --- a/dimension/directives.declarations +++ b/dimension/directives.declarations @@ -21,7 +21,8 @@ %name-prefix "dmnsn_ld_yy" -%expect 10 +%expect 12 +%expect-rr 6 %parse-param {const char *filename} %parse-param {void *yyscanner} diff --git a/dimension/directives.nonterminals b/dimension/directives.nonterminals index 6f858af..a864910 100644 --- a/dimension/directives.nonterminals +++ b/dimension/directives.nonterminals @@ -20,3 +20,8 @@ *************************************************************************/ %type RVALUE +%type DECL_PARAMS +%type DECL_PARAM_LIST +%type PARAMS +%type PARAM_LIST +%type PARAM diff --git a/dimension/directives.rules b/dimension/directives.rules index b995c78..c628807 100644 --- a/dimension/directives.rules +++ b/dimension/directives.rules @@ -8,19 +8,19 @@ LANGUAGE_DIRECTIVE: "#include" STRING { dmnsn_declare_symbol(symtable, "__include__", $2); dmnsn_delete_astnode($2); } - | "#declare" "identifier" "=" RVALUE { - dmnsn_declare_symbol(symtable, $2, $4); - free($2); + | "#declare" IDENTIFIER "=" RVALUE { + dmnsn_declare_symbol(symtable, $2.ptr, $4); + dmnsn_delete_astnode($2); dmnsn_delete_astnode($4); } - | "#local" "identifier" "=" RVALUE { - dmnsn_local_symbol(symtable, $2, $4); - free($2); + | "#local" IDENTIFIER "=" RVALUE { + dmnsn_local_symbol(symtable, $2.ptr, $4); + dmnsn_delete_astnode($2); dmnsn_delete_astnode($4); } - | "#undef" "identifier" { - dmnsn_undef_symbol(symtable, $2); - free($2); + | "#undef" IDENTIFIER { + dmnsn_undef_symbol(symtable, $2.ptr); + dmnsn_delete_astnode($2); } | "#if" "(" CONDITIONAL ")" { dmnsn_astnode cond = dmnsn_eval($3, symtable); @@ -34,17 +34,17 @@ LANGUAGE_DIRECTIVE: "#include" STRING { dmnsn_local_symbol(symtable, "__cond__", cond); dmnsn_delete_astnode(cond); } - | "#ifdef" "(" "identifier" ")" { - dmnsn_astnode *node = dmnsn_find_symbol(symtable, $3); + | "#ifdef" "(" IDENTIFIER ")" { + dmnsn_astnode *node = dmnsn_find_symbol(symtable, $3.ptr); dmnsn_local_symbol(symtable, "__cond__", dmnsn_new_ast_integer(node ? 1 : 0)); - free($3); + dmnsn_delete_astnode($3); } - | "#ifndef" "(" "identifier" ")" { - dmnsn_astnode *node = dmnsn_find_symbol(symtable, $3); + | "#ifndef" "(" IDENTIFIER ")" { + dmnsn_astnode *node = dmnsn_find_symbol(symtable, $3.ptr); dmnsn_local_symbol(symtable, "__cond__", dmnsn_new_ast_integer(node ? 0 : 1)); - free($3); + dmnsn_delete_astnode($3); } | "#version" FLOAT ";" { dmnsn_diagnostic(@$.first_filename, @$.first_line, @@ -70,6 +70,42 @@ LANGUAGE_DIRECTIVE: "#include" STRING { dmnsn_delete_astnode($2); YYERROR; } + | "#macro" IDENTIFIER "(" DECL_PARAMS ")" { + dmnsn_declare_symbol(symtable, $2.ptr, $4); + dmnsn_local_symbol(symtable, "__macro__", + dmnsn_new_ast_string($2.ptr)); + dmnsn_delete_astnode($2); + dmnsn_delete_astnode($4); + } + | IDENTIFIER "(" PARAMS ")" { + dmnsn_astnode *node = dmnsn_find_symbol(symtable, $1.ptr); + dmnsn_assert(node && node->type == DMNSN_AST_MACRO, + "Attempt to expand non-macro."); + dmnsn_delete_astnode($1); + + unsigned int nparams = dmnsn_array_size(node->children); + unsigned int nparams_given = dmnsn_array_size($3.children); + + if (nparams_given != nparams) { + dmnsn_diagnostic(@$.first_filename, @$.first_line, + @$.first_column, + "wrong number of macro arguments" + " (%u; should be %u)", + nparams_given, nparams); + dmnsn_delete_astnode($3); + YYERROR; + } + + unsigned int i; + for (i = 0; i < nparams; ++i) { + dmnsn_astnode id, param; + dmnsn_array_get(node->children, i, &id); + dmnsn_array_get($3.children, i, ¶m); + dmnsn_local_symbol(symtable, id.ptr, param); + } + + dmnsn_delete_astnode($3); + } ; RVALUE: ARITH_EXPR ";" %dprec 2 { @@ -90,3 +126,62 @@ RVALUE: ARITH_EXPR ";" %dprec 2 { | CAMERA | TRANSFORMATION ; + +DECL_PARAMS: /* empty */ { + $$ = dmnsn_new_astnode(DMNSN_AST_MACRO, @$); + } + | DECL_PARAM_LIST +; + +DECL_PARAM_LIST: IDENTIFIER { + $$ = dmnsn_new_astnode(DMNSN_AST_MACRO, @$); + dmnsn_array_push($$.children, &$1); + } + | DECL_PARAM_LIST "," IDENTIFIER { + $$ = $1; + dmnsn_array_push($$.children, &$3); + } +; + +PARAMS: /* empty */ { + $$ = dmnsn_new_astnode(DMNSN_AST_ARRAY, @$); + } + | PARAM_LIST +; + +PARAM_LIST: IDENTIFIER %dprec 2 { + $$ = dmnsn_new_astnode(DMNSN_AST_MACRO, @$); + dmnsn_array_push($$.children, &$1); + } + | PARAM %dprec 1 { + $$ = dmnsn_new_astnode(DMNSN_AST_MACRO, @$); + dmnsn_array_push($$.children, &$1); + } + | PARAM_LIST "," IDENTIFIER %dprec 2 { + $$ = $1; + dmnsn_array_push($$.children, &$3); + } + | PARAM_LIST "," PARAM %dprec 1 { + $$ = $1; + dmnsn_array_push($$.children, &$3); + } +; + +PARAM: ARITH_EXPR %dprec 2 { + $$ = dmnsn_eval($1, symtable); + dmnsn_delete_astnode($1); + + if ($$.type == DMNSN_AST_NONE) { + dmnsn_delete_astnode($$); + YYERROR; + } + } + | COLOR %dprec 1 + | OBJECT + | TEXTURE + | PIGMENT + | FINISH + | INTERIOR + | CAMERA + | TRANSFORMATION +; diff --git a/dimension/grammar.epilogue b/dimension/grammar.epilogue index 6393044..a61aff5 100644 --- a/dimension/grammar.epilogue +++ b/dimension/grammar.epilogue @@ -180,52 +180,52 @@ dmnsn_astnode_string(dmnsn_astnode_type astnode_type) dmnsn_astnode_map(DMNSN_AST_NOT, "!" ); dmnsn_astnode_map(DMNSN_AST_TERNARY, "?:"); - dmnsn_astnode_map(DMNSN_AST_ABS, "abs" ); - dmnsn_astnode_map(DMNSN_AST_ACOS, "acos" ); - dmnsn_astnode_map(DMNSN_AST_ACOSH, "acosh" ); - dmnsn_astnode_map(DMNSN_AST_ASC, "asc" ); - dmnsn_astnode_map(DMNSN_AST_ASIN, "asin" ); - dmnsn_astnode_map(DMNSN_AST_ASINH, "asinh" ); - dmnsn_astnode_map(DMNSN_AST_ATAN, "atan" ); - dmnsn_astnode_map(DMNSN_AST_ATAN2, "atan2" ); - dmnsn_astnode_map(DMNSN_AST_ATANH, "atanh" ); - dmnsn_astnode_map(DMNSN_AST_CEIL, "ceil" ); - dmnsn_astnode_map(DMNSN_AST_COS, "cos" ); - dmnsn_astnode_map(DMNSN_AST_COSH, "cosh" ); - dmnsn_astnode_map(DMNSN_AST_DEGREES, "degrees" ); - dmnsn_astnode_map(DMNSN_AST_INT_DIV, "div" ); - dmnsn_astnode_map(DMNSN_AST_EXP, "exp" ); - dmnsn_astnode_map(DMNSN_AST_FLOOR, "floor" ); - dmnsn_astnode_map(DMNSN_AST_INT, "int" ); - dmnsn_astnode_map(DMNSN_AST_LN, "ln" ); - dmnsn_astnode_map(DMNSN_AST_LOG, "log" ); - dmnsn_astnode_map(DMNSN_AST_MAX, "max" ); - dmnsn_astnode_map(DMNSN_AST_MIN, "min" ); - dmnsn_astnode_map(DMNSN_AST_MOD, "mod" ); - dmnsn_astnode_map(DMNSN_AST_POW, "pow" ); - dmnsn_astnode_map(DMNSN_AST_RADIANS, "radians" ); - dmnsn_astnode_map(DMNSN_AST_SIN, "sin" ); - dmnsn_astnode_map(DMNSN_AST_SINH, "sinh" ); - dmnsn_astnode_map(DMNSN_AST_SQRT, "sqrt" ); - dmnsn_astnode_map(DMNSN_AST_STRCMP, "strcmp" ); - dmnsn_astnode_map(DMNSN_AST_STRLEN, "strlen" ); - dmnsn_astnode_map(DMNSN_AST_TAN, "tan" ); - dmnsn_astnode_map(DMNSN_AST_TANH, "tanh" ); - dmnsn_astnode_map(DMNSN_AST_VAL, "val" ); + dmnsn_astnode_map(DMNSN_AST_ABS, "abs"); + dmnsn_astnode_map(DMNSN_AST_ACOS, "acos"); + dmnsn_astnode_map(DMNSN_AST_ACOSH, "acosh"); + dmnsn_astnode_map(DMNSN_AST_ASC, "asc"); + dmnsn_astnode_map(DMNSN_AST_ASIN, "asin"); + dmnsn_astnode_map(DMNSN_AST_ASINH, "asinh"); + dmnsn_astnode_map(DMNSN_AST_ATAN, "atan"); + dmnsn_astnode_map(DMNSN_AST_ATAN2, "atan2"); + dmnsn_astnode_map(DMNSN_AST_ATANH, "atanh"); + dmnsn_astnode_map(DMNSN_AST_CEIL, "ceil"); + dmnsn_astnode_map(DMNSN_AST_COS, "cos"); + dmnsn_astnode_map(DMNSN_AST_COSH, "cosh"); + dmnsn_astnode_map(DMNSN_AST_DEGREES, "degrees"); + dmnsn_astnode_map(DMNSN_AST_INT_DIV, "div"); + dmnsn_astnode_map(DMNSN_AST_EXP, "exp"); + dmnsn_astnode_map(DMNSN_AST_FLOOR, "floor"); + dmnsn_astnode_map(DMNSN_AST_INT, "int"); + dmnsn_astnode_map(DMNSN_AST_LN, "ln"); + dmnsn_astnode_map(DMNSN_AST_LOG, "log"); + dmnsn_astnode_map(DMNSN_AST_MAX, "max"); + dmnsn_astnode_map(DMNSN_AST_MIN, "min"); + dmnsn_astnode_map(DMNSN_AST_MOD, "mod"); + dmnsn_astnode_map(DMNSN_AST_POW, "pow"); + dmnsn_astnode_map(DMNSN_AST_RADIANS, "radians"); + dmnsn_astnode_map(DMNSN_AST_SIN, "sin"); + dmnsn_astnode_map(DMNSN_AST_SINH, "sinh"); + dmnsn_astnode_map(DMNSN_AST_SQRT, "sqrt"); + dmnsn_astnode_map(DMNSN_AST_STRCMP, "strcmp"); + dmnsn_astnode_map(DMNSN_AST_STRLEN, "strlen"); + dmnsn_astnode_map(DMNSN_AST_TAN, "tan"); + dmnsn_astnode_map(DMNSN_AST_TANH, "tanh"); + dmnsn_astnode_map(DMNSN_AST_VAL, "val"); dmnsn_astnode_map(DMNSN_AST_VAXIS_ROTATE, "vaxis_rotate"); - dmnsn_astnode_map(DMNSN_AST_VCROSS, "vcross" ); - dmnsn_astnode_map(DMNSN_AST_VDOT, "vdot" ); - dmnsn_astnode_map(DMNSN_AST_VLENGTH, "vlength" ); - dmnsn_astnode_map(DMNSN_AST_VNORMALIZE, "vnormalize" ); - dmnsn_astnode_map(DMNSN_AST_VROTATE, "vrotate" ); - - dmnsn_astnode_map(DMNSN_AST_PI, "pi" ); - dmnsn_astnode_map(DMNSN_AST_TRUE, "true" ); + dmnsn_astnode_map(DMNSN_AST_VCROSS, "vcross"); + dmnsn_astnode_map(DMNSN_AST_VDOT, "vdot"); + dmnsn_astnode_map(DMNSN_AST_VLENGTH, "vlength"); + dmnsn_astnode_map(DMNSN_AST_VNORMALIZE, "vnormalize"); + dmnsn_astnode_map(DMNSN_AST_VROTATE, "vrotate"); + + dmnsn_astnode_map(DMNSN_AST_PI, "pi"); + dmnsn_astnode_map(DMNSN_AST_TRUE, "true"); dmnsn_astnode_map(DMNSN_AST_FALSE, "false"); - dmnsn_astnode_map(DMNSN_AST_X, "x" ); - dmnsn_astnode_map(DMNSN_AST_Y, "y" ); - dmnsn_astnode_map(DMNSN_AST_Z, "z" ); - dmnsn_astnode_map(DMNSN_AST_T, "t" ); + dmnsn_astnode_map(DMNSN_AST_X, "x"); + dmnsn_astnode_map(DMNSN_AST_Y, "y"); + dmnsn_astnode_map(DMNSN_AST_Z, "z"); + dmnsn_astnode_map(DMNSN_AST_T, "t"); dmnsn_astnode_map(DMNSN_AST_NEGATE, "-"); dmnsn_astnode_map(DMNSN_AST_DOT_X, ".x"); @@ -240,6 +240,8 @@ dmnsn_astnode_string(dmnsn_astnode_type astnode_type) dmnsn_astnode_map(DMNSN_AST_ARRAY, "array"); + dmnsn_astnode_map(DMNSN_AST_MACRO, "macro"); + default: fprintf(stderr, "Warning: unrecognised astnode type %d.\n", (int)astnode_type); diff --git a/dimension/parse.c b/dimension/parse.c index 4008fcf..c568271 100644 --- a/dimension/parse.c +++ b/dimension/parse.c @@ -338,6 +338,7 @@ dmnsn_new_astnode(dmnsn_astnode_type type) .type = type, .children = NULL, .ptr = NULL, + .free_fn = NULL, .refcount = malloc(sizeof(unsigned int)), .filename = "", .line = -1, @@ -479,7 +480,11 @@ dmnsn_delete_astnode(dmnsn_astnode astnode) { if (*astnode.refcount <= 1) { dmnsn_delete_astree(astnode.children); - free(astnode.ptr); + if (astnode.free_fn) { + (*astnode.free_fn)(astnode.ptr); + } else { + free(astnode.ptr); + } free(astnode.refcount); } else { --*astnode.refcount; diff --git a/dimension/parse.h b/dimension/parse.h index 35a82db..a34e546 100644 --- a/dimension/parse.h +++ b/dimension/parse.h @@ -152,7 +152,9 @@ typedef enum { DMNSN_AST_STRING, - DMNSN_AST_ARRAY + DMNSN_AST_ARRAY, + + DMNSN_AST_MACRO } dmnsn_astnode_type; /* Abstract syntax tree node (a dmnsn_array* of these is an AST) */ @@ -164,6 +166,7 @@ typedef struct dmnsn_astnode { /* Generic data pointer */ void *ptr; + dmnsn_free_fn *free_fn; /* Reference count */ unsigned int *refcount; diff --git a/dimension/tokenize.c b/dimension/tokenize.c index 2bead0b..fb50cd6 100644 --- a/dimension/tokenize.c +++ b/dimension/tokenize.c @@ -62,8 +62,9 @@ dmnsn_new_token_buffer(int type, dmnsn_token_buffer *prev, const char *filename) } static void -dmnsn_delete_token_buffer(dmnsn_token_buffer *tbuffer) +dmnsn_delete_token_buffer(void *ptr) { + dmnsn_token_buffer *tbuffer = ptr; if (tbuffer) { unsigned int i; for (i = 0; i < dmnsn_array_size(tbuffer->buffered); ++i) { @@ -636,6 +637,148 @@ dmnsn_stream_buffer(int token, dmnsn_token_buffer *prev, return tbuffer; } +static bool +dmnsn_declare_macro(int token, dmnsn_token_buffer *prev, + dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, + const char *filename, dmnsn_symbol_table *symtable, + void *yyscanner) +{ + dmnsn_token_buffer *decl_buffer + = dmnsn_new_token_buffer(DMNSN_T_LEX_VERBATIM, prev, filename); + + /* Buffer the current token */ + dmnsn_buffered_token buffered = { + .type = token, + .lval = *lvalp, + .lloc = *llocp + }; + dmnsn_array_push(decl_buffer->buffered, &buffered); + + /* Grab all the tokens belonging to the #macro ID (...) */ + if (dmnsn_buffer_balanced(decl_buffer, true, DMNSN_T_LPAREN, DMNSN_T_RPAREN, + filename, symtable, yyscanner) + != 0) + { + dmnsn_delete_token_buffer(decl_buffer); + return false; + } + + /* Fake EOF */ + buffered.type = DMNSN_T_EOF; + buffered.lval.value = NULL; + dmnsn_array_push(decl_buffer->buffered, &buffered); + + dmnsn_yyset_extra(decl_buffer, yyscanner); + if (dmnsn_ld_yyparse(filename, yyscanner, symtable) != 0) { + dmnsn_yyset_extra(decl_buffer->prev, yyscanner); + dmnsn_delete_token_buffer(decl_buffer); + return false; + } + + dmnsn_yyset_extra(decl_buffer->prev, yyscanner); + dmnsn_delete_token_buffer(decl_buffer); + + dmnsn_token_buffer *tbuffer = dmnsn_new_token_buffer(token, NULL, filename); + + dmnsn_astnode *mname = dmnsn_find_symbol(symtable, "__macro__"); + dmnsn_assert(mname, "__macro__ unset."); + dmnsn_assert(mname->type == DMNSN_AST_STRING, "__macro__ has wrong type."); + dmnsn_astnode *mnode = dmnsn_find_symbol(symtable, mname->ptr); + dmnsn_assert(mnode, "#macro unset."); + dmnsn_assert(mnode->type == DMNSN_AST_MACRO, "#macro has wrong type."); + dmnsn_undef_symbol(symtable, "__macro__"); + + int nesting = 1; + while (1) { + /* Non-recursive call */ + buffered.type = dmnsn_yylex_wrapper(&buffered.lval, &buffered.lloc, + filename, symtable, yyscanner); + + if (buffered.type == DMNSN_T_EOF) { + dmnsn_diagnostic(filename, buffered.lloc.first_line, + buffered.lloc.first_column, + "syntax error, unexpected end-of-file"); + dmnsn_delete_token_buffer(tbuffer); + return false; + } + + switch (buffered.type) { + case DMNSN_T_IF: + case DMNSN_T_IFDEF: + case DMNSN_T_IFNDEF: + case DMNSN_T_MACRO: + case DMNSN_T_SWITCH: + case DMNSN_T_WHILE: + ++nesting; + break; + + case DMNSN_T_END: + --nesting; + break; + + default: + break; + } + + if (nesting == 0) + break; + + dmnsn_array_push(tbuffer->buffered, &buffered); + } + + mnode->ptr = tbuffer; + mnode->free_fn = &dmnsn_delete_token_buffer; + return true; +} + +static dmnsn_token_buffer * +dmnsn_macro_buffer(int token, dmnsn_astnode *mnode, dmnsn_token_buffer *prev, + dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, + const char *filename, dmnsn_symbol_table *symtable, + void *yyscanner) +{ + dmnsn_token_buffer *invoke_buffer + = dmnsn_new_token_buffer(DMNSN_T_LEX_VERBATIM, prev, filename); + + /* Buffer the current token */ + dmnsn_buffered_token buffered = { + .type = token, + .lval = *lvalp, + .lloc = *llocp + }; + dmnsn_array_push(invoke_buffer->buffered, &buffered); + + /* Grab all the tokens belonging to the #macro ID (...) */ + if (dmnsn_buffer_balanced(invoke_buffer, true, DMNSN_T_LPAREN, DMNSN_T_RPAREN, + filename, symtable, yyscanner) + != 0) + { + dmnsn_delete_token_buffer(invoke_buffer); + return NULL; + } + + /* Fake EOF */ + buffered.type = DMNSN_T_EOF; + buffered.lval.value = NULL; + dmnsn_array_push(invoke_buffer->buffered, &buffered); + + dmnsn_yyset_extra(invoke_buffer, yyscanner); + dmnsn_push_scope(symtable); + if (dmnsn_ld_yyparse(filename, yyscanner, symtable) != 0) { + dmnsn_yyset_extra(invoke_buffer->prev, yyscanner); + dmnsn_delete_token_buffer(invoke_buffer); + return NULL; + } + + dmnsn_yyset_extra(invoke_buffer->prev, yyscanner); + dmnsn_delete_token_buffer(invoke_buffer); + + dmnsn_token_buffer *tbuffer = mnode->ptr; + tbuffer->i = 0; + tbuffer->prev = prev; + return tbuffer; +} + int dmnsn_yylex_impl(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, const char *filename, void *yyscanner); @@ -660,7 +803,11 @@ dmnsn_yylex_wrapper(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, } dmnsn_yyset_extra(tbuffer->prev, yyscanner); - dmnsn_delete_token_buffer(tbuffer); + if (tbuffer->type == DMNSN_T_MACRO) { + dmnsn_pop_scope(symtable); + } else { + dmnsn_delete_token_buffer(tbuffer); + } tbuffer = dmnsn_yyget_extra(yyscanner); } } @@ -862,6 +1009,35 @@ dmnsn_yylex(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, break; } + case DMNSN_T_MACRO: + { + bool status = dmnsn_declare_macro( + token, tbuffer, lvalp, llocp, filename, symtable, yyscanner + ); + if (!status) { + return DMNSN_T_LEX_ERROR; + } + break; + } + + case DMNSN_T_IDENTIFIER: + { + dmnsn_astnode *symbol = dmnsn_find_symbol(symtable, lvalp->value); + if (symbol && symbol->type == DMNSN_AST_MACRO) { + dmnsn_token_buffer *tb = dmnsn_macro_buffer( + token, symbol, tbuffer, lvalp, llocp, filename, symtable, yyscanner + ); + if (!tb) { + return DMNSN_T_LEX_ERROR; + } + + dmnsn_yyset_extra(tb, yyscanner); + break; + } else { + return token; + } + } + default: return token; } diff --git a/tests/dimension/directives.pov b/tests/dimension/directives.pov index 416bbf5..e4721fc 100644 --- a/tests/dimension/directives.pov +++ b/tests/dimension/directives.pov @@ -40,18 +40,26 @@ #error "#undef failed" #end +#macro Make_Sphere(n) + sphere { + Center + <0, n, 0>, R + pigment { + color Color green 1 + } + } +#end + +#macro Inc(n) + #declare n = n + 1; +#end + #declare Counter = 0; #while (Counter < 2) #if (#if (1 = 1) 0 #end = 0 & !1) #error "Nested #if parsing failed" #else - sphere { - Center + <0, Counter, 0>, R - pigment { - color Color green 1 - } - } + Make_Sphere(Counter) #end - #declare Counter = Counter + 1; + Inc(Counter) #end diff --git a/tests/dimension/directives.sh b/tests/dimension/directives.sh index 42815a6..713a1fe 100755 --- a/tests/dimension/directives.sh +++ b/tests/dimension/directives.sh @@ -35,19 +35,25 @@ directives_exp="$(echo -n \ #ifdef \( (identifier "Unused") \) #error (string "#undef failed") #end + #macro (identifier "Make_Sphere") \( (identifier "n") \) + sphere { + (identifier "Center") + < (integer "0") , (identifier "n") , (integer "0") > , (identifier "R") + pigment { + color (identifier "Color") green (integer "1") + } + } + #end + #macro (identifier "Inc") \( (identifier "n") \) + #declare (identifier "n") = (identifier "n") + (integer "1") ; + #end #declare (identifier "Counter") = (integer "0") ; #while \( (identifier "Counter") < (integer "2") \) #if \( #if \( (integer "1") = (integer "1") \) (integer "0") #end = (integer "0") & ! (integer "1") \) #error (string "Nested #if parsing failed") #else - sphere { - (identifier "Center") + < (integer "0") , (identifier "Counter") , (integer "0") > , (identifier "R") - pigment { - color (identifier "Color") green (integer "1") - } - } + (identifier "Make_Sphere") \( (identifier "Counter") \) #end - #declare (identifier "Counter") = (identifier "Counter") + (integer "1") ; + (identifier "Inc") \( (identifier "Counter") \) #end)' \ | tr '\n' ' ' | sed -r 's/[[:space:]]+/ /g') $(echo -n \ -- cgit v1.2.3