/* * OpenSCAD (www.openscad.org) * Copyright (C) 2009-2011 Clifford Wolf and * Marius Kintel * * This program 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 2 of the License, or * (at your option) any later version. * * As a special exception, you have permission to link this program * with the CGAL library and distribute executables, as long as you * follow the requirements of the GNU GPL in regard to all of the * software in the executable aside from CGAL. * * This program 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ %expect 1 /* Expect 1 shift/reduce conflict for ifelse_statement - "dangling else problem" */ %{ #include #include #ifndef _MSC_VER #include #endif #include "typedefs.h" #include "module.h" #include "expression.h" #include "value.h" #include "function.h" #include "printutils.h" #include #include #include namespace fs = boost::filesystem; #define foreach BOOST_FOREACH #include "boosty.h" int parser_error_pos = -1; int parserlex(void); void yyerror(char const *s); int lexerget_lineno(void); int lexerlex_destroy(void); int lexerlex(void); std::stack scope_stack; FileModule *rootmodule; extern void lexerdestroy(); extern FILE *lexerin; extern const char *parser_input_buffer; const char *parser_input_buffer; std::string parser_source_path; %} %union { char *text; double number; class Value *value; class Expression *expr; class ModuleInstantiation *inst; class IfElseModuleInstantiation *ifelse; Assignment *arg; AssignmentList *args; } %token TOK_MODULE %token TOK_FUNCTION %token TOK_IF %token TOK_ELSE %token TOK_ID %token TOK_STRING %token TOK_USE %token TOK_NUMBER %token TOK_TRUE %token TOK_FALSE %token TOK_UNDEF %token LE GE EQ NE AND OR %right '?' ':' %left OR %left AND %left '<' LE GE '>' %left EQ NE %left '!' '+' '-' %left '*' '/' '%' %left '[' ']' %left '.' %type expr %type vector_expr %type module_instantiation %type if_statement %type ifelse_statement %type single_module_instantiation %type arguments_call %type arguments_decl %type argument_call %type argument_decl %debug %% input: /* empty */ | TOK_USE { rootmodule->usedlibs.insert($1); } input | statement input ; statement: ';' | '{' inner_input '}' | module_instantiation { if ($1) scope_stack.top()->addChild($1); } | assignment | TOK_MODULE TOK_ID '(' arguments_decl optional_commas ')' { Module *newmodule = new Module(); newmodule->definition_arguments = *$4; scope_stack.top()->modules[$2] = newmodule; scope_stack.push(&newmodule->scope); free($2); delete $4; } statement { scope_stack.pop(); } | TOK_FUNCTION TOK_ID '(' arguments_decl optional_commas ')' '=' expr { Function *func = new Function(); func->definition_arguments = *$4; func->expr = $8; scope_stack.top()->functions[$2] = func; free($2); delete $4; } ';' ; inner_input: /* empty */ | statement inner_input ; assignment: TOK_ID '=' expr ';' { bool found = false; foreach (Assignment& iter, scope_stack.top()->assignments) { if (iter.first == $1) { iter.second = $3; found = true; break; } } if (!found) { scope_stack.top()->assignments.push_back(Assignment($1, $3)); } } ; module_instantiation: '!' module_instantiation { $$ = $2; if ($$) $$->tag_root = true; } | '#' module_instantiation { $$ = $2; if ($$) $$->tag_highlight = true; } | '%' module_instantiation { $$ = $2; if ($$) $$->tag_background = true; } | '*' module_instantiation { delete $2; $$ = NULL; } | single_module_instantiation { $$ = $1; scope_stack.push(&$1->scope); } child_statement { scope_stack.pop(); $$ = $2; } | ifelse_statement { $$ = $1; } ; ifelse_statement: if_statement { $$ = $1; } | if_statement TOK_ELSE { scope_stack.push(&$1->else_scope); } child_statement { scope_stack.pop(); $$ = $1; } ; if_statement: TOK_IF '(' expr ')' { $$ = new IfElseModuleInstantiation(); $$->arguments.push_back(Assignment("", $3)); $$->setPath(parser_source_path); scope_stack.push(&$$->scope); } child_statement { scope_stack.pop(); $$ = $5; } ; child_statements: /* empty */ | child_statements child_statement ; child_statement: ';' | '{' child_statements '}' | module_instantiation { if ($1) scope_stack.top()->addChild($1); } ; /* FIXME: This allows for variable declaration in child blocks, not activated yet | assignment ; */ single_module_instantiation: TOK_ID '(' arguments_call ')' { $$ = new ModuleInstantiation($1); $$->arguments = *$3; $$->setPath(parser_source_path); free($1); delete $3; } ; expr: TOK_TRUE { $$ = new Expression(Value(true)); } | TOK_FALSE { $$ = new Expression(Value(false)); } | TOK_UNDEF { $$ = new Expression(Value::undefined); } | TOK_ID { $$ = new Expression(); $$->type = "L"; $$->var_name = $1; free($1); } | expr '.' TOK_ID { $$ = new Expression("N", $1); $$->var_name = $3; free($3); } | TOK_STRING { $$ = new Expression(Value(std::string($1))); free($1); } | TOK_NUMBER { $$ = new Expression(Value($1)); } | '[' expr ':' expr ']' { $$ = new Expression(); $$->type = "R"; $$->children.push_back($2); $$->children.push_back($4); } | '[' expr ':' expr ':' expr ']' { $$ = new Expression(); $$->type = "R"; $$->children.push_back($2); $$->children.push_back($4); $$->children.push_back($6); } | '[' optional_commas ']' { $$ = new Expression(Value(Value::VectorType())); } | '[' vector_expr optional_commas ']' { $$ = $2; } | expr '*' expr { $$ = new Expression("*", $1, $3); } | expr '/' expr { $$ = new Expression("/", $1, $3); } | expr '%' expr { $$ = new Expression("%", $1, $3); } | expr '+' expr { $$ = new Expression("+", $1, $3); } | expr '-' expr { $$ = new Expression("-", $1, $3); } | expr '<' expr { $$ = new Expression("<", $1, $3); } | expr LE expr { $$ = new Expression("<=", $1, $3); } | expr EQ expr { $$ = new Expression("==", $1, $3); } | expr NE expr { $$ = new Expression("!=", $1, $3); } | expr GE expr { $$ = new Expression(">=", $1, $3); } | expr '>' expr { $$ = new Expression(">", $1, $3); } | expr AND expr { $$ = new Expression("&&", $1, $3); } | expr OR expr { $$ = new Expression("||", $1, $3); } | '+' expr { $$ = $2; } | '-' expr { $$ = new Expression("I", $2); } | '!' expr { $$ = new Expression("!", $2); } | '(' expr ')' { $$ = $2; } | expr '?' expr ':' expr { $$ = new Expression(); $$->type = "?:"; $$->children.push_back($1); $$->children.push_back($3); $$->children.push_back($5); } | expr '[' expr ']' { $$ = new Expression("[]", $1, $3); } | TOK_ID '(' arguments_call ')' { $$ = new Expression(); $$->type = "F"; $$->call_funcname = $1; $$->call_arguments = *$3; free($1); delete $3; } ; optional_commas: ',' optional_commas | /* empty */ ; vector_expr: expr { $$ = new Expression("V", $1); } | vector_expr ',' optional_commas expr { $$ = $1; $$->children.push_back($4); } ; arguments_decl: /* empty */ { $$ = new AssignmentList(); } | argument_decl { $$ = new AssignmentList(); $$->push_back(*$1); delete $1; } | arguments_decl ',' optional_commas argument_decl { $$ = $1; $$->push_back(*$4); delete $4; } ; argument_decl: TOK_ID { $$ = new Assignment($1, NULL); free($1); } | TOK_ID '=' expr { $$ = new Assignment($1, $3); free($1); } ; arguments_call: /* empty */ { $$ = new AssignmentList(); } | argument_call { $$ = new AssignmentList(); $$->push_back(*$1); delete $1; } | arguments_call ',' optional_commas argument_call { $$ = $1; $$->push_back(*$4); delete $4; } ; argument_call: expr { $$ = new Assignment("", $1); } | TOK_ID '=' expr { $$ = new Assignment($1, $3); free($1); } ; %% int parserlex(void) { return lexerlex(); } void yyerror (char const *s) { // FIXME: We leak memory on parser errors... PRINTB("Parser error in line %d: %s\n", lexerget_lineno() % s); } FileModule *parse(const char *text, const char *path, int debug) { lexerin = NULL; parser_error_pos = -1; parser_input_buffer = text; parser_source_path = boosty::absolute(std::string(path)).string(); rootmodule = new FileModule(); rootmodule->setModulePath(path); scope_stack.push(&rootmodule->scope); // PRINTB_NOCACHE("New module: %s %p", "root" % rootmodule); parserdebug = debug; int parserretval = parserparse(); lexerdestroy(); lexerlex_destroy(); if (parserretval != 0) return NULL; parser_error_pos = -1; scope_stack.pop(); return rootmodule; }