diff options
-rw-r--r-- | RELEASE_NOTES | 6 | ||||
-rw-r--r-- | doc/TODO.txt | 12 | ||||
-rw-r--r-- | src/func.cc | 46 | ||||
-rw-r--r-- | src/lexer.l | 98 |
4 files changed, 133 insertions, 29 deletions
diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 51b20ee..69fe747 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,8 +1,10 @@ -OpenSCAD 2010.06 +OpenSCAD 2010.XX ================ -... add stuff here ... +o Added rands() function +o Added sign() function +o Bugfixes: More robust DXF export OpenSCAD 2010.05 ================ diff --git a/doc/TODO.txt b/doc/TODO.txt index 7348bec..6d608ee 100644 --- a/doc/TODO.txt +++ b/doc/TODO.txt @@ -131,10 +131,11 @@ o Function-Module-Interface - Pass a module instanciation to a function (e.g. for a volume() function) - Pass a function to a module instanciation (e.g. for dynamic extrusion paths) o Language Frontend + - include statement doesn't support nesting. This can be fixed by + keeping a nested stack of current input files in the lexer. See + the "Flex & Bison" O'Reilly book, "Start States and Nested Input + Files", page 28, for an example. - Allow local variables and functions everywhere (not only on module level) - - Add "use" statement to load modules. Like include but read a module only once, - ignore all top level objects (they are used as module testcase) and search in - a module search path. - allow 0/1 f/t FALSE/TRUE as boolean values - allow any expression to be evaluated as boolean (e.g. 1 = true, 0 = false) - Rethink for vs. intersection_for vs. group. Should for loops @@ -194,6 +195,11 @@ o Consider evaluating all referenced files relative to the document path instead of being absolute. This would e.g. make regression testing of dumpcaching easier. This would require us to pass a document contect to all traversal methods though. +BUILD SYSTEM +------------ +o Fedora is reported to ship with byacc, which doesn't support bison extensions (e.g. %debuig). Look into this, either be yacc-compatible or force the build system to use bison. +o We currently link in -lboost_thread. Should we always use -lboost_thread-mt under Linux or can we pick this up using qmake? + TESTING ------- o Caching and MDI looks suspicious when the code relies on external resources diff --git a/src/func.cc b/src/func.cc index d73b152..7627a94 100644 --- a/src/func.cc +++ b/src/func.cc @@ -125,6 +125,51 @@ Value builtin_sign(const Context *, const QVector<QString>&, const QVector<Value return Value(); } +double frand() +{ + return rand()/(double(RAND_MAX)+1); +} + +double frand(double min, double max) +{ + return (min>max) ? frand()*(min-max)+max : frand()*(max-min)+min; +} + +Value builtin_rands(const Context *, const QVector<QString>&, const QVector<Value> &args) +{ + if (args.size() == 3 && + args[0].type == Value::NUMBER && + args[1].type == Value::NUMBER && + args[2].type == Value::NUMBER) + { + srand((unsigned int)time(0)); + } + else if (args.size() == 4 && + args[0].type == Value::NUMBER && + args[1].type == Value::NUMBER && + args[2].type == Value::NUMBER && + args[3].type == Value::NUMBER) + { + srand((unsigned int)args[3].num); + } + else + { + return Value(); + } + + Value v; + v.type = Value::VECTOR; + + for (int i=0; i<args[2].num; i++) + { + Value * r = new Value(frand(args[0].num, args[1].num)); + v.vec.append(r); + } + + return v; +} + + Value builtin_min(const Context *, const QVector<QString>&, const QVector<Value> &args) { if (args.size() >= 1 && args[0].type == Value::NUMBER) { @@ -298,6 +343,7 @@ void initialize_builtin_functions() { builtin_functions["abs"] = new BuiltinFunction(&builtin_abs); builtin_functions["sign"] = new BuiltinFunction(&builtin_sign); + builtin_functions["rands"] = new BuiltinFunction(&builtin_rands); builtin_functions["min"] = new BuiltinFunction(&builtin_min); builtin_functions["max"] = new BuiltinFunction(&builtin_max); builtin_functions["sin"] = new BuiltinFunction(&builtin_sin); diff --git a/src/lexer.l b/src/lexer.l index 48729c8..9e8aaf8 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -28,9 +28,10 @@ #include "openscad.h" #include "printutils.h" #include "parser_yacc.h" +#include <QStack> #include <QFileInfo> #include <QDir> - +QString* stringcontents; int lexerget_lineno(void); #ifdef __GNUC__ static void yyunput(int, char*) __attribute__((unused)); @@ -58,33 +59,33 @@ extern const char *parser_source_path; } \ } +void includefile(); +QDir sourcepath(); +QStack<QDir> path_stack; + +QString filename; +QString filepath; + %} %option yylineno %option noyywrap -%x comment +%x comment string +%x include + +DIGIT [0-9] %% -include[ \t\r\n>]*"<"[^ \t\r\n>]+">" { - QString filename(yytext); - filename.remove(QRegExp("^include[ \t\r\n>]*<")); - filename.remove(QRegExp(">$")); - QFileInfo finfo(QDir(parser_source_path), filename); - if (!finfo.exists()) { - finfo = QFileInfo(QDir(librarydir), filename); - } - handle_dep(finfo.absoluteFilePath()); - yyin = fopen(finfo.absoluteFilePath().toLocal8Bit(), "r"); - if (!yyin) { - PRINTA("WARNING: Can't open input file `%1'.", filename); - } else { - yypush_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE )); - BEGIN(INITIAL); - } +include[ \t\r\n>]*"<" { BEGIN(include); } +<include>{ +[^\t\r\n>]+"/" { filepath = yytext; } +[^\t\r\n>/]+ { filename = yytext; } +">" { BEGIN(INITIAL); includefile(); } } + use[ \t\r\n>]*"<"[^ \t\r\n>]+">" { QString filename(yytext); filename.remove(QRegExp("^use[ \t\r\n>]*<")); @@ -106,7 +107,7 @@ use[ \t\r\n>]*"<"[^ \t\r\n>]+">" { finfo = QFileInfo(QDir(librarydir), filename); } - PRINTF("WARNING: Support for implicit include will be removed in future releases. Use `include <filename>' instead."); + PRINTF("DEPRECATED: Support for implicit include will be removed in future releases. Use `include <filename>' instead."); handle_dep(finfo.absoluteFilePath()); yyin = fopen(finfo.absoluteFilePath().toLocal8Bit(), "r"); if (!yyin) { @@ -119,6 +120,8 @@ use[ \t\r\n>]*"<"[^ \t\r\n>]+">" { } <<EOF>> { + if(!path_stack.isEmpty()) + path_stack.pop(); if (yyin && yyin != stdin) fclose(yyin); yypop_buffer_state(); @@ -135,13 +138,21 @@ use[ \t\r\n>]*"<"[^ \t\r\n>]+">" { "false" return TOK_FALSE; "undef" return TOK_UNDEF; -[0-9][0-9.]* { parserlval.number = QString(yytext).toDouble(); return TOK_NUMBER; } +{DIGIT}+|{DIGIT}*\.{DIGIT}+|{DIGIT}+\.{DIGIT}* { parserlval.number = QString(yytext).toDouble(); return TOK_NUMBER; } "$"?[a-zA-Z0-9_]+ { parserlval.text = strdup(yytext); return TOK_ID; } -\"[^"]*\" { - parserlval.text = strdup(yytext+1); - parserlval.text[strlen(parserlval.text)-1] = 0; - return TOK_STRING; +\" { BEGIN(string); stringcontents = new QString(); } +<string>{ +\\n { stringcontents->append('\n'); } +\\t { stringcontents->append('\t'); } +\\r { stringcontents->append('\r'); } +\\\\ { stringcontents->append('\\'); } +\\\" { stringcontents->append('"'); } +[^\\\n\"]+ { stringcontents->append(lexertext); } +\" { BEGIN(INITIAL); + parserlval.text = strdup(stringcontents->toLocal8Bit()); + delete stringcontents; + return TOK_STRING; } } [\n\r\t ] @@ -159,3 +170,42 @@ use[ \t\r\n>]*"<"[^ \t\r\n>]+">" { . { return yytext[0]; } +%% + +QDir sourcepath() +{ + if(!path_stack.isEmpty()) + return path_stack.top(); + + return QDir(parser_source_path); +} + +void includefile() +{ + if(filename.isEmpty()) + return; + + if(filepath.isEmpty()) { + path_stack.push(sourcepath()); + } else { + QFileInfo dirinfo(sourcepath(),filepath); + path_stack.push(dirinfo.dir()); + filepath.clear(); + } + + QFileInfo finfo(sourcepath(), filename); + if (!finfo.exists()) { + finfo = QFileInfo(QDir(librarydir), filename); + } + + handle_dep(finfo.absoluteFilePath()); + yyin = fopen(finfo.absoluteFilePath().toLocal8Bit(), "r"); + if (!yyin) { + PRINTA("WARNING: Can't open input file `%1'.", filename); + return; + } + filename.clear(); + + yypush_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE )); +} + |