diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | dimension/Makefile.am | 33 | ||||
-rw-r--r-- | dimension/common.declarations | 31 | ||||
-rw-r--r-- | dimension/common.nonterminals | 75 | ||||
-rw-r--r-- | dimension/common.prologue | 145 | ||||
-rw-r--r-- | dimension/common.rules | 506 | ||||
-rw-r--r-- | dimension/common.terminals (renamed from dimension/grammar.terminals) | 0 | ||||
-rw-r--r-- | dimension/directives.declarations | 26 | ||||
-rw-r--r-- | dimension/directives.epilogue | 0 | ||||
-rw-r--r-- | dimension/directives.nonterminals | 20 | ||||
-rw-r--r-- | dimension/directives.prologue | 34 | ||||
-rw-r--r-- | dimension/directives.rules | 35 | ||||
-rw-r--r-- | dimension/grammar.declarations | 11 | ||||
-rw-r--r-- | dimension/grammar.epilogue | 1 | ||||
-rw-r--r-- | dimension/grammar.nonterminals | 58 | ||||
-rw-r--r-- | dimension/grammar.prologue | 127 | ||||
-rw-r--r-- | dimension/grammar.rules | 524 | ||||
-rw-r--r-- | dimension/lexer.l | 1 | ||||
-rw-r--r-- | dimension/tokenize.c | 207 | ||||
-rw-r--r-- | dimension/tokenize.h | 13 |
20 files changed, 1119 insertions, 731 deletions
@@ -40,6 +40,9 @@ Makefile.in # Files created by `make' /dimension/dimension +/dimension/directives.c +/dimension/directives.h +/dimension/directives.y /dimension/grammar.c /dimension/grammar.h /dimension/grammar.y diff --git a/dimension/Makefile.am b/dimension/Makefile.am index 1ee125c..6335ce7 100644 --- a/dimension/Makefile.am +++ b/dimension/Makefile.am @@ -22,25 +22,42 @@ INCLUDES = -I$(top_srcdir)/libdimension bin_PROGRAMS = dimension AM_YFLAGS = -d -BUILT_SOURCES = grammar.h -EXTRA_DIST = grammar.prologue \ +BUILT_SOURCES = directives.h grammar.h +EXTRA_DIST = common.prologue \ + common.declarations \ + common.terminals \ + common.nonterminals \ + common.rules \ + directives.prologue \ + directives.declarations \ + directives.nonterminals \ + directives.rules \ + grammar.prologue \ grammar.declarations \ - grammar.terminals \ grammar.nonterminals \ grammar.rules \ grammar.epilogue -grammar.y: grammar.prologue grammar.declarations grammar.terminals grammar.nonterminals grammar.rules grammar.epilogue +grammar.y: grammar.prologue grammar.declarations grammar.nonterminals grammar.rules grammar.epilogue common.prologue common.declarations common.terminals common.nonterminals common.rules echo "%{" >$@ - cat grammar.prologue >>$@ + cat {common,grammar}.prologue >>$@ echo "%}" >>$@ - cat grammar.{declarations,terminals,nonterminals} >>$@ + cat common.terminals {grammar,common}.{declarations,nonterminals} >>$@ echo "%%" >>$@ - cat grammar.rules >>$@ + cat grammar.rules common.rules >>$@ echo "%%" >>$@ cat grammar.epilogue >>$@ -dimension_SOURCES = grammar.y \ +directives.y: directives.prologue directives.declarations directives.nonterminals directives.rules common.prologue common.declarations common.terminals common.nonterminals common.rules + echo "%{" >$@ + cat {common,directives}.prologue >>$@ + echo "%}" >>$@ + cat common.terminals {directives,common}.{declarations,nonterminals} >>$@ + echo "%%" >>$@ + cat directives.rules common.rules >>$@ + +dimension_SOURCES = directives.y \ + grammar.y \ lexer.l \ main.c \ parse.c \ diff --git a/dimension/common.declarations b/dimension/common.declarations new file mode 100644 index 0000000..7e31f5b --- /dev/null +++ b/dimension/common.declarations @@ -0,0 +1,31 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes <tavianator@gmail.com> * + * * + * This file is part of Dimension. * + * * + * Dimension is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 3 of the License, or (at * + * your option) any later version. * + * * + * Dimension is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +%define api.pure +%locations +%error-verbose + +%glr-parser + +%lex-param {const char *filename} +%lex-param {dmnsn_symbol_table *symtable} +%lex-param {void *yyscanner} + +%destructor { free($$); } <value> +%destructor { dmnsn_delete_astnode($$); } <astnode> diff --git a/dimension/common.nonterminals b/dimension/common.nonterminals new file mode 100644 index 0000000..48e7335 --- /dev/null +++ b/dimension/common.nonterminals @@ -0,0 +1,75 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes <tavianator@gmail.com> * + * * + * This file is part of Dimension. * + * * + * Dimension is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 3 of the License, or (at * + * your option) any later version. * + * * + * Dimension is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +/* Identifiers */ +%type <astnode> IDENTIFIER + +/* Transformations */ +%type <astnode> TRANSFORMATION + +/* The camera */ +%type <astnode> CAMERA +%type <astnode> CAMERA_ITEMS +%type <astnode> CAMERA_ITEM +%type <astnode> CAMERA_TYPE +%type <astnode> CAMERA_VECTOR +%type <astnode> CAMERA_MODIFIER + +/* Objects */ +%type <astnode> OBJECT +%type <astnode> FINITE_SOLID_OBJECT +%type <astnode> BOX +%type <astnode> LIGHT_SOURCE +%type <astnode> SPHERE + +/* Object modifiers */ +%type <astnode> OBJECT_MODIFIERS +%type <astnode> OBJECT_MODIFIER + +/* Textures */ +%type <astnode> TEXTURE +%type <astnode> TEXTURE_ITEMS + +/* Pigments */ +%type <astnode> PIGMENT +%type <astnode> PIGMENT_TYPE + +/* Finishes */ +%type <astnode> FINISH +%type <astnode> FINISH_ITEMS +%type <astnode> REFLECTION +%type <astnode> REFLECTION_ITEMS + +/* Floats */ +%type <astnode> FLOAT +%type <astnode> FLOAT_LITERAL + +/* Vectors */ +%type <astnode> VECTOR +%type <astnode> VECTOR_LITERAL + +/* Generalized arithmetic expressions */ +%type <astnode> ARITH_EXPR + +/* Colors */ +%type <astnode> COLOR +%type <astnode> COLOR_BODY +%type <astnode> COLOR_VECTOR +%type <astnode> COLOR_KEYWORD_GROUP +%type <astnode> COLOR_KEYWORD_GROUP_INIT diff --git a/dimension/common.prologue b/dimension/common.prologue new file mode 100644 index 0000000..11ab99c --- /dev/null +++ b/dimension/common.prologue @@ -0,0 +1,145 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes <tavianator@gmail.com> * + * * + * This file is part of Dimension. * + * * + * Dimension is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 3 of the License, or (at * + * your option) any later version. * + * * + * Dimension is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +#include "parse.h" +#include "tokenize.h" +#include "utility.h" +#include <stdlib.h> +#include <stdio.h> + +#define YYSTYPE dmnsn_parse_item +#define YYLTYPE dmnsn_parse_location + +#define YYLLOC_DEFAULT(Current, Rhs, N) \ + do { \ + if (N) { \ + (Current).first_filename = YYRHSLOC(Rhs, 1).first_filename; \ + (Current).first_line = YYRHSLOC(Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC(Rhs, 1).first_column; \ + (Current).last_filename = YYRHSLOC(Rhs, N).last_filename; \ + (Current).last_line = YYRHSLOC(Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC(Rhs, N).last_column; \ + } else { \ + (Current).first_filename = (Current).last_filename = \ + YYRHSLOC(Rhs, 0).last_filename; \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC(Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC(Rhs, 0).last_column; \ + } \ + } while (0) + +/* Create a new astnode, populating filename, line, and col */ + +static dmnsn_astnode +dmnsn_new_astnode(dmnsn_astnode_type type, YYLTYPE lloc) +{ + dmnsn_astnode astnode = { + .type = type, + .children = dmnsn_new_array(sizeof(dmnsn_astnode)), + .ptr = NULL, + .refcount = malloc(sizeof(unsigned int)), + .filename = lloc.first_filename, + .line = lloc.first_line, + .col = lloc.first_column + }; + + if (!astnode.refcount) { + dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't allocate reference count."); + } + *astnode.refcount = 1; + + return astnode; +} + +/* Semi-shallow copy */ +static void +dmnsn_copy_children(dmnsn_astnode dest, dmnsn_astnode src) +{ + unsigned int i; + for (i = 0; i < dmnsn_array_size(src.children); ++i) { + dmnsn_astnode node; + dmnsn_array_get(src.children, i, &node); + ++*node.refcount; + + if (i < dmnsn_array_size(dest.children)) { + dmnsn_astnode clobbered; + dmnsn_array_get(dest.children, i, &clobbered); + dmnsn_delete_astnode(clobbered); + } + + dmnsn_array_set(dest.children, i, &node); + } +} + +static dmnsn_astnode +dmnsn_new_astnode1(dmnsn_astnode_type type, YYLTYPE lloc, dmnsn_astnode n1) +{ + dmnsn_astnode astnode = dmnsn_new_astnode(type, lloc); + dmnsn_array_push(astnode.children, &n1); + return astnode; +} + +static dmnsn_astnode +dmnsn_new_astnode2(dmnsn_astnode_type type, YYLTYPE lloc, + dmnsn_astnode n1, dmnsn_astnode n2) +{ + dmnsn_astnode astnode = dmnsn_new_astnode(type, lloc); + dmnsn_array_push(astnode.children, &n1); + dmnsn_array_push(astnode.children, &n2); + return astnode; +} + +static dmnsn_astnode +dmnsn_new_astnode3(dmnsn_astnode_type type, YYLTYPE lloc, + dmnsn_astnode n1, dmnsn_astnode n2, dmnsn_astnode n3) +{ + dmnsn_astnode astnode = dmnsn_new_astnode(type, lloc); + dmnsn_array_push(astnode.children, &n1); + dmnsn_array_push(astnode.children, &n2); + dmnsn_array_push(astnode.children, &n3); + return astnode; +} + +static dmnsn_astnode +dmnsn_new_astnode4(dmnsn_astnode_type type, YYLTYPE lloc, + dmnsn_astnode n1, dmnsn_astnode n2, dmnsn_astnode n3, + dmnsn_astnode n4) +{ + dmnsn_astnode astnode = dmnsn_new_astnode(type, lloc); + dmnsn_array_push(astnode.children, &n1); + dmnsn_array_push(astnode.children, &n2); + dmnsn_array_push(astnode.children, &n3); + dmnsn_array_push(astnode.children, &n4); + return astnode; +} + +static dmnsn_astnode +dmnsn_new_astnode5(dmnsn_astnode_type type, YYLTYPE lloc, + dmnsn_astnode n1, dmnsn_astnode n2, dmnsn_astnode n3, + dmnsn_astnode n4, dmnsn_astnode n5) +{ + dmnsn_astnode astnode = dmnsn_new_astnode(type, lloc); + dmnsn_array_push(astnode.children, &n1); + dmnsn_array_push(astnode.children, &n2); + dmnsn_array_push(astnode.children, &n3); + dmnsn_array_push(astnode.children, &n4); + dmnsn_array_push(astnode.children, &n5); + return astnode; +} diff --git a/dimension/common.rules b/dimension/common.rules new file mode 100644 index 0000000..ef4b3dd --- /dev/null +++ b/dimension/common.rules @@ -0,0 +1,506 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes <tavianator@gmail.com> * + * * + * This file is part of Dimension. * + * * + * Dimension is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 3 of the License, or (at * + * your option) any later version. * + * * + * Dimension is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +/* Identifiers */ + +IDENTIFIER: "identifier" { + $$ = dmnsn_new_astnode(DMNSN_AST_IDENTIFIER, @$); + $$.ptr = $1; + } +; + +/* Transformations */ + +TRANSFORMATION: "rotate" VECTOR { + $$ = dmnsn_new_astnode1(DMNSN_AST_ROTATION, @$, $2); + } + | "scale" VECTOR { + $$ = dmnsn_new_astnode1(DMNSN_AST_SCALE, @$, $2); + } + | "translate" VECTOR { + $$ = dmnsn_new_astnode1(DMNSN_AST_TRANSLATION, @$, $2); + } +; + +/* Cameras */ + +CAMERA: "camera" "{" + CAMERA_ITEMS + "}" + { + $$ = $3; + } +; + +CAMERA_ITEMS: /* empty */ { + $$ = dmnsn_new_astnode(DMNSN_AST_CAMERA, @$); + } + | CAMERA_ITEMS CAMERA_ITEM { + $$ = $1; + dmnsn_array_push($$.children, &$2); + } +; + +CAMERA_ITEM: CAMERA_TYPE + | CAMERA_VECTOR + | CAMERA_MODIFIER +; + +CAMERA_TYPE: "perspective" { + $$ = dmnsn_new_astnode(DMNSN_AST_PERSPECTIVE, @$); + } +; + +CAMERA_VECTOR: "location" VECTOR { + $$ = dmnsn_new_astnode1(DMNSN_AST_LOCATION, @$, $2); + } + | "right" VECTOR { + $$ = dmnsn_new_astnode1(DMNSN_AST_RIGHT, @$, $2); + } + | "up" VECTOR { + $$ = dmnsn_new_astnode1(DMNSN_AST_UP, @$, $2); + } + | "sky" VECTOR { + $$ = dmnsn_new_astnode1(DMNSN_AST_SKY, @$, $2); + } + | "direction" VECTOR { + $$ = dmnsn_new_astnode1(DMNSN_AST_DIRECTION, @$, $2); + } +; + +CAMERA_MODIFIER: "angle" FLOAT { + $$ = dmnsn_new_astnode1(DMNSN_AST_ANGLE, @$, $2); + } + | "look_at" VECTOR { + $$ = dmnsn_new_astnode1(DMNSN_AST_LOOK_AT, @$, $2); + } + | TRANSFORMATION +; + +/* Objects */ + +OBJECT: FINITE_SOLID_OBJECT + | LIGHT_SOURCE +; + +FINITE_SOLID_OBJECT: BOX + | SPHERE +; + +BOX: "box" "{" + VECTOR "," VECTOR + OBJECT_MODIFIERS + "}" + { + $$ = dmnsn_new_astnode3(DMNSN_AST_BOX, @$, $3, $5, $6); + } +; + +LIGHT_SOURCE: "light_source" "{" + VECTOR "," COLOR + "}" + { + $$ = dmnsn_new_astnode2(DMNSN_AST_LIGHT_SOURCE, @$, $3, $5); + } +; + +SPHERE: "sphere" "{" + VECTOR "," FLOAT + OBJECT_MODIFIERS + "}" + { + $$ = dmnsn_new_astnode3(DMNSN_AST_SPHERE, @$, $3, $5, $6); + } +; + +/* Object modifiers */ + +OBJECT_MODIFIERS: /* empty */ { + $$ = dmnsn_new_astnode(DMNSN_AST_OBJECT_MODIFIERS, @$); + } + | OBJECT_MODIFIERS OBJECT_MODIFIER { + $$ = $1; + dmnsn_array_push($$.children, &$2); + } +; + +OBJECT_MODIFIER: TRANSFORMATION + | TEXTURE + | PIGMENT { + $$ = dmnsn_new_astnode1(DMNSN_AST_TEXTURE, @$, $1); + } +; + +/* Textures */ + +TEXTURE: "texture" "{" + TEXTURE_ITEMS + "}" + { $$ = $3; } +; + +TEXTURE_ITEMS: /* empty */ { + $$ = dmnsn_new_astnode(DMNSN_AST_TEXTURE, @$); + } + | TEXTURE_ITEMS PIGMENT { + $$ = $1; + dmnsn_array_push($$.children, &$2); + } + | TEXTURE_ITEMS FINISH { + $$ = $1; + dmnsn_array_push($$.children, &$2); + } +; + +/* Pigments */ + +PIGMENT: "pigment" "{" + PIGMENT_TYPE + "}" + { + $$ = dmnsn_new_astnode1(DMNSN_AST_PIGMENT, @$, $3); + } +; + +PIGMENT_TYPE: /* empty */ { + $$ = dmnsn_new_astnode(DMNSN_AST_NONE, @$); + } + | COLOR +; + +/* Finishes */ +FINISH: "finish" "{" + FINISH_ITEMS + "}" + { $$ = $3; } +; + +FINISH_ITEMS: /* empty */ { + $$ = dmnsn_new_astnode(DMNSN_AST_FINISH, @$); + } + | FINISH_ITEMS "ambient" COLOR { + dmnsn_astnode ambient = dmnsn_new_astnode1(DMNSN_AST_AMBIENT, + @2, $3); + $$ = $1; + dmnsn_array_push($$.children, &ambient); + } + | FINISH_ITEMS "diffuse" FLOAT { + dmnsn_astnode diffuse = dmnsn_new_astnode1(DMNSN_AST_DIFFUSE, + @2, $3); + $$ = $1; + dmnsn_array_push($$.children, &diffuse); + } + | FINISH_ITEMS "phong" FLOAT { + dmnsn_astnode phong = dmnsn_new_astnode1(DMNSN_AST_PHONG, @2, $3); + $$ = $1; + dmnsn_array_push($$.children, &phong); + } + | FINISH_ITEMS "phong_size" FLOAT { + dmnsn_astnode phong_size + = dmnsn_new_astnode1(DMNSN_AST_PHONG_SIZE, @2, $3); + $$ = $1; + dmnsn_array_push($$.children, &phong_size); + } + | FINISH_ITEMS REFLECTION { + $$ = $1; + dmnsn_array_push($$.children, &$2); + } +; + +REFLECTION: "reflection" "{" + COLOR + REFLECTION_ITEMS + "}" + { + ++*$3.refcount; + $$ = dmnsn_new_astnode3(DMNSN_AST_REFLECTION, @$, $3, $3, $4); + } + | "reflection" "{" + COLOR "," COLOR + REFLECTION_ITEMS + "}" + { + $$ = dmnsn_new_astnode3(DMNSN_AST_REFLECTION, @$, $3, $5, $6); + } +; + +REFLECTION_ITEMS: /* empty */ { + $$ = dmnsn_new_astnode(DMNSN_AST_REFLECTION_ITEMS, @$); + } + | REFLECTION_ITEMS "falloff" FLOAT { + dmnsn_astnode falloff + = dmnsn_new_astnode1(DMNSN_AST_FALLOFF, @2, $3); + $$ = $1; + dmnsn_array_push($$.children, &falloff); + } +; + +/* Floats */ + +FLOAT: ARITH_EXPR { + $$ = dmnsn_eval_scalar($1, symtable); + dmnsn_delete_astnode($1); + + if ($$.type == DMNSN_AST_NONE) { + dmnsn_delete_astnode($$); + YYERROR; + } + } +; + +FLOAT_LITERAL: "integer" { + $$ = dmnsn_new_astnode(DMNSN_AST_INTEGER, @$); + $$.ptr = malloc(sizeof(long)); + if (!$$.ptr) + dmnsn_error(DMNSN_SEVERITY_HIGH, + "Failed to allocate room for integer."); + + *(long *)$$.ptr = strtol($1, NULL, 0); + free($1); + } + | "float" { + $$ = dmnsn_new_astnode(DMNSN_AST_FLOAT, @$); + $$.ptr = malloc(sizeof(double)); + if (!$$.ptr) + dmnsn_error(DMNSN_SEVERITY_HIGH, + "Failed to allocate room for float."); + + *(double *)$$.ptr = strtod($1, NULL); + free($1); + } +; + +/* Vectors */ + +VECTOR: ARITH_EXPR { + $$ = dmnsn_eval_vector($1, symtable); + dmnsn_delete_astnode($1); + + if ($$.type == DMNSN_AST_NONE) { + dmnsn_delete_astnode($$); + YYERROR; + } + } +; + +VECTOR_LITERAL: "<" ARITH_EXPR "," ARITH_EXPR ">" { + $$ = dmnsn_new_astnode2(DMNSN_AST_VECTOR, @$, $2, $4); + } + | "<" ARITH_EXPR "," ARITH_EXPR "," ARITH_EXPR ">" { + $$ = dmnsn_new_astnode3(DMNSN_AST_VECTOR, @$, $2, $4, $6); + } + | "<" ARITH_EXPR "," ARITH_EXPR "," ARITH_EXPR "," + ARITH_EXPR ">" { + $$ = dmnsn_new_astnode4(DMNSN_AST_VECTOR, @$, $2, $4, $6, $8); + } + | "<" ARITH_EXPR "," ARITH_EXPR "," ARITH_EXPR "," + ARITH_EXPR "," ARITH_EXPR ">" { + $$ = dmnsn_new_astnode5(DMNSN_AST_VECTOR, @$, + $2, $4, $6, $8, $10); + } +; + +/* Generalized arithmetic expressions */ + +ARITH_EXPR: FLOAT_LITERAL + | VECTOR_LITERAL + | ARITH_EXPR "+" ARITH_EXPR { + $$ = dmnsn_new_astnode2(DMNSN_AST_ADD, @$, $1, $3); + } + | ARITH_EXPR "-" ARITH_EXPR { + $$ = dmnsn_new_astnode2(DMNSN_AST_SUB, @$, $1, $3); + } + | ARITH_EXPR "*" ARITH_EXPR { + $$ = dmnsn_new_astnode2(DMNSN_AST_MUL, @$, $1, $3); + } + | ARITH_EXPR "/" ARITH_EXPR { + $$ = dmnsn_new_astnode2(DMNSN_AST_DIV, @$, $1, $3); + } + | "+" ARITH_EXPR %prec DMNSN_T_NEGATE { $$ = $2; } + | "-" ARITH_EXPR %prec DMNSN_T_NEGATE { + $$ = dmnsn_new_astnode1(DMNSN_AST_NEGATE, @$, $2); + } + | ARITH_EXPR "." "x" { + $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_X, @$, $1); + } + | ARITH_EXPR "." "u" { + $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_X, @$, $1); + } + | ARITH_EXPR "." "red" { + $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_X, @$, $1); + } + | ARITH_EXPR "." "y" { + $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_Y, @$, $1); + } + | ARITH_EXPR "." "v" { + $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_Y, @$, $1); + } + | ARITH_EXPR "." "green" { + $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_Y, @$, $1); + } + | ARITH_EXPR "." "z" { + $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_Z, @$, $1); + } + | ARITH_EXPR "." "blue" { + $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_Z, @$, $1); + } + | ARITH_EXPR "." "t" { + $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_T, @$, $1); + } + | ARITH_EXPR "." "filter" { + $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_T, @$, $1); + } + | ARITH_EXPR "." "transmit" { + $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_TRANSMIT, @$, $1); + } + | "(" ARITH_EXPR ")" { $$ = $2; } + | IDENTIFIER +; + +/* Colors */ + +COLOR: COLOR_BODY { + $$ = $1; + $$.type = DMNSN_AST_COLOR; + } + | "color" COLOR_BODY { + $$ = $2; + $$.type = DMNSN_AST_COLOR; + } +; + +COLOR_BODY: COLOR_VECTOR %dprec 1 + | COLOR_KEYWORD_GROUP %dprec 2 +; + +COLOR_VECTOR: "rgb" VECTOR { + dmnsn_astnode f, t; + dmnsn_array_get($2.children, 3, &f); + dmnsn_array_get($2.children, 4, &t); + dmnsn_delete_astnode(f); + dmnsn_delete_astnode(t); + + dmnsn_array_resize($2.children, 3); + + $$ = dmnsn_eval_vector($2, symtable); + dmnsn_delete_astnode($2); + } + | "rgbf" VECTOR { + dmnsn_astnode t; + dmnsn_array_get($2.children, 4, &t); + dmnsn_delete_astnode(t); + + dmnsn_array_resize($2.children, 4); + + $$ = dmnsn_eval_vector($2, symtable); + dmnsn_delete_astnode($2); + } + | "rgbt" VECTOR { + /* Chop off the fifth component */ + dmnsn_astnode t; + dmnsn_array_get($2.children, 4, &t); + dmnsn_delete_astnode(t); + + dmnsn_array_resize($2.children, 4); + + $$ = dmnsn_eval_vector($2, symtable); + dmnsn_delete_astnode($2); + + /* Swap the transmit and filter components */ + dmnsn_astnode temp; + dmnsn_array_get($$.children, 4, &temp); + dmnsn_array_set($$.children, 4, dmnsn_array_at($$.children, 3)); + dmnsn_array_set($$.children, 3, &temp); + } + | "rgbft" VECTOR { $$ = $2; } + | VECTOR +; + +COLOR_KEYWORD_GROUP: COLOR_KEYWORD_GROUP_INIT COLOR_KEYWORD_ITEM + | COLOR_KEYWORD_GROUP COLOR_KEYWORD_ITEM +; + +COLOR_KEYWORD_GROUP_INIT: /* empty */ { + dmnsn_astnode zero = + dmnsn_new_astnode(DMNSN_AST_INTEGER, @$); + zero.ptr = malloc(sizeof(long)); + if (!zero.ptr) + dmnsn_error(DMNSN_SEVERITY_HIGH, + "Failed to allocate room for integer."); + *(long *)zero.ptr = 0; + + $$ = dmnsn_eval_vector(zero, symtable); + dmnsn_delete_astnode(zero); + } +; + +COLOR_KEYWORD_ITEM: "identifier" { + dmnsn_astnode *symbol = dmnsn_find_symbol(symtable, $1); + if (!symbol) { + dmnsn_diagnostic(@1.first_filename, @1.first_line, + @1.first_column, + "unbound identifier '%s'", $1); + free($1); + YYERROR; + } else if (symbol->type != DMNSN_AST_COLOR) { + dmnsn_astnode eval = dmnsn_eval_vector(*symbol, symtable); + if (eval.type == DMNSN_AST_NONE) { + free($1); + YYERROR; + } + + dmnsn_copy_children($<astnode>0, eval); + dmnsn_delete_astnode(eval); + } else { + dmnsn_copy_children($<astnode>0, *symbol); + } + + free($1); + } + | "red" FLOAT { + dmnsn_astnode old; + dmnsn_array_get($<astnode>0.children, 0, &old); + dmnsn_array_set($<astnode>0.children, 0, &$2); + dmnsn_delete_astnode(old); + } + | "green" FLOAT { + dmnsn_astnode old; + dmnsn_array_get($<astnode>0.children, 1, &old); + dmnsn_array_set($<astnode>0.children, 1, &$2); + dmnsn_delete_astnode(old); + } + | "blue" FLOAT { + dmnsn_astnode old; + dmnsn_array_get($<astnode>0.children, 2, &old); + dmnsn_array_set($<astnode>0.children, 2, &$2); + dmnsn_delete_astnode(old); + } + | "filter" FLOAT { + dmnsn_astnode old; + dmnsn_array_get($<astnode>0.children, 3, &old); + dmnsn_array_set($<astnode>0.children, 3, &$2); + dmnsn_delete_astnode(old); + } + | "transmit" FLOAT { + dmnsn_astnode old; + dmnsn_array_get($<astnode>0.children, 4, &old); + dmnsn_array_set($<astnode>0.children, 4, &$2); + dmnsn_delete_astnode(old); + } +; diff --git a/dimension/grammar.terminals b/dimension/common.terminals index 37d3c16..37d3c16 100644 --- a/dimension/grammar.terminals +++ b/dimension/common.terminals diff --git a/dimension/directives.declarations b/dimension/directives.declarations new file mode 100644 index 0000000..0543b3d --- /dev/null +++ b/dimension/directives.declarations @@ -0,0 +1,26 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes <tavianator@gmail.com> * + * * + * This file is part of Dimension. * + * * + * Dimension is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 3 of the License, or (at * + * your option) any later version. * + * * + * Dimension is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +%name-prefix "dmnsn_ld_yy" + +%expect 9 + +%parse-param {const char *filename} +%parse-param {void *yyscanner} +%parse-param {dmnsn_symbol_table *symtable} diff --git a/dimension/directives.epilogue b/dimension/directives.epilogue new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dimension/directives.epilogue diff --git a/dimension/directives.nonterminals b/dimension/directives.nonterminals new file mode 100644 index 0000000..ac23a0a --- /dev/null +++ b/dimension/directives.nonterminals @@ -0,0 +1,20 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes <tavianator@gmail.com> * + * * + * This file is part of Dimension. * + * * + * Dimension is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 3 of the License, or (at * + * your option) any later version. * + * * + * Dimension is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +%type <astnode> RVALUE diff --git a/dimension/directives.prologue b/dimension/directives.prologue new file mode 100644 index 0000000..9bbcafc --- /dev/null +++ b/dimension/directives.prologue @@ -0,0 +1,34 @@ +/************************************************************************* + * Copyright (C) 2010 Tavian Barnes <tavianator@gmail.com> * + * * + * This file is part of Dimension. * + * * + * Dimension is free software; you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; either version 3 of the License, or (at * + * your option) any later version. * + * * + * Dimension is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +void +yyerror(YYLTYPE *locp, const char *filename, void *yyscanner, + dmnsn_symbol_table *symtable, const char *str) +{ + dmnsn_diagnostic(locp->first_filename, locp->first_line, locp->first_column, + "%s", str); +} + +static int +dmnsn_ld_yylex(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, + const char *filename, dmnsn_symbol_table *symtable, + void *yyscanner) +{ + return dmnsn_yylex(lvalp, llocp, filename, symtable, yyscanner); +} diff --git a/dimension/directives.rules b/dimension/directives.rules new file mode 100644 index 0000000..916039b --- /dev/null +++ b/dimension/directives.rules @@ -0,0 +1,35 @@ +/* + * Start symbol + */ + +LANGUAGE_DIRECTIVE: "#declare" "identifier" "=" RVALUE { + dmnsn_declare_symbol(symtable, $2, $4); + free($2); + dmnsn_delete_astnode($4); + } + | "#local" "identifier" "=" RVALUE { + dmnsn_local_symbol(symtable, $2, $4); + free($2); + dmnsn_delete_astnode($4); + } + | "#undef" "identifier" { + dmnsn_undef_symbol(symtable, $2); + free($2); + } + +RVALUE: 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 + | CAMERA + | TRANSFORMATION diff --git a/dimension/grammar.declarations b/dimension/grammar.declarations index c951758..247e903 100644 --- a/dimension/grammar.declarations +++ b/dimension/grammar.declarations @@ -17,22 +17,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * *************************************************************************/ -%define api.pure -%locations -%error-verbose %token-table %name-prefix "dmnsn_yy" -%expect 10 -%glr-parser +%expect 7 %parse-param {const char *filename} %parse-param {void *yyscanner} %parse-param {dmnsn_astree *astree} %parse-param {dmnsn_symbol_table *symtable} -%lex-param {const char *filename} -%lex-param {void *yyscanner} - -%destructor { free($$); } <value> -%destructor { dmnsn_delete_astnode($$); } <astnode> diff --git a/dimension/grammar.epilogue b/dimension/grammar.epilogue index 345fa0f..b62165e 100644 --- a/dimension/grammar.epilogue +++ b/dimension/grammar.epilogue @@ -34,6 +34,7 @@ dmnsn_parse(FILE *file, dmnsn_symbol_table *symtable) dmnsn_yylex_init(&scanner); dmnsn_yyset_in(file, scanner); + dmnsn_yyset_extra(NULL, scanner); if (yyparse(filename, scanner, astree, symtable) != 0) { dmnsn_delete_astree(astree); diff --git a/dimension/grammar.nonterminals b/dimension/grammar.nonterminals index ae752c3..4b608c1 100644 --- a/dimension/grammar.nonterminals +++ b/dimension/grammar.nonterminals @@ -20,64 +20,6 @@ /* Top-level items */ %type <astnode> SCENE_ITEM -/* Language directives */ -%type <astnode> RVALUE -%type <astnode> IDENTIFIER - -/* Transformations */ -%type <astnode> TRANSFORMATION - -/* The camera */ -%type <astnode> CAMERA -%type <astnode> CAMERA_ITEMS -%type <astnode> CAMERA_ITEM -%type <astnode> CAMERA_TYPE -%type <astnode> CAMERA_VECTOR -%type <astnode> CAMERA_MODIFIER - /* Atmospheric effects */ %type <astnode> ATMOSPHERIC_EFFECT %type <astnode> BACKGROUND - -/* Objects */ -%type <astnode> OBJECT -%type <astnode> FINITE_SOLID_OBJECT -%type <astnode> BOX -%type <astnode> LIGHT_SOURCE -%type <astnode> SPHERE - -/* Object modifiers */ -%type <astnode> OBJECT_MODIFIERS -%type <astnode> OBJECT_MODIFIER - -/* Textures */ -%type <astnode> TEXTURE -%type <astnode> TEXTURE_ITEMS - -/* Pigments */ -%type <astnode> PIGMENT -%type <astnode> PIGMENT_TYPE - -/* Finishes */ -%type <astnode> FINISH -%type <astnode> FINISH_ITEMS -%type <astnode> REFLECTION -%type <astnode> REFLECTION_ITEMS - -/* Floats */ -%type <astnode> FLOAT -%type <astnode> FLOAT_LITERAL - -/* Vectors */ -%type <astnode> VECTOR -%type <astnode> VECTOR_LITERAL - -/* Generalized arithmetic expressions */ -%type <astnode> ARITH_EXPR - -/* Colors */ -%type <astnode> COLOR -%type <astnode> COLOR_BODY -%type <astnode> COLOR_VECTOR -%type <astnode> COLOR_KEYWORD_GROUP -%type <astnode> COLOR_KEYWORD_GROUP_INIT diff --git a/dimension/grammar.prologue b/dimension/grammar.prologue index 7b0db1f..598f12f 100644 --- a/dimension/grammar.prologue +++ b/dimension/grammar.prologue @@ -17,133 +17,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * *************************************************************************/ -#include "parse.h" -#include "tokenize.h" -#include "utility.h" -#include <stdlib.h> -#include <stdio.h> - -#define YYSTYPE dmnsn_parse_item -#define YYLTYPE dmnsn_parse_location - -#define YYLLOC_DEFAULT(Current, Rhs, N) \ - do { \ - if (N) { \ - (Current).first_filename = YYRHSLOC(Rhs, 1).first_filename; \ - (Current).first_line = YYRHSLOC(Rhs, 1).first_line; \ - (Current).first_column = YYRHSLOC(Rhs, 1).first_column; \ - (Current).last_filename = YYRHSLOC(Rhs, N).last_filename; \ - (Current).last_line = YYRHSLOC(Rhs, N).last_line; \ - (Current).last_column = YYRHSLOC(Rhs, N).last_column; \ - } else { \ - (Current).first_filename = (Current).last_filename = \ - YYRHSLOC(Rhs, 0).last_filename; \ - (Current).first_line = (Current).last_line = \ - YYRHSLOC(Rhs, 0).last_line; \ - (Current).first_column = (Current).last_column = \ - YYRHSLOC(Rhs, 0).last_column; \ - } \ - } while (0) - -/* Create a new astnode, populating filename, line, and col */ - -static dmnsn_astnode -dmnsn_new_astnode(dmnsn_astnode_type type, YYLTYPE lloc) -{ - dmnsn_astnode astnode = { - .type = type, - .children = dmnsn_new_array(sizeof(dmnsn_astnode)), - .ptr = NULL, - .refcount = malloc(sizeof(unsigned int)), - .filename = lloc.first_filename, - .line = lloc.first_line, - .col = lloc.first_column - }; - - if (!astnode.refcount) { - dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't allocate reference count."); - } - *astnode.refcount = 1; - - return astnode; -} - -/* Semi-shallow copy */ -static void -dmnsn_copy_children(dmnsn_astnode dest, dmnsn_astnode src) -{ - unsigned int i; - for (i = 0; i < dmnsn_array_size(src.children); ++i) { - dmnsn_astnode node; - dmnsn_array_get(src.children, i, &node); - ++*node.refcount; - - if (i < dmnsn_array_size(dest.children)) { - dmnsn_astnode clobbered; - dmnsn_array_get(dest.children, i, &clobbered); - dmnsn_delete_astnode(clobbered); - } - - dmnsn_array_set(dest.children, i, &node); - } -} - -static dmnsn_astnode -dmnsn_new_astnode1(dmnsn_astnode_type type, YYLTYPE lloc, dmnsn_astnode n1) -{ - dmnsn_astnode astnode = dmnsn_new_astnode(type, lloc); - dmnsn_array_push(astnode.children, &n1); - return astnode; -} - -static dmnsn_astnode -dmnsn_new_astnode2(dmnsn_astnode_type type, YYLTYPE lloc, - dmnsn_astnode n1, dmnsn_astnode n2) -{ - dmnsn_astnode astnode = dmnsn_new_astnode(type, lloc); - dmnsn_array_push(astnode.children, &n1); - dmnsn_array_push(astnode.children, &n2); - return astnode; -} - -static dmnsn_astnode -dmnsn_new_astnode3(dmnsn_astnode_type type, YYLTYPE lloc, - dmnsn_astnode n1, dmnsn_astnode n2, dmnsn_astnode n3) -{ - dmnsn_astnode astnode = dmnsn_new_astnode(type, lloc); - dmnsn_array_push(astnode.children, &n1); - dmnsn_array_push(astnode.children, &n2); - dmnsn_array_push(astnode.children, &n3); - return astnode; -} - -static dmnsn_astnode -dmnsn_new_astnode4(dmnsn_astnode_type type, YYLTYPE lloc, - dmnsn_astnode n1, dmnsn_astnode n2, dmnsn_astnode n3, - dmnsn_astnode n4) -{ - dmnsn_astnode astnode = dmnsn_new_astnode(type, lloc); - dmnsn_array_push(astnode.children, &n1); - dmnsn_array_push(astnode.children, &n2); - dmnsn_array_push(astnode.children, &n3); - dmnsn_array_push(astnode.children, &n4); - return astnode; -} - -static dmnsn_astnode -dmnsn_new_astnode5(dmnsn_astnode_type type, YYLTYPE lloc, - dmnsn_astnode n1, dmnsn_astnode n2, dmnsn_astnode n3, - dmnsn_astnode n4, dmnsn_astnode n5) -{ - dmnsn_astnode astnode = dmnsn_new_astnode(type, lloc); - dmnsn_array_push(astnode.children, &n1); - dmnsn_array_push(astnode.children, &n2); - dmnsn_array_push(astnode.children, &n3); - dmnsn_array_push(astnode.children, &n4); - dmnsn_array_push(astnode.children, &n5); - return astnode; -} - void yyerror(YYLTYPE *locp, const char *filename, void *yyscanner, dmnsn_astree *astree, dmnsn_symbol_table *symtable, const char *str) diff --git a/dimension/grammar.rules b/dimension/grammar.rules index 2558485..548e769 100644 --- a/dimension/grammar.rules +++ b/dimension/grammar.rules @@ -17,128 +17,22 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * *************************************************************************/ -/* Start symbol */ +/* + * Start symbol + */ SCENE: /* empty */ - | SCENE LANGUAGE_DIRECTIVE | SCENE SCENE_ITEM { dmnsn_array_push(astree, &$2); } ; -/* Language directives */ - -LANGUAGE_DIRECTIVE: DECLARATION -; - -DECLARATION: "#declare" "identifier" "=" RVALUE { - dmnsn_declare_symbol(symtable, $2, $4); - free($2); - dmnsn_delete_astnode($4); - } - | "#local" "identifier" "=" RVALUE { - dmnsn_local_symbol(symtable, $2, $4); - free($2); - dmnsn_delete_astnode($4); - } - | "#undef" "identifier" { - dmnsn_undef_symbol(symtable, $2); - free($2); - } -; - -RVALUE: ARITH_EXPR ";" %dprec 2 { - $$ = dmnsn_eval($1, symtable); - dmnsn_delete_astnode($1); - - if ($$.type == DMNSN_AST_NONE) { - dmnsn_delete_astnode($$); - YYERROR; - } - } - | COLOR ";" %dprec 1 -; - -IDENTIFIER: "identifier" { - $$ = dmnsn_new_astnode(DMNSN_AST_IDENTIFIER, @$); - $$.ptr = $1; - } -; - /* Top-level scene item */ SCENE_ITEM: ATMOSPHERIC_EFFECT | CAMERA | OBJECT ; -/* Transformations */ - -TRANSFORMATION: "rotate" VECTOR { - $$ = dmnsn_new_astnode1(DMNSN_AST_ROTATION, @$, $2); - } - | "scale" VECTOR { - $$ = dmnsn_new_astnode1(DMNSN_AST_SCALE, @$, $2); - } - | "translate" VECTOR { - $$ = dmnsn_new_astnode1(DMNSN_AST_TRANSLATION, @$, $2); - } -; - -/* Cameras */ - -CAMERA: "camera" "{" - CAMERA_ITEMS - "}" - { - $$ = $3; - } -; - -CAMERA_ITEMS: /* empty */ { - $$ = dmnsn_new_astnode(DMNSN_AST_CAMERA, @$); - } - | CAMERA_ITEMS CAMERA_ITEM { - $$ = $1; - dmnsn_array_push($$.children, &$2); - } -; - -CAMERA_ITEM: CAMERA_TYPE - | CAMERA_VECTOR - | CAMERA_MODIFIER -; - -CAMERA_TYPE: "perspective" { - $$ = dmnsn_new_astnode(DMNSN_AST_PERSPECTIVE, @$); - } -; - -CAMERA_VECTOR: "location" VECTOR { - $$ = dmnsn_new_astnode1(DMNSN_AST_LOCATION, @$, $2); - } - | "right" VECTOR { - $$ = dmnsn_new_astnode1(DMNSN_AST_RIGHT, @$, $2); - } - | "up" VECTOR { - $$ = dmnsn_new_astnode1(DMNSN_AST_UP, @$, $2); - } - | "sky" VECTOR { - $$ = dmnsn_new_astnode1(DMNSN_AST_SKY, @$, $2); - } - | "direction" VECTOR { - $$ = dmnsn_new_astnode1(DMNSN_AST_DIRECTION, @$, $2); - } -; - -CAMERA_MODIFIER: "angle" FLOAT { - $$ = dmnsn_new_astnode1(DMNSN_AST_ANGLE, @$, $2); - } - | "look_at" VECTOR { - $$ = dmnsn_new_astnode1(DMNSN_AST_LOOK_AT, @$, $2); - } - | TRANSFORMATION -; - /* Atmospheric effects */ ATMOSPHERIC_EFFECT: BACKGROUND @@ -148,415 +42,3 @@ BACKGROUND: "background" "{" COLOR "}" { $$ = dmnsn_new_astnode1(DMNSN_AST_BACKGROUND, @$, $3); } ; - -/* Objects */ - -OBJECT: FINITE_SOLID_OBJECT - | LIGHT_SOURCE -; - -FINITE_SOLID_OBJECT: BOX - | SPHERE -; - -BOX: "box" "{" - VECTOR "," VECTOR - OBJECT_MODIFIERS - "}" - { - $$ = dmnsn_new_astnode3(DMNSN_AST_BOX, @$, $3, $5, $6); - } -; - -LIGHT_SOURCE: "light_source" "{" - VECTOR "," COLOR - "}" - { - $$ = dmnsn_new_astnode2(DMNSN_AST_LIGHT_SOURCE, @$, $3, $5); - } -; - -SPHERE: "sphere" "{" - VECTOR "," FLOAT - OBJECT_MODIFIERS - "}" - { - $$ = dmnsn_new_astnode3(DMNSN_AST_SPHERE, @$, $3, $5, $6); - } -; - -/* Object modifiers */ - -OBJECT_MODIFIERS: /* empty */ { - $$ = dmnsn_new_astnode(DMNSN_AST_OBJECT_MODIFIERS, @$); - } - | OBJECT_MODIFIERS OBJECT_MODIFIER { - $$ = $1; - dmnsn_array_push($$.children, &$2); - } -; - -OBJECT_MODIFIER: TRANSFORMATION - | TEXTURE - | PIGMENT { - $$ = dmnsn_new_astnode1(DMNSN_AST_TEXTURE, @$, $1); - } -; - -/* Textures */ - -TEXTURE: "texture" "{" - TEXTURE_ITEMS - "}" - { $$ = $3; } -; - -TEXTURE_ITEMS: /* empty */ { - $$ = dmnsn_new_astnode(DMNSN_AST_TEXTURE, @$); - } - | TEXTURE_ITEMS PIGMENT { - $$ = $1; - dmnsn_array_push($$.children, &$2); - } - | TEXTURE_ITEMS FINISH { - $$ = $1; - dmnsn_array_push($$.children, &$2); - } -; - -/* Pigments */ - -PIGMENT: "pigment" "{" - PIGMENT_TYPE - "}" - { - $$ = dmnsn_new_astnode1(DMNSN_AST_PIGMENT, @$, $3); - } -; - -PIGMENT_TYPE: /* empty */ { - $$ = dmnsn_new_astnode(DMNSN_AST_NONE, @$); - } - | COLOR -; - -/* Finishes */ -FINISH: "finish" "{" - FINISH_ITEMS - "}" - { $$ = $3; } -; - -FINISH_ITEMS: /* empty */ { - $$ = dmnsn_new_astnode(DMNSN_AST_FINISH, @$); - } - | FINISH_ITEMS "ambient" COLOR { - dmnsn_astnode ambient = dmnsn_new_astnode1(DMNSN_AST_AMBIENT, - @2, $3); - $$ = $1; - dmnsn_array_push($$.children, &ambient); - } - | FINISH_ITEMS "diffuse" FLOAT { - dmnsn_astnode diffuse = dmnsn_new_astnode1(DMNSN_AST_DIFFUSE, - @2, $3); - $$ = $1; - dmnsn_array_push($$.children, &diffuse); - } - | FINISH_ITEMS "phong" FLOAT { - dmnsn_astnode phong = dmnsn_new_astnode1(DMNSN_AST_PHONG, @2, $3); - $$ = $1; - dmnsn_array_push($$.children, &phong); - } - | FINISH_ITEMS "phong_size" FLOAT { - dmnsn_astnode phong_size - = dmnsn_new_astnode1(DMNSN_AST_PHONG_SIZE, @2, $3); - $$ = $1; - dmnsn_array_push($$.children, &phong_size); - } - | FINISH_ITEMS REFLECTION { - $$ = $1; - dmnsn_array_push($$.children, &$2); - } -; - -REFLECTION: "reflection" "{" - COLOR - REFLECTION_ITEMS - "}" - { - ++*$3.refcount; - $$ = dmnsn_new_astnode3(DMNSN_AST_REFLECTION, @$, $3, $3, $4); - } - | "reflection" "{" - COLOR "," COLOR - REFLECTION_ITEMS - "}" - { - $$ = dmnsn_new_astnode3(DMNSN_AST_REFLECTION, @$, $3, $5, $6); - } -; - -REFLECTION_ITEMS: /* empty */ { - $$ = dmnsn_new_astnode(DMNSN_AST_REFLECTION_ITEMS, @$); - } - | REFLECTION_ITEMS "falloff" FLOAT { - dmnsn_astnode falloff - = dmnsn_new_astnode1(DMNSN_AST_FALLOFF, @2, $3); - $$ = $1; - dmnsn_array_push($$.children, &falloff); - } -; - -/* Floats */ - -FLOAT: ARITH_EXPR { - $$ = dmnsn_eval_scalar($1, symtable); - dmnsn_delete_astnode($1); - - if ($$.type == DMNSN_AST_NONE) { - dmnsn_delete_astnode($$); - YYERROR; - } - } -; - -FLOAT_LITERAL: "integer" { - $$ = dmnsn_new_astnode(DMNSN_AST_INTEGER, @$); - $$.ptr = malloc(sizeof(long)); - if (!$$.ptr) - dmnsn_error(DMNSN_SEVERITY_HIGH, - "Failed to allocate room for integer."); - - *(long *)$$.ptr = strtol($1, NULL, 0); - free($1); - } - | "float" { - $$ = dmnsn_new_astnode(DMNSN_AST_FLOAT, @$); - $$.ptr = malloc(sizeof(double)); - if (!$$.ptr) - dmnsn_error(DMNSN_SEVERITY_HIGH, - "Failed to allocate room for float."); - - *(double *)$$.ptr = strtod($1, NULL); - free($1); - } -; - -/* Vectors */ - -VECTOR: ARITH_EXPR { - $$ = dmnsn_eval_vector($1, symtable); - dmnsn_delete_astnode($1); - - if ($$.type == DMNSN_AST_NONE) { - dmnsn_delete_astnode($$); - YYERROR; - } - } -; - -VECTOR_LITERAL: "<" ARITH_EXPR "," ARITH_EXPR ">" { - $$ = dmnsn_new_astnode2(DMNSN_AST_VECTOR, @$, $2, $4); - } - | "<" ARITH_EXPR "," ARITH_EXPR "," ARITH_EXPR ">" { - $$ = dmnsn_new_astnode3(DMNSN_AST_VECTOR, @$, $2, $4, $6); - } - | "<" ARITH_EXPR "," ARITH_EXPR "," ARITH_EXPR "," - ARITH_EXPR ">" { - $$ = dmnsn_new_astnode4(DMNSN_AST_VECTOR, @$, $2, $4, $6, $8); - } - | "<" ARITH_EXPR "," ARITH_EXPR "," ARITH_EXPR "," - ARITH_EXPR "," ARITH_EXPR ">" { - $$ = dmnsn_new_astnode5(DMNSN_AST_VECTOR, @$, - $2, $4, $6, $8, $10); - } -; - -/* Generalized arithmetic expressions */ - -ARITH_EXPR: FLOAT_LITERAL - | VECTOR_LITERAL - | ARITH_EXPR "+" ARITH_EXPR { - $$ = dmnsn_new_astnode2(DMNSN_AST_ADD, @$, $1, $3); - } - | ARITH_EXPR "-" ARITH_EXPR { - $$ = dmnsn_new_astnode2(DMNSN_AST_SUB, @$, $1, $3); - } - | ARITH_EXPR "*" ARITH_EXPR { - $$ = dmnsn_new_astnode2(DMNSN_AST_MUL, @$, $1, $3); - } - | ARITH_EXPR "/" ARITH_EXPR { - $$ = dmnsn_new_astnode2(DMNSN_AST_DIV, @$, $1, $3); - } - | "+" ARITH_EXPR %prec DMNSN_T_NEGATE { $$ = $2; } - | "-" ARITH_EXPR %prec DMNSN_T_NEGATE { - $$ = dmnsn_new_astnode1(DMNSN_AST_NEGATE, @$, $2); - } - | ARITH_EXPR "." "x" { - $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_X, @$, $1); - } - | ARITH_EXPR "." "u" { - $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_X, @$, $1); - } - | ARITH_EXPR "." "red" { - $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_X, @$, $1); - } - | ARITH_EXPR "." "y" { - $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_Y, @$, $1); - } - | ARITH_EXPR "." "v" { - $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_Y, @$, $1); - } - | ARITH_EXPR "." "green" { - $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_Y, @$, $1); - } - | ARITH_EXPR "." "z" { - $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_Z, @$, $1); - } - | ARITH_EXPR "." "blue" { - $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_Z, @$, $1); - } - | ARITH_EXPR "." "t" { - $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_T, @$, $1); - } - | ARITH_EXPR "." "filter" { - $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_T, @$, $1); - } - | ARITH_EXPR "." "transmit" { - $$ = dmnsn_new_astnode1(DMNSN_AST_DOT_TRANSMIT, @$, $1); - } - | "(" ARITH_EXPR ")" { $$ = $2; } - | IDENTIFIER -; - -/* Colors */ - -COLOR: COLOR_BODY { - $$ = $1; - $$.type = DMNSN_AST_COLOR; - } - | "color" COLOR_BODY { - $$ = $2; - $$.type = DMNSN_AST_COLOR; - } -; - -COLOR_BODY: COLOR_VECTOR %dprec 1 - | COLOR_KEYWORD_GROUP %dprec 2 -; - -COLOR_VECTOR: "rgb" VECTOR { - dmnsn_astnode f, t; - dmnsn_array_get($2.children, 3, &f); - dmnsn_array_get($2.children, 4, &t); - dmnsn_delete_astnode(f); - dmnsn_delete_astnode(t); - - dmnsn_array_resize($2.children, 3); - - $$ = dmnsn_eval_vector($2, symtable); - dmnsn_delete_astnode($2); - } - | "rgbf" VECTOR { - dmnsn_astnode t; - dmnsn_array_get($2.children, 4, &t); - dmnsn_delete_astnode(t); - - dmnsn_array_resize($2.children, 4); - - $$ = dmnsn_eval_vector($2, symtable); - dmnsn_delete_astnode($2); - } - | "rgbt" VECTOR { - /* Chop off the fifth component */ - dmnsn_astnode t; - dmnsn_array_get($2.children, 4, &t); - dmnsn_delete_astnode(t); - - dmnsn_array_resize($2.children, 4); - - $$ = dmnsn_eval_vector($2, symtable); - dmnsn_delete_astnode($2); - - /* Swap the transmit and filter components */ - dmnsn_astnode temp; - dmnsn_array_get($$.children, 4, &temp); - dmnsn_array_set($$.children, 4, dmnsn_array_at($$.children, 3)); - dmnsn_array_set($$.children, 3, &temp); - } - | "rgbft" VECTOR { $$ = $2; } - | VECTOR -; - -COLOR_KEYWORD_GROUP: COLOR_KEYWORD_GROUP_INIT COLOR_KEYWORD_ITEM - | COLOR_KEYWORD_GROUP COLOR_KEYWORD_ITEM -; - -COLOR_KEYWORD_GROUP_INIT: /* empty */ { - dmnsn_astnode zero = - dmnsn_new_astnode(DMNSN_AST_INTEGER, @$); - zero.ptr = malloc(sizeof(long)); - if (!zero.ptr) - dmnsn_error(DMNSN_SEVERITY_HIGH, - "Failed to allocate room for integer."); - *(long *)zero.ptr = 0; - - $$ = dmnsn_eval_vector(zero, symtable); - dmnsn_delete_astnode(zero); - } -; - -COLOR_KEYWORD_ITEM: "identifier" { - dmnsn_astnode *symbol = dmnsn_find_symbol(symtable, $1); - if (!symbol) { - dmnsn_diagnostic(@1.first_filename, @1.first_line, - @1.first_column, - "unbound identifier '%s'", $1); - free($1); - YYERROR; - } else if (symbol->type != DMNSN_AST_COLOR) { - dmnsn_astnode eval = dmnsn_eval_vector(*symbol, symtable); - if (eval.type == DMNSN_AST_NONE) { - free($1); - YYERROR; - } - - dmnsn_copy_children($<astnode>0, eval); - dmnsn_delete_astnode(eval); - } else { - dmnsn_copy_children($<astnode>0, *symbol); - } - - free($1); - } - | "red" FLOAT { - dmnsn_astnode old; - dmnsn_array_get($<astnode>0.children, 0, &old); - dmnsn_array_set($<astnode>0.children, 0, &$2); - dmnsn_delete_astnode(old); - } - | "green" FLOAT { - dmnsn_astnode old; - dmnsn_array_get($<astnode>0.children, 1, &old); - dmnsn_array_set($<astnode>0.children, 1, &$2); - dmnsn_delete_astnode(old); - } - | "blue" FLOAT { - dmnsn_astnode old; - dmnsn_array_get($<astnode>0.children, 2, &old); - dmnsn_array_set($<astnode>0.children, 2, &$2); - dmnsn_delete_astnode(old); - } - | "filter" FLOAT { - dmnsn_astnode old; - dmnsn_array_get($<astnode>0.children, 3, &old); - dmnsn_array_set($<astnode>0.children, 3, &$2); - dmnsn_delete_astnode(old); - } - | "transmit" FLOAT { - dmnsn_astnode old; - dmnsn_array_get($<astnode>0.children, 4, &old); - dmnsn_array_set($<astnode>0.children, 4, &$2); - dmnsn_delete_astnode(old); - } -; diff --git a/dimension/lexer.l b/dimension/lexer.l index e456e24..9510bcf 100644 --- a/dimension/lexer.l +++ b/dimension/lexer.l @@ -278,6 +278,7 @@ dmnsn_tokenize(FILE *file, const char *filename) yylex_init(&scanner); yyset_in(file, scanner); + yyset_extra(NULL, scanner); while ((token.type = dmnsn_yylex_impl(&item, &location, filename, scanner)) != 0) { diff --git a/dimension/tokenize.c b/dimension/tokenize.c index 74caace..4a9e9c6 100644 --- a/dimension/tokenize.c +++ b/dimension/tokenize.c @@ -18,13 +18,216 @@ *************************************************************************/ #include "tokenize.h" +#include "directives.h" +#include "utility.h" + +typedef struct dmnsn_buffered_token { + int type; + dmnsn_parse_item lval; + dmnsn_parse_location lloc; +} dmnsn_buffered_token; + +typedef struct dmnsn_yylex_extra { + int type; + /* Indicate that the first token should be returned as-is */ + #define DMNSN_T_LEX_VERBATIM DMNSN_T_EOF + + dmnsn_array *buffered; + unsigned int i; + + struct dmnsn_yylex_extra *prev; +} dmnsn_yylex_extra; + +dmnsn_yylex_extra * +dmnsn_new_yylex_extra(int type) +{ + dmnsn_yylex_extra *extra = malloc(sizeof(dmnsn_yylex_extra)); + if (!extra) { + dmnsn_error(DMNSN_SEVERITY_HIGH, "Failed to allocate token buffer."); + } + + extra->type = type; + extra->buffered = dmnsn_new_array(sizeof(dmnsn_buffered_token)); + extra->i = 0; + extra->prev = NULL; + return extra; +} + +void +dmnsn_delete_yylex_extra(dmnsn_yylex_extra *extra) +{ + if (extra) { + dmnsn_delete_array(extra->buffered); + free(extra); + } +} int dmnsn_yylex_impl(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); int dmnsn_yylex(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, - const char *filename, void *yyscanner) + const char *filename, dmnsn_symbol_table *symtable, void *yyscanner) { - return dmnsn_yylex_impl(lvalp, llocp, filename, 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. + * + * Ideally we'd use a push parser for the language directives, but bison + * doesn't support GLR push parsers. + */ + + dmnsn_yylex_extra *extra = dmnsn_yyget_extra(yyscanner); + + int token; + while (1) { + if (extra) { + /* We are returning buffered tokens */ + dmnsn_buffered_token buffered; + + if (extra->i < dmnsn_array_size(extra->buffered)) { + dmnsn_array_get(extra->buffered, extra->i, &buffered); + token = buffered.type; + *lvalp = buffered.lval; + *llocp = buffered.lloc; + ++extra->i; + + if (extra->type == DMNSN_T_LEX_VERBATIM && extra->i == 1) { + /* Don't reprocess the first token in some situations */ + return token; + } + } else { + dmnsn_yyset_extra(extra->prev, yyscanner); + dmnsn_delete_yylex_extra(extra); + extra = dmnsn_yyget_extra(yyscanner); + continue; + } + } else { + token = dmnsn_yylex_impl(lvalp, llocp, filename, yyscanner); + } + + switch (token) { + case DMNSN_T_DECLARE: + case DMNSN_T_LOCAL: + { + dmnsn_yylex_extra *ex = dmnsn_new_yylex_extra(DMNSN_T_LEX_VERBATIM); + ex->prev = extra; + + /* Buffer the current token */ + dmnsn_buffered_token buffered = { + .type = token, + .lval = *lvalp, + .lloc = *llocp + }; + dmnsn_array_push(ex->buffered, &buffered); + + /* Grab all the tokens belonging to the #declare/#local */ + int bracelevel = -1; + while (1) { + /* Recursive call */ + buffered.type = dmnsn_yylex(&buffered.lval, &buffered.lloc, + filename, symtable, yyscanner); + + if (buffered.type == DMNSN_T_EOF) { + dmnsn_diagnostic(filename, buffered.lloc.first_line, + buffered.lloc.first_column, + "unexpected end-of-file"); + dmnsn_delete_yylex_extra(ex); + return DMNSN_T_LEX_ERROR; + } else if (buffered.type == DMNSN_T_LEX_ERROR) { + dmnsn_delete_yylex_extra(ex); + return DMNSN_T_LEX_ERROR; + } + + dmnsn_array_push(ex->buffered, &buffered); + + if (buffered.type == DMNSN_T_LBRACE) { + if (bracelevel < 0) + bracelevel = 1; + else + ++bracelevel; + } else if (buffered.type == DMNSN_T_RBRACE) { + --bracelevel; + if (bracelevel == 0) { + break; + } + } else if (buffered.type == DMNSN_T_SEMICOLON) { + break; + } + } + + /* Fake EOF */ + buffered.type = DMNSN_T_EOF; + dmnsn_array_push(ex->buffered, &buffered); + + dmnsn_yyset_extra(ex, yyscanner); + if (dmnsn_ld_yyparse(filename, yyscanner, symtable) != 0) { + dmnsn_yyset_extra(ex->prev, yyscanner); + dmnsn_delete_yylex_extra(ex); + return DMNSN_T_LEX_ERROR; + } + + /* Restore the previous extra pointer */ + dmnsn_yyset_extra(ex->prev, yyscanner); + dmnsn_delete_yylex_extra(ex); + break; + } + + case DMNSN_T_UNDEF: + { + dmnsn_yylex_extra *ex = dmnsn_new_yylex_extra(DMNSN_T_LEX_VERBATIM); + ex->prev = extra; + + /* Buffer the current token */ + dmnsn_buffered_token buffered = { + .type = token, + .lval = *lvalp, + .lloc = *llocp + }; + dmnsn_array_push(ex->buffered, &buffered); + + /* Recursive call */ + buffered.type = dmnsn_yylex(&buffered.lval, &buffered.lloc, + filename, symtable, yyscanner); + + if (buffered.type == DMNSN_T_EOF) { + dmnsn_diagnostic(filename, buffered.lloc.first_line, + buffered.lloc.first_column, + "unexpected end-of-file"); + dmnsn_delete_yylex_extra(ex); + return DMNSN_T_LEX_ERROR; + } else if (buffered.type == DMNSN_T_LEX_ERROR) { + dmnsn_delete_yylex_extra(ex); + return DMNSN_T_LEX_ERROR; + } + /* Buffer the next token */ + dmnsn_array_push(ex->buffered, &buffered); + + /* Fake EOF */ + buffered.type = DMNSN_T_EOF; + dmnsn_array_push(ex->buffered, &buffered); + + dmnsn_yyset_extra(ex, yyscanner); + if (dmnsn_ld_yyparse(filename, yyscanner, symtable) != 0) { + dmnsn_yyset_extra(ex->prev, yyscanner); + dmnsn_delete_yylex_extra(ex); + return DMNSN_T_LEX_ERROR; + } + + /* Restore the previous extra pointer */ + dmnsn_yyset_extra(ex->prev, yyscanner); + dmnsn_delete_yylex_extra(ex); + break; + } + + default: + return token; + } + } } diff --git a/dimension/tokenize.h b/dimension/tokenize.h index 801092a..6dafc97 100644 --- a/dimension/tokenize.h +++ b/dimension/tokenize.h @@ -46,14 +46,17 @@ struct dmnsn_token { int line, col; }; -/* Set up the scanner */ -int dmnsn_yylex_init(void **scannerp); -void dmnsn_yyset_in(FILE *file, void *scanner); -int dmnsn_yylex_destroy(void *scanner); +/* Scanner manipulation */ +int dmnsn_yylex_init(void **scannerp); +void dmnsn_yyset_in(FILE *file, void *scanner); +int dmnsn_yylex_destroy(void *scanner); +void *dmnsn_yyget_extra(void *scanner); +void dmnsn_yyset_extra(void *arbitrary_data, void *scanner); /* Actual lexer */ int dmnsn_yylex(dmnsn_parse_item *lvalp, dmnsn_parse_location *llocp, - const char *filename, void *yyscanner); + const char *filename, dmnsn_symbol_table *symtable, + void *yyscanner); /* For debugging - returns an array of raw tokens */ dmnsn_array *dmnsn_tokenize(FILE *file, const char *filename); |