summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dimension/common.terminals2
-rw-r--r--dimension/lexer.l1
-rw-r--r--dimension/parse.c2
-rw-r--r--dimension/tokenize.c191
-rw-r--r--tests/dimension/directives.pov21
-rwxr-xr-xtests/dimension/directives.sh27
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,15 +29,19 @@ 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 \
@@ -47,6 +51,13 @@ $(echo -n \
(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)
(integer 0) (integer 0)))))))' \
| tr '\n' ' ' | sed -r 's/[[:space:]]+/ /g')"