summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@gmail.com>2010-03-23 17:35:53 -0400
committerTavian Barnes <tavianator@gmail.com>2010-03-23 17:48:37 -0400
commite51389d68ef2e152054d987d7a99930bce180954 (patch)
tree78a59584f7e640e0d077a5d887305e3b5fdba060
parent1d441aea4446484342f93fbf315f7f70de8adaf7 (diff)
downloaddimension-e51389d68ef2e152054d987d7a99930bce180954.tar.xz
Implement macro support.
-rw-r--r--dimension/common.prologue1
-rw-r--r--dimension/common.rules25
-rw-r--r--dimension/directives.declarations3
-rw-r--r--dimension/directives.nonterminals5
-rw-r--r--dimension/directives.rules125
-rw-r--r--dimension/grammar.epilogue90
-rw-r--r--dimension/parse.c7
-rw-r--r--dimension/parse.h5
-rw-r--r--dimension/tokenize.c180
-rw-r--r--tests/dimension/directives.pov22
-rwxr-xr-xtests/dimension/directives.sh20
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 <astnode> RVALUE
+%type <astnode> DECL_PARAMS
+%type <astnode> DECL_PARAM_LIST
+%type <astnode> PARAMS
+%type <astnode> PARAM_LIST
+%type <astnode> 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, &param);
+ 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 = "<environment>",
.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 \