From 58bd8b3b09a11ee2f698e21608ef71c65d97f804 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 3 Feb 2010 02:38:30 -0500 Subject: Implement #while. --- dimension/common.terminals | 2 +- dimension/lexer.l | 1 + dimension/parse.c | 2 +- dimension/tokenize.c | 191 ++++++++++++++++++++++++++++++++--------- tests/dimension/directives.pov | 21 +++-- tests/dimension/directives.sh | 27 ++++-- 6 files changed, 185 insertions(+), 59 deletions(-) diff --git a/dimension/common.terminals b/dimension/common.terminals index 86205b5..c8323a4 100644 --- a/dimension/common.terminals +++ b/dimension/common.terminals @@ -515,7 +515,7 @@ %token DMNSN_T_UNDEF "#undef" %token DMNSN_T_VERSION %token DMNSN_T_WARNING -%token DMNSN_T_WHILE +%token DMNSN_T_WHILE "#while" %token DMNSN_T_WRITE /* Identifiers */ diff --git a/dimension/lexer.l b/dimension/lexer.l index 14096d1..24195c0 100644 --- a/dimension/lexer.l +++ b/dimension/lexer.l @@ -216,6 +216,7 @@ unsigned long wchar; "#include" RETURN_TOKEN(DMNSN_T_INCLUDE); "#local" RETURN_TOKEN(DMNSN_T_LOCAL); "#undef" RETURN_TOKEN(DMNSN_T_UNDEF); +"#while" RETURN_TOKEN(DMNSN_T_WHILE); (?# Identifiers) [[:alpha:]][[:alnum:]_]* RETURN_VALUE_TOKEN(DMNSN_T_IDENTIFIER); diff --git a/dimension/parse.c b/dimension/parse.c index bd40ff8..e15a8cf 100644 --- a/dimension/parse.c +++ b/dimension/parse.c @@ -806,7 +806,7 @@ dmnsn_eval(dmnsn_astnode astnode, dmnsn_symbol_table *symtable) return dmnsn_eval(*symbol, symtable); } else { dmnsn_diagnostic(astnode.filename, astnode.line, astnode.col, - "unbound identifier '%s'", astnode.ptr); + "Unbound identifier '%s'", astnode.ptr); dmnsn_astnode error = dmnsn_new_astnode(DMNSN_AST_NONE); ++*error.refcount; return error; diff --git a/dimension/tokenize.c b/dimension/tokenize.c index 84d5f5d..bee03b6 100644 --- a/dimension/tokenize.c +++ b/dimension/tokenize.c @@ -58,13 +58,21 @@ static void dmnsn_delete_token_buffer(dmnsn_token_buffer *tbuffer) { if (tbuffer) { + unsigned int i; + for (i = 0; i < dmnsn_array_size(tbuffer->buffered); ++i) { + dmnsn_buffered_token buffered; + dmnsn_array_get(tbuffer->buffered, i, &buffered); + free(buffered.lval.value); + } + dmnsn_delete_array(tbuffer->buffered); free(tbuffer); } } -int dmnsn_yylex_impl(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, - const char *filename, void *yyscanner); +static int +dmnsn_yylex_wrapper(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, + const char *filename, void *yyscanner); int dmnsn_ld_yyparse(const char *filename, void *yyscanner, dmnsn_symbol_table *symtable); @@ -123,6 +131,7 @@ dmnsn_declaration_buffer(int token, dmnsn_token_buffer *prev, /* Fake EOF */ buffered.type = DMNSN_T_EOF; + buffered.lval.value = NULL; dmnsn_array_push(tbuffer->buffered, &buffered); return tbuffer; @@ -164,6 +173,7 @@ dmnsn_undef_buffer(int token, dmnsn_token_buffer *prev, /* Fake EOF */ buffered.type = DMNSN_T_EOF; + buffered.lval.value = NULL; dmnsn_array_push(tbuffer->buffered, &buffered); return tbuffer; @@ -221,6 +231,7 @@ dmnsn_if_buffer(int token, dmnsn_token_buffer *prev, /* Fake EOF */ buffered.type = DMNSN_T_EOF; + buffered.lval.value = NULL; dmnsn_array_push(cond_buffer->buffered, &buffered); dmnsn_yyset_extra(cond_buffer, yyscanner); @@ -254,8 +265,8 @@ dmnsn_if_buffer(int token, dmnsn_token_buffer *prev, int nesting = 1; while (1) { /* Non-recursive call */ - buffered.type = dmnsn_yylex_impl(&buffered.lval, &buffered.lloc, - filename, yyscanner); + buffered.type = dmnsn_yylex_wrapper(&buffered.lval, &buffered.lloc, + filename, yyscanner); if (buffered.type == DMNSN_T_EOF) { dmnsn_diagnostic(filename, buffered.lloc.first_line, @@ -291,12 +302,7 @@ dmnsn_if_buffer(int token, dmnsn_token_buffer *prev, } if (cond) { - if (buffered.type == DMNSN_T_LEX_ERROR) { - dmnsn_delete_token_buffer(tbuffer); - return NULL; - } else { - dmnsn_array_push(tbuffer->buffered, &buffered); - } + dmnsn_array_push(tbuffer->buffered, &buffered); } else { free(buffered.lval.value); } @@ -305,50 +311,141 @@ dmnsn_if_buffer(int token, dmnsn_token_buffer *prev, return tbuffer; } +static dmnsn_token_buffer * +dmnsn_while_buffer(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 *tbuffer = dmnsn_new_token_buffer(token, prev); + + /* Pretend to be an if */ + dmnsn_buffered_token buffered = { + .type = DMNSN_T_IF, + .lval = *lvalp, + .lloc = *llocp + }; + dmnsn_array_push(tbuffer->buffered, &buffered); + + /* Grab all the tokens belonging to the #while ... #end */ + int nesting = 1; + while (1) { + /* Non-recursive call */ + buffered.type = dmnsn_yylex_wrapper(&buffered.lval, &buffered.lloc, + filename, 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 NULL; + } + + dmnsn_array_push(tbuffer->buffered, &buffered); + + 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; + } + } + + return tbuffer; +} + +int dmnsn_yylex_impl(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, + const char *filename, void *yyscanner); + +static int +dmnsn_yylex_wrapper(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, + const char *filename, void *yyscanner) +{ + dmnsn_token_buffer *tbuffer = dmnsn_yyget_extra(yyscanner); + + if (tbuffer) { + while (tbuffer && tbuffer->i >= dmnsn_array_size(tbuffer->buffered)) { + if (tbuffer->type == DMNSN_T_WHILE) { + tbuffer->i = 0; + } else { + if (dmnsn_array_size(tbuffer->buffered) == 0 + && tbuffer->prev && tbuffer->prev->type == DMNSN_T_WHILE) + { + dmnsn_yyset_extra(tbuffer->prev, yyscanner); + dmnsn_delete_token_buffer(tbuffer); + tbuffer = dmnsn_yyget_extra(yyscanner); + } + + dmnsn_yyset_extra(tbuffer->prev, yyscanner); + dmnsn_delete_token_buffer(tbuffer); + tbuffer = dmnsn_yyget_extra(yyscanner); + } + } + } + + int token; + + if (tbuffer) { + /* Return buffered tokens */ + dmnsn_buffered_token buffered; + + dmnsn_array_get(tbuffer->buffered, tbuffer->i, &buffered); + token = buffered.type; + + if (buffered.lval.value) { + lvalp->value = strdup(buffered.lval.value); + } else { + lvalp->value = NULL; + } + + *llocp = buffered.lloc; + ++tbuffer->i; + } else { + token = dmnsn_yylex_impl(lvalp, llocp, filename, yyscanner); + } + + return token; +} + int dmnsn_yylex(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, const char *filename, dmnsn_symbol_table *symtable, void *yyscanner) { /* * So... this is kind of ugly. POV-Ray's language directives are not parsable - * by reasonable bison grammar, since some require skipping arbitrary amounts - * of tokens, or repeatedly parsing tokens. Instead, they are implemented - * transparently by the lexer. The lexing function calls a separate parser - * to handle language directives as they arrise, and returns only non- - * directive tokens. + * by any reasonable bison grammar, since some require skipping arbitrary + * amounts of tokens, or repeatedly parsing tokens. Instead, they are + * implemented transparently by the lexer. The lexing function calls a + * separate parser to handle language directives as they arrise, and returns + * only non-directive tokens. * * Ideally we'd use a push parser for the language directives, but bison * doesn't support GLR push parsers. Instead, we buffer all the appropriate * tokens and call a pull parser, then discard the buffer and continue. */ - dmnsn_token_buffer *tbuffer = dmnsn_yyget_extra(yyscanner); - - int token; while (1) { - if (tbuffer) { - /* Return buffered tokens */ - dmnsn_buffered_token buffered; - - if (tbuffer->i < dmnsn_array_size(tbuffer->buffered)) { - dmnsn_array_get(tbuffer->buffered, tbuffer->i, &buffered); - token = buffered.type; - *lvalp = buffered.lval; - *llocp = buffered.lloc; - ++tbuffer->i; + int token = dmnsn_yylex_wrapper(lvalp, llocp, filename, yyscanner); + dmnsn_token_buffer *tbuffer = dmnsn_yyget_extra(yyscanner); - if (tbuffer->type == DMNSN_T_LEX_VERBATIM && tbuffer->i == 1) { - /* Don't double-process the first token */ - return token; - } - } else { - dmnsn_yyset_extra(tbuffer->prev, yyscanner); - dmnsn_delete_token_buffer(tbuffer); - tbuffer = dmnsn_yyget_extra(yyscanner); - continue; - } - } else { - token = dmnsn_yylex_impl(lvalp, llocp, filename, yyscanner); + if (tbuffer && tbuffer->type == DMNSN_T_LEX_VERBATIM && tbuffer->i == 1) { + return token; } switch (token) { @@ -409,7 +506,19 @@ dmnsn_yylex(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, } dmnsn_yyset_extra(tb, yyscanner); - tbuffer = tb; + continue; + } + + case DMNSN_T_WHILE: + { + dmnsn_token_buffer *tb = dmnsn_while_buffer( + token, tbuffer, lvalp, llocp, filename, symtable, yyscanner + ); + if (!tb) { + return DMNSN_T_LEX_ERROR; + } + + dmnsn_yyset_extra(tb, yyscanner); continue; } diff --git a/tests/dimension/directives.pov b/tests/dimension/directives.pov index 94e613f..c6ac07b 100644 --- a/tests/dimension/directives.pov +++ b/tests/dimension/directives.pov @@ -30,13 +30,18 @@ Illegal #end -#if (#if (1 = 1) 0 #end = 0 & 0) - `Illegal -#else - sphere { - Center, R - pigment { - color Color green 1 +#declare Counter = 0; +#while (Counter < 2) + #if (#if (1 = 1) 0 #end = 0 & 0) + `Illegal + #else + sphere { + Center + <0, Counter, 0>, R + pigment { + color Color green 1 + } } - } + #end + + #declare Counter = Counter + 1; #end diff --git a/tests/dimension/directives.sh b/tests/dimension/directives.sh index 198e25b..9152d2f 100755 --- a/tests/dimension/directives.sh +++ b/tests/dimension/directives.sh @@ -29,21 +29,32 @@ directives_exp="$(echo -n \ #ifdef \( (identifier "Unused") \) (identifier "Illegal") #end - #if \( #if \( (integer "1") = (integer "1") \) (integer "0") #end = (integer "0") & (integer "0") \) - error (identifier "Illegal") - #else - sphere { - (identifier "Center") , (identifier "R") - pigment { - color (identifier "Color") green (integer "1") + #declare (identifier "Counter") = (integer "0") ; + #while \( (identifier "Counter") < (integer "2") \) + #if \( #if \( (integer "1") = (integer "1") \) (integer "0") #end = (integer "0") & (integer "0") \) + error (identifier "Illegal") + #else + sphere { + (identifier "Center") + < (integer "0") , (identifier "Counter") , (integer "0") > , (identifier "R") + pigment { + color (identifier "Color") green (integer "1") + } } - } + #end + #declare (identifier "Counter") = (identifier "Counter") + (integer "1") ; #end)' \ | tr '\n' ' ' | sed -r 's/[[:space:]]+/ /g') $(echo -n \ '((sphere (vector (integer 0) (integer 0) (integer 0) (integer 0) (integer 0)) (integer 1) + (object-modifiers + (texture + (pigment (color (integer 1) (integer 1) (integer 1) + (integer 0) (integer 0)))))) + (sphere + (vector (integer 0) (integer 1) (integer 0) (integer 0) (integer 0)) + (integer 1) (object-modifiers (texture (pigment (color (integer 1) (integer 1) (integer 1) -- cgit v1.2.3