diff options
66 files changed, 845 insertions, 637 deletions
@@ -1,4 +1,4 @@ -win32 { +{ bison.name = Bison ${QMAKE_FILE_IN} bison.input = BISONSOURCES bison.output = ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}_yacc.cpp @@ -1,4 +1,4 @@ -win32 { +{ flex.name = Flex ${QMAKE_FILE_IN} flex.input = FLEXSOURCES flex.output = ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}.lexer.cpp diff --git a/openscad.pro b/openscad.pro index 0f3394e..81f5e6f 100644 --- a/openscad.pro +++ b/openscad.pro @@ -171,13 +171,8 @@ CONFIG(mingw-cross-env) { include(mingw-cross-env.pri) } -win32 { - FLEXSOURCES = src/lexer.l - BISONSOURCES = src/parser.y -} else { - LEXSOURCES += src/lexer.l - YACCSOURCES += src/parser.y -} +FLEXSOURCES = src/lexer.l +BISONSOURCES = src/parser.y RESOURCES = openscad.qrc @@ -216,6 +211,7 @@ HEADERS += src/typedefs.h \ src/function.h \ src/grid.h \ src/highlighter.h \ + src/localscope.h \ src/module.h \ src/node.h \ src/csgnode.h \ @@ -272,6 +268,7 @@ SOURCES += src/version_check.cc \ src/value.cc \ src/expr.cc \ src/func.cc \ + src/localscope.cc \ src/module.cc \ src/node.cc \ src/context.cc \ diff --git a/src/MainWindow.h b/src/MainWindow.h index 65deb15..378705e 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -29,8 +29,8 @@ public: QTimer *autoReloadTimer; std::string autoReloadId; - ModuleContext root_ctx; - Module *root_module; // Result of parsing + ModuleContext top_ctx; + FileModule *root_module; // Result of parsing ModuleInstantiation root_inst; // Top level instance AbstractNode *absolute_root_node; // Result of tree evaluation AbstractNode *root_node; // Root if the root modifier (!) is used diff --git a/src/ModuleCache.cc b/src/ModuleCache.cc index 19a3f84..4944495 100644 --- a/src/ModuleCache.cc +++ b/src/ModuleCache.cc @@ -28,9 +28,9 @@ static bool is_modified(const std::string &filename, const time_t &mtime) return (st.st_mtime > mtime); } -Module *ModuleCache::evaluate(const std::string &filename) +FileModule *ModuleCache::evaluate(const std::string &filename) { - Module *lib_mod = NULL; + FileModule *lib_mod = NULL; // Create cache ID struct stat st; @@ -48,7 +48,7 @@ Module *ModuleCache::evaluate(const std::string &filename) #endif lib_mod = &(*this->entries[filename].module); - BOOST_FOREACH(const Module::IncludeContainer::value_type &item, lib_mod->includes) { + BOOST_FOREACH(const FileModule::IncludeContainer::value_type &item, lib_mod->includes) { if (is_modified(item.first, item.second)) { lib_mod = NULL; break; @@ -78,7 +78,7 @@ Module *ModuleCache::evaluate(const std::string &filename) print_messages_push(); - Module *oldmodule = NULL; + FileModule *oldmodule = NULL; cache_entry e = { NULL, cache_id }; if (this->entries.find(filename) != this->entries.end()) { oldmodule = this->entries[filename].module; @@ -86,7 +86,7 @@ Module *ModuleCache::evaluate(const std::string &filename) this->entries[filename] = e; std::string pathname = boosty::stringy(fs::path(filename).parent_path()); - lib_mod = dynamic_cast<Module*>(parse(textbuf.str().c_str(), pathname.c_str(), false)); + lib_mod = dynamic_cast<FileModule*>(parse(textbuf.str().c_str(), pathname.c_str(), false)); PRINTB_NOCACHE(" compiled module: %p", lib_mod); if (lib_mod) { diff --git a/src/ModuleCache.h b/src/ModuleCache.h index 1e6373d..b8ded38 100644 --- a/src/ModuleCache.h +++ b/src/ModuleCache.h @@ -1,11 +1,14 @@ #include <string> #include <boost/unordered_map.hpp> +/*! + Caches FileModules based on their filenames +*/ class ModuleCache { public: static ModuleCache *instance() { if (!inst) inst = new ModuleCache; return inst; } - class Module *evaluate(const std::string &filename); + class FileModule *evaluate(const std::string &filename); size_t size() { return this->entries.size(); } void clear(); @@ -16,7 +19,7 @@ private: static ModuleCache *inst; struct cache_entry { - class Module *module; + class FileModule *module; std::string cache_id; }; boost::unordered_map<std::string, cache_entry> entries; diff --git a/src/builtin.cc b/src/builtin.cc index bdd2d3b..e116f17 100644 --- a/src/builtin.cc +++ b/src/builtin.cc @@ -16,12 +16,12 @@ Builtins *Builtins::instance(bool erase) void Builtins::init(const char *name, class AbstractModule *module) { - Builtins::instance()->rootmodule.modules[name] = module; + Builtins::instance()->globalscope.modules[name] = module; } void Builtins::init(const char *name, class AbstractFunction *function) { - Builtins::instance()->rootmodule.functions[name] = function; + Builtins::instance()->globalscope.functions[name] = function; } extern void register_builtin_functions(); @@ -80,24 +80,18 @@ std::string Builtins::isDeprecated(const std::string &name) Builtins::Builtins() { - this->rootmodule.assignments_var.push_back("$fn"); - this->rootmodule.assignments["$fn"] = new Expression(Value(0.0)); - this->rootmodule.assignments_var.push_back("$fs"); - this->rootmodule.assignments["$fs"] = new Expression(Value(2.0)); - this->rootmodule.assignments_var.push_back("$fa"); - this->rootmodule.assignments["$fa"] = new Expression(Value(12.0)); - this->rootmodule.assignments_var.push_back("$t"); - this->rootmodule.assignments["$t"] = new Expression(Value(0.0)); + this->globalscope.assignments.push_back(Assignment("$fn", new Expression(Value(0.0)))); + this->globalscope.assignments.push_back(Assignment("$fs", new Expression(Value(2.0)))); + this->globalscope.assignments.push_back(Assignment("$fa", new Expression(Value(12.0)))); + this->globalscope.assignments.push_back(Assignment("$t", new Expression(Value(0.0)))); Value::VectorType zero3; zero3.push_back(Value(0.0)); zero3.push_back(Value(0.0)); zero3.push_back(Value(0.0)); Value zero3val(zero3); - this->rootmodule.assignments_var.push_back("$vpt"); - this->rootmodule.assignments["$vpt"] = new Expression(zero3val); - this->rootmodule.assignments_var.push_back("$vpr"); - this->rootmodule.assignments["$vpr"] = new Expression(zero3val); + this->globalscope.assignments.push_back(Assignment("$vpt", new Expression(zero3val))); + this->globalscope.assignments.push_back(Assignment("$vpr", new Expression(zero3val))); } Builtins::~Builtins() diff --git a/src/builtin.h b/src/builtin.h index 564c951..9397aa9 100644 --- a/src/builtin.h +++ b/src/builtin.h @@ -4,6 +4,7 @@ #include <string> #include <boost/unordered_map.hpp> #include "module.h" +#include "localscope.h" class Builtins { @@ -17,18 +18,13 @@ public: void initialize(); std::string isDeprecated(const std::string &name); - const FunctionContainer &functions() { return this->builtinfunctions; } - const ModuleContainer &modules() { return this->builtinmodules; } - - const Module &getRootModule() { return this->rootmodule; } + const LocalScope &getGlobalScope() { return this->globalscope; } private: Builtins(); ~Builtins(); - Module rootmodule; - FunctionContainer builtinfunctions; - ModuleContainer builtinmodules; + LocalScope globalscope; boost::unordered_map<std::string, std::string> deprecations; }; diff --git a/src/cgaladv.cc b/src/cgaladv.cc index aad3a95..70590f7 100644 --- a/src/cgaladv.cc +++ b/src/cgaladv.cc @@ -39,10 +39,10 @@ class CgaladvModule : public AbstractModule public: cgaladv_type_e type; CgaladvModule(cgaladv_type_e type) : type(type) { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; }; -AbstractNode *CgaladvModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *CgaladvModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { CgaladvNode *node = new CgaladvNode(inst, type); @@ -110,8 +110,8 @@ AbstractNode *CgaladvModule::evaluate(const Context *ctx, const ModuleInstantiat if (node->level <= 1) node->level = 1; - std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx); - node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); + std::vector<AbstractNode *> instantiatednodes = inst->instantiateChildren(evalctx); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); return node; } diff --git a/src/color.cc b/src/color.cc index 6c3aaef..7fef030 100644 --- a/src/color.cc +++ b/src/color.cc @@ -40,14 +40,14 @@ class ColorModule : public AbstractModule { public: ColorModule() { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; private: static boost::unordered_map<std::string, Color4f> colormap; }; #include "colormap.h" -AbstractNode *ColorModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { ColorNode *node = new ColorNode(inst); @@ -87,8 +87,8 @@ AbstractNode *ColorModule::evaluate(const Context *ctx, const ModuleInstantiatio node->color[3] = alpha.toDouble(); } - std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx); - node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); + std::vector<AbstractNode *> instantiatednodes = inst->instantiateChildren(evalctx); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); return node; } diff --git a/src/context.cc b/src/context.cc index 706407c..a7273a4 100644 --- a/src/context.cc +++ b/src/context.cc @@ -66,9 +66,9 @@ void Context::setVariables(const AssignmentList &args, if (evalctx) { size_t posarg = 0; - for (size_t i=0; i<evalctx->eval_arguments.size(); i++) { - const std::string &name = evalctx->eval_arguments[i].first; - const Value &val = evalctx->eval_arguments[i].second; + for (size_t i=0; i<evalctx->numArgs(); i++) { + const std::string &name = evalctx->getArgName(i); + const Value &val = evalctx->getArgValue(i); if (name.empty()) { if (posarg < args.size()) this->set_variable(args[posarg++].first, val); } else { @@ -124,9 +124,9 @@ Value Context::evaluate_function(const std::string &name, const EvalContext *eva return Value(); } -AbstractNode *Context::evaluate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) const +AbstractNode *Context::instantiate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) const { - if (this->parent) return this->parent->evaluate_module(inst, evalctx); + if (this->parent) return this->parent->instantiate_module(inst, evalctx); PRINTB("WARNING: Ignoring unknown module '%s'.", inst.name()); return NULL; } diff --git a/src/context.h b/src/context.h index 51fc45c..9817df3 100644 --- a/src/context.h +++ b/src/context.h @@ -14,7 +14,7 @@ public: virtual ~Context(); virtual Value evaluate_function(const std::string &name, const class EvalContext *evalctx) const; - virtual class AbstractNode *evaluate_module(const class ModuleInstantiation &inst, const EvalContext *evalctx) const; + virtual class AbstractNode *instantiate_module(const class ModuleInstantiation &inst, const EvalContext *evalctx) const; void setVariables(const AssignmentList &args, const class EvalContext *evalctx = NULL); @@ -39,7 +39,7 @@ protected: ValueMap variables; ValueMap config_variables; - std::string document_path; + std::string document_path; // FIXME: This is a remnant only needed by dxfdim #ifdef DEBUG public: diff --git a/src/control.cc b/src/control.cc index d47eec0..7786e36 100644 --- a/src/control.cc +++ b/src/control.cc @@ -46,15 +46,15 @@ class ControlModule : public AbstractModule public: control_type_e type; ControlModule(control_type_e type) : type(type) { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; }; void for_eval(AbstractNode &node, const ModuleInstantiation &inst, size_t l, const Context *ctx, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() > l) { - const std::string &it_name = evalctx->eval_arguments[l].first; - const Value &it_values = evalctx->eval_arguments[l].second; + if (evalctx->numArgs() > l) { + const std::string &it_name = evalctx->getArgName(l); + const Value &it_values = evalctx->getArgValue(l, ctx); Context c(ctx); if (it_values.type() == Value::RANGE) { Value::RangeType range = it_values.toRange(); @@ -81,21 +81,21 @@ void for_eval(AbstractNode &node, const ModuleInstantiation &inst, size_t l, for_eval(node, inst, l+1, &c, evalctx); } } else if (l > 0) { - std::vector<AbstractNode *> evaluatednodes = inst.evaluateChildren(ctx); - node.children.insert(node.children.end(), evaluatednodes.begin(), evaluatednodes.end()); + std::vector<AbstractNode *> instantiatednodes = inst.instantiateChildren(ctx); + node.children.insert(node.children.end(), instantiatednodes.begin(), instantiatednodes.end()); } } -AbstractNode *ControlModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *ControlModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { AbstractNode *node = NULL; if (type == CHILD) { int n = 0; - if (evalctx->eval_arguments.size() > 0) { + if (evalctx->numArgs() > 0) { double v; - if (evalctx->eval_arguments[0].second.getDouble(v)) { + if (evalctx->getArgValue(0).getDouble(v)) { n = trunc(v); if (n < 0) { PRINTB("WARNING: Negative child index (%d) not allowed", n); @@ -114,14 +114,14 @@ AbstractNode *ControlModule::evaluate(const Context *ctx, const ModuleInstantiat // assert(filectx->evalctx); if (filectx->evalctx) { - if (n < filectx->evalctx->children.size()) { - node = filectx->evalctx->children[n]->evaluate_instance(filectx->evalctx); + if (n < filectx->evalctx->numChildren()) { + node = filectx->evalctx->getChild(n)->evaluate(filectx->evalctx); } else { // How to deal with negative objects in this case? // (e.g. first child of difference is invalid) PRINTB("WARNING: Child index (%d) out of bounds (%d children)", - n % filectx->evalctx->children.size()); + n % filectx->evalctx->numChildren()); } } return node; @@ -142,8 +142,8 @@ AbstractNode *ControlModule::evaluate(const Context *ctx, const ModuleInstantiat msg << "ECHO: "; for (size_t i = 0; i < inst->arguments.size(); i++) { if (i > 0) msg << ", "; - if (!evalctx->eval_arguments[i].first.empty()) msg << evalctx->eval_arguments[i].first << " = "; - msg << evalctx->eval_arguments[i].second; + if (!evalctx->getArgName(i).empty()) msg << evalctx->getArgName(i) << " = "; + msg << evalctx->getArgValue(i); } PRINTB("%s", msg.str()); } @@ -151,12 +151,12 @@ AbstractNode *ControlModule::evaluate(const Context *ctx, const ModuleInstantiat if (type == ASSIGN) { Context c(evalctx); - for (size_t i = 0; i < evalctx->eval_arguments.size(); i++) { - if (!evalctx->eval_arguments[i].first.empty()) - c.set_variable(evalctx->eval_arguments[i].first, evalctx->eval_arguments[i].second); + for (size_t i = 0; i < evalctx->numArgs(); i++) { + if (!evalctx->getArgName(i).empty()) + c.set_variable(evalctx->getArgName(i), evalctx->getArgValue(i)); } - std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(&c); - node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); + std::vector<AbstractNode *> instantiatednodes = inst->instantiateChildren(&c); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); } if (type == FOR || type == INT_FOR) @@ -167,13 +167,13 @@ AbstractNode *ControlModule::evaluate(const Context *ctx, const ModuleInstantiat if (type == IF) { const IfElseModuleInstantiation *ifelse = dynamic_cast<const IfElseModuleInstantiation*>(inst); - if (evalctx->eval_arguments.size() > 0 && evalctx->eval_arguments[0].second.toBool()) { - std::vector<AbstractNode *> evaluatednodes = ifelse->evaluateChildren(evalctx); - node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); + if (evalctx->numArgs() > 0 && evalctx->getArgValue(0).toBool()) { + std::vector<AbstractNode *> instantiatednodes = ifelse->instantiateChildren(evalctx); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); } else { - std::vector<AbstractNode *> evaluatednodes = ifelse->evaluateElseChildren(evalctx); - node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); + std::vector<AbstractNode *> instantiatednodes = ifelse->instantiateElseChildren(evalctx); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); } } diff --git a/src/csgops.cc b/src/csgops.cc index 425b747..92b97e7 100644 --- a/src/csgops.cc +++ b/src/csgops.cc @@ -38,14 +38,14 @@ class CsgModule : public AbstractModule public: csg_type_e type; CsgModule(csg_type_e type) : type(type) { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; }; -AbstractNode *CsgModule::evaluate(const Context*, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *CsgModule::instantiate(const Context*, const ModuleInstantiation *inst, const EvalContext *evalctx) const { CsgNode *node = new CsgNode(inst, type); - std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx); - node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); + std::vector<AbstractNode *> instantiatednodes = inst->instantiateChildren(evalctx); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); return node; } diff --git a/src/dxfdim.cc b/src/dxfdim.cc index fbc24c4..555ed49 100644 --- a/src/dxfdim.cc +++ b/src/dxfdim.cc @@ -52,17 +52,17 @@ Value builtin_dxf_dim(const Context *ctx, const EvalContext *evalctx) // FIXME: We don't lookup the file relative to where this function was instantiated // since the path is only available for ModuleInstantiations, not function expressions. // See issue #217 - for (size_t i = 0; i < evalctx->eval_arguments.size(); i++) { - if (evalctx->eval_arguments[i].first == "file") - filename = ctx->getAbsolutePath(evalctx->eval_arguments[i].second.toString()); - if (evalctx->eval_arguments[i].first == "layer") - layername = evalctx->eval_arguments[i].second.toString(); - if (evalctx->eval_arguments[i].first == "origin") - evalctx->eval_arguments[i].second.getVec2(xorigin, yorigin); - if (evalctx->eval_arguments[i].first == "scale") - evalctx->eval_arguments[i].second.getDouble(scale); - if (evalctx->eval_arguments[i].first == "name") - name = evalctx->eval_arguments[i].second.toString(); + for (size_t i = 0; i < evalctx->numArgs(); i++) { + if (evalctx->getArgName(i) == "file") + filename = evalctx->getAbsolutePath(evalctx->getArgValue(i).toString()); + if (evalctx->getArgName(i) == "layer") + layername = evalctx->getArgValue(i).toString(); + if (evalctx->getArgName(i) == "origin") + evalctx->getArgValue(i).getVec2(xorigin, yorigin); + if (evalctx->getArgName(i) == "scale") + evalctx->getArgValue(i).getDouble(scale); + if (evalctx->getArgName(i) == "name") + name = evalctx->getArgValue(i).toString(); } std::stringstream keystream; @@ -146,15 +146,15 @@ Value builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx) // FIXME: We don't lookup the file relative to where this function was instantiated // since the path is only available for ModuleInstantiations, not function expressions. // See isse #217 - for (size_t i = 0; i < evalctx->eval_arguments.size(); i++) { - if (evalctx->eval_arguments[i].first == "file") - filename = ctx->getAbsolutePath(evalctx->eval_arguments[i].second.toString()); - if (evalctx->eval_arguments[i].first == "layer") - layername = evalctx->eval_arguments[i].second.toString(); - if (evalctx->eval_arguments[i].first == "origin") - evalctx->eval_arguments[i].second.getVec2(xorigin, yorigin); - if (evalctx->eval_arguments[i].first == "scale") - evalctx->eval_arguments[i].second.getDouble(scale); + for (size_t i = 0; i < evalctx->numArgs(); i++) { + if (evalctx->getArgName(i) == "file") + filename = ctx->getAbsolutePath(evalctx->getArgValue(i).toString()); + if (evalctx->getArgName(i) == "layer") + layername = evalctx->getArgValue(i).toString(); + if (evalctx->getArgName(i) == "origin") + evalctx->getArgValue(i).getVec2(xorigin, yorigin); + if (evalctx->getArgName(i) == "scale") + evalctx->getArgValue(i).getDouble(scale); } std::stringstream keystream; diff --git a/src/evalcontext.cc b/src/evalcontext.cc index b7566e3..57c206f 100644 --- a/src/evalcontext.cc +++ b/src/evalcontext.cc @@ -4,9 +4,33 @@ #include "function.h" #include "printutils.h" #include "builtin.h" +#include "localscope.h" #include <boost/foreach.hpp> +const std::string &EvalContext::getArgName(size_t i) const +{ + assert(i < this->eval_arguments.size()); + return this->eval_arguments[i].first; +} + +Value EvalContext::getArgValue(size_t i, const Context *ctx) const +{ + assert(i < this->eval_arguments.size()); + const Assignment &arg = this->eval_arguments[i]; + return arg.second ? arg.second->evaluate(ctx ? ctx : this) : Value(); +} + +size_t EvalContext::numChildren() const +{ + return this->scope ? this->scope->children.size() : 0; +} + +ModuleInstantiation *EvalContext::getChild(size_t i) const +{ + return this->scope ? this->scope->children[i] : NULL; +} + #ifdef DEBUG void EvalContext::dump(const AbstractModule *mod, const ModuleInstantiation *inst) { @@ -20,9 +44,9 @@ void EvalContext::dump(const AbstractModule *mod, const ModuleInstantiation *ins for (int i=0;i<this->eval_arguments.size();i++) { PRINTB(" %s = %s", this->eval_arguments[i].first % this->eval_arguments[i].second); } - if (this->children.size() > 0) { + if (this->scope && this->scope->children.size() > 0) { PRINT(" children:"); - BOOST_FOREACH(const ModuleInstantiation *ch, this->children) { + BOOST_FOREACH(const ModuleInstantiation *ch, this->scope->children) { PRINTB(" %s", ch->name()); } } diff --git a/src/evalcontext.h b/src/evalcontext.h index 3d7d222..34f339a 100644 --- a/src/evalcontext.h +++ b/src/evalcontext.h @@ -10,15 +10,28 @@ class EvalContext : public Context { public: - EvalContext(const Context *parent = NULL) : Context(parent) {} + typedef std::vector<class ModuleInstantiation *> InstanceList; + + EvalContext(const Context *parent, + const AssignmentList &args, const class LocalScope *const scope = NULL) + : Context(parent), eval_arguments(args), scope(scope) {} virtual ~EvalContext() {} - std::vector<std::pair<std::string, Value> > eval_arguments; - std::vector<class ModuleInstantiation *> children; + size_t numArgs() const { return this->eval_arguments.size(); } + const std::string &getArgName(size_t i) const; + Value getArgValue(size_t i, const Context *ctx = NULL) const; + + size_t numChildren() const; + ModuleInstantiation *getChild(size_t i) const; #ifdef DEBUG virtual void dump(const class AbstractModule *mod, const ModuleInstantiation *inst); #endif + +private: + const AssignmentList &eval_arguments; + std::vector<std::pair<std::string, Value> > eval_values; + const LocalScope *const scope; }; #endif diff --git a/src/expr.cc b/src/expr.cc index 7a8180f..746c0e3 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -38,6 +38,20 @@ Expression::Expression() { } +Expression::Expression(const std::string &type, + Expression *left, Expression *right) + : type(type) +{ + this->children.push_back(left); + this->children.push_back(right); +} + +Expression::Expression(const std::string &type, Expression *expr) + : type(type) +{ + this->children.push_back(expr); +} + Expression::Expression(const Value &val) : const_value(val), type("C") { } @@ -127,17 +141,7 @@ Value Expression::evaluate(const Context *context) const return Value(); } if (this->type == "F") { - EvalContext c(context); - for (size_t i=0; i < this->call_arguments.size(); i++) { - c.eval_arguments.push_back(std::make_pair(this->call_arguments[i].first, - this->call_arguments[i].second->evaluate(context))); - } - // Value::VectorType argvalues; - // std::transform(this->children.begin(), this->children.end(), - // std::back_inserter(argvalues), - // boost::bind(&Expression::evaluate, _1, context)); - // for (size_t i=0; i < this->children.size(); i++) - // argvalues.push_back(this->children[i]->evaluate(context)); + EvalContext c(context, this->call_arguments); return context->evaluate_function(this->call_funcname, &c); } abort(); diff --git a/src/expression.h b/src/expression.h index 06becc0..6c03f52 100644 --- a/src/expression.h +++ b/src/expression.h @@ -34,6 +34,8 @@ public: Expression(); Expression(const Value &val); + Expression(const std::string &type, Expression *left, Expression *right); + Expression(const std::string &type, Expression *expr); ~Expression(); Value evaluate(const class Context *context) const; diff --git a/src/func.cc b/src/func.cc index 88bf3c8..4377a90 100644 --- a/src/func.cc +++ b/src/func.cc @@ -129,35 +129,35 @@ static inline double rad2deg(double x) Value builtin_abs(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(fabs(evalctx->eval_arguments[0].second.toDouble())); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(fabs(evalctx->getArgValue(0).toDouble())); return Value(); } Value builtin_sign(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value((evalctx->eval_arguments[0].second.toDouble()<0) ? -1.0 : ((evalctx->eval_arguments[0].second.toDouble()>0) ? 1.0 : 0.0)); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value((evalctx->getArgValue(0).toDouble()<0) ? -1.0 : ((evalctx->getArgValue(0).toDouble()>0) ? 1.0 : 0.0)); return Value(); } Value builtin_rands(const Context *, const EvalContext *evalctx) { bool deterministic = false; - if (evalctx->eval_arguments.size() == 3 && - evalctx->eval_arguments[0].second.type() == Value::NUMBER && - evalctx->eval_arguments[1].second.type() == Value::NUMBER && - evalctx->eval_arguments[2].second.type() == Value::NUMBER) + if (evalctx->numArgs() == 3 && + evalctx->getArgValue(0).type() == Value::NUMBER && + evalctx->getArgValue(1).type() == Value::NUMBER && + evalctx->getArgValue(2).type() == Value::NUMBER) { deterministic = false; } - else if (evalctx->eval_arguments.size() == 4 && - evalctx->eval_arguments[0].second.type() == Value::NUMBER && - evalctx->eval_arguments[1].second.type() == Value::NUMBER && - evalctx->eval_arguments[2].second.type() == Value::NUMBER && - evalctx->eval_arguments[3].second.type() == Value::NUMBER) + else if (evalctx->numArgs() == 4 && + evalctx->getArgValue(0).type() == Value::NUMBER && + evalctx->getArgValue(1).type() == Value::NUMBER && + evalctx->getArgValue(2).type() == Value::NUMBER && + evalctx->getArgValue(3).type() == Value::NUMBER) { - deterministic_rng.seed( (unsigned int) evalctx->eval_arguments[3].second.toDouble() ); + deterministic_rng.seed( (unsigned int) evalctx->getArgValue(3).toDouble() ); deterministic = true; } else @@ -165,11 +165,11 @@ Value builtin_rands(const Context *, const EvalContext *evalctx) return Value(); } - double min = std::min( evalctx->eval_arguments[0].second.toDouble(), evalctx->eval_arguments[1].second.toDouble() ); - double max = std::max( evalctx->eval_arguments[0].second.toDouble(), evalctx->eval_arguments[1].second.toDouble() ); + double min = std::min( evalctx->getArgValue(0).toDouble(), evalctx->getArgValue(1).toDouble() ); + double max = std::max( evalctx->getArgValue(0).toDouble(), evalctx->getArgValue(1).toDouble() ); boost::uniform_real<> distributor( min, max ); Value::VectorType vec; - for (int i=0; i<evalctx->eval_arguments[2].second.toDouble(); i++) { + for (int i=0; i<evalctx->getArgValue(2).toDouble(); i++) { if ( deterministic ) { vec.push_back( Value( distributor( deterministic_rng ) ) ); } else { @@ -183,11 +183,11 @@ Value builtin_rands(const Context *, const EvalContext *evalctx) Value builtin_min(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() >= 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) { - double val = evalctx->eval_arguments[0].second.toDouble(); - for (size_t i = 1; i < evalctx->eval_arguments.size(); i++) - if (evalctx->eval_arguments[1].second.type() == Value::NUMBER) - val = fmin(val, evalctx->eval_arguments[i].second.toDouble()); + if (evalctx->numArgs() >= 1 && evalctx->getArgValue(0).type() == Value::NUMBER) { + double val = evalctx->getArgValue(0).toDouble(); + for (size_t i = 1; i < evalctx->numArgs(); i++) + if (evalctx->getArgValue(1).type() == Value::NUMBER) + val = fmin(val, evalctx->getArgValue(i).toDouble()); return Value(val); } return Value(); @@ -195,11 +195,11 @@ Value builtin_min(const Context *, const EvalContext *evalctx) Value builtin_max(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() >= 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) { - double val = evalctx->eval_arguments[0].second.toDouble(); - for (size_t i = 1; i < evalctx->eval_arguments.size(); i++) - if (evalctx->eval_arguments[1].second.type() == Value::NUMBER) - val = fmax(val, evalctx->eval_arguments[i].second.toDouble()); + if (evalctx->numArgs() >= 1 && evalctx->getArgValue(0).type() == Value::NUMBER) { + double val = evalctx->getArgValue(0).toDouble(); + for (size_t i = 1; i < evalctx->numArgs(); i++) + if (evalctx->getArgValue(1).type() == Value::NUMBER) + val = fmax(val, evalctx->getArgValue(i).toDouble()); return Value(val); } return Value(); @@ -207,117 +207,117 @@ Value builtin_max(const Context *, const EvalContext *evalctx) Value builtin_sin(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(sin(deg2rad(evalctx->eval_arguments[0].second.toDouble()))); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(sin(deg2rad(evalctx->getArgValue(0).toDouble()))); return Value(); } Value builtin_cos(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(cos(deg2rad(evalctx->eval_arguments[0].second.toDouble()))); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(cos(deg2rad(evalctx->getArgValue(0).toDouble()))); return Value(); } Value builtin_asin(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(rad2deg(asin(evalctx->eval_arguments[0].second.toDouble()))); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(rad2deg(asin(evalctx->getArgValue(0).toDouble()))); return Value(); } Value builtin_acos(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(rad2deg(acos(evalctx->eval_arguments[0].second.toDouble()))); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(rad2deg(acos(evalctx->getArgValue(0).toDouble()))); return Value(); } Value builtin_tan(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(tan(deg2rad(evalctx->eval_arguments[0].second.toDouble()))); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(tan(deg2rad(evalctx->getArgValue(0).toDouble()))); return Value(); } Value builtin_atan(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(rad2deg(atan(evalctx->eval_arguments[0].second.toDouble()))); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(rad2deg(atan(evalctx->getArgValue(0).toDouble()))); return Value(); } Value builtin_atan2(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 2 && evalctx->eval_arguments[0].second.type() == Value::NUMBER && evalctx->eval_arguments[1].second.type() == Value::NUMBER) - return Value(rad2deg(atan2(evalctx->eval_arguments[0].second.toDouble(), evalctx->eval_arguments[1].second.toDouble()))); + if (evalctx->numArgs() == 2 && evalctx->getArgValue(0).type() == Value::NUMBER && evalctx->getArgValue(1).type() == Value::NUMBER) + return Value(rad2deg(atan2(evalctx->getArgValue(0).toDouble(), evalctx->getArgValue(1).toDouble()))); return Value(); } Value builtin_pow(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 2 && evalctx->eval_arguments[0].second.type() == Value::NUMBER && evalctx->eval_arguments[1].second.type() == Value::NUMBER) - return Value(pow(evalctx->eval_arguments[0].second.toDouble(), evalctx->eval_arguments[1].second.toDouble())); + if (evalctx->numArgs() == 2 && evalctx->getArgValue(0).type() == Value::NUMBER && evalctx->getArgValue(1).type() == Value::NUMBER) + return Value(pow(evalctx->getArgValue(0).toDouble(), evalctx->getArgValue(1).toDouble())); return Value(); } Value builtin_round(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(round(evalctx->eval_arguments[0].second.toDouble())); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(round(evalctx->getArgValue(0).toDouble())); return Value(); } Value builtin_ceil(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(ceil(evalctx->eval_arguments[0].second.toDouble())); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(ceil(evalctx->getArgValue(0).toDouble())); return Value(); } Value builtin_floor(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(floor(evalctx->eval_arguments[0].second.toDouble())); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(floor(evalctx->getArgValue(0).toDouble())); return Value(); } Value builtin_sqrt(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(sqrt(evalctx->eval_arguments[0].second.toDouble())); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(sqrt(evalctx->getArgValue(0).toDouble())); return Value(); } Value builtin_exp(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(exp(evalctx->eval_arguments[0].second.toDouble())); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(exp(evalctx->getArgValue(0).toDouble())); return Value(); } Value builtin_length(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1) { - if (evalctx->eval_arguments[0].second.type() == Value::VECTOR) return Value(int(evalctx->eval_arguments[0].second.toVector().size())); - if (evalctx->eval_arguments[0].second.type() == Value::STRING) return Value(int(evalctx->eval_arguments[0].second.toString().size())); + if (evalctx->numArgs() == 1) { + if (evalctx->getArgValue(0).type() == Value::VECTOR) return Value(int(evalctx->getArgValue(0).toVector().size())); + if (evalctx->getArgValue(0).type() == Value::STRING) return Value(int(evalctx->getArgValue(0).toString().size())); } return Value(); } Value builtin_log(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 2 && evalctx->eval_arguments[0].second.type() == Value::NUMBER && evalctx->eval_arguments[1].second.type() == Value::NUMBER) - return Value(log(evalctx->eval_arguments[1].second.toDouble()) / log(evalctx->eval_arguments[0].second.toDouble())); - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(log(evalctx->eval_arguments[0].second.toDouble()) / log(10.0)); + if (evalctx->numArgs() == 2 && evalctx->getArgValue(0).type() == Value::NUMBER && evalctx->getArgValue(1).type() == Value::NUMBER) + return Value(log(evalctx->getArgValue(1).toDouble()) / log(evalctx->getArgValue(0).toDouble())); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(log(evalctx->getArgValue(0).toDouble()) / log(10.0)); return Value(); } Value builtin_ln(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER) - return Value(log(evalctx->eval_arguments[0].second.toDouble())); + if (evalctx->numArgs() == 1 && evalctx->getArgValue(0).type() == Value::NUMBER) + return Value(log(evalctx->getArgValue(0).toDouble())); return Value(); } @@ -325,8 +325,8 @@ Value builtin_str(const Context *, const EvalContext *evalctx) { std::stringstream stream; - for (size_t i = 0; i < evalctx->eval_arguments.size(); i++) { - stream << evalctx->eval_arguments[i].second.toString(); + for (size_t i = 0; i < evalctx->numArgs(); i++) { + stream << evalctx->getArgValue(i).toString(); } return Value(stream.str()); } @@ -334,16 +334,16 @@ Value builtin_str(const Context *, const EvalContext *evalctx) Value builtin_lookup(const Context *, const EvalContext *evalctx) { double p, low_p, low_v, high_p, high_v; - if (evalctx->eval_arguments.size() < 2 || // Needs two args - !evalctx->eval_arguments[0].second.getDouble(p) || // First must be a number - evalctx->eval_arguments[1].second.toVector().size() < 2 || // Second must be a vector of vectors - evalctx->eval_arguments[1].second.toVector()[0].toVector().size() < 2) + if (evalctx->numArgs() < 2 || // Needs two args + !evalctx->getArgValue(0).getDouble(p) || // First must be a number + evalctx->getArgValue(1).toVector().size() < 2 || // Second must be a vector of vectors + evalctx->getArgValue(1).toVector()[0].toVector().size() < 2) return Value(); - if (!evalctx->eval_arguments[1].second.toVector()[0].getVec2(low_p, low_v) || !evalctx->eval_arguments[1].second.toVector()[0].getVec2(high_p, high_v)) + if (!evalctx->getArgValue(1).toVector()[0].getVec2(low_p, low_v) || !evalctx->getArgValue(1).toVector()[0].getVec2(high_p, high_v)) return Value(); - for (size_t i = 1; i < evalctx->eval_arguments[1].second.toVector().size(); i++) { + for (size_t i = 1; i < evalctx->getArgValue(1).toVector().size(); i++) { double this_p, this_v; - if (evalctx->eval_arguments[1].second.toVector()[i].getVec2(this_p, this_v)) { + if (evalctx->getArgValue(1).toVector()[i].getVec2(this_p, this_v)) { if (this_p <= p && (this_p > low_p || low_p > p)) { low_p = this_p; low_v = this_v; @@ -406,12 +406,12 @@ Value builtin_lookup(const Context *, const EvalContext *evalctx) */ Value builtin_search(const Context *, const EvalContext *evalctx) { - if (evalctx->eval_arguments.size() < 2) return Value(); + if (evalctx->numArgs() < 2) return Value(); - const Value &findThis = evalctx->eval_arguments[0].second; - const Value &searchTable = evalctx->eval_arguments[1].second; - unsigned int num_returns_per_match = (evalctx->eval_arguments.size() > 2) ? evalctx->eval_arguments[2].second.toDouble() : 1; - unsigned int index_col_num = (evalctx->eval_arguments.size() > 3) ? evalctx->eval_arguments[3].second.toDouble() : 0; + const Value &findThis = evalctx->getArgValue(0); + const Value &searchTable = evalctx->getArgValue(1); + unsigned int num_returns_per_match = (evalctx->numArgs() > 2) ? evalctx->getArgValue(2).toDouble() : 1; + unsigned int index_col_num = (evalctx->numArgs() > 3) ? evalctx->getArgValue(3).toDouble() : 0; Value::VectorType returnvec; @@ -512,7 +512,7 @@ Value builtin_version(const Context *, const EvalContext *evalctx) Value builtin_version_num(const Context *ctx, const EvalContext *evalctx) { - Value val = (evalctx->eval_arguments.size() == 0) ? builtin_version(ctx, evalctx) : evalctx->eval_arguments[0].second; + Value val = (evalctx->numArgs() == 0) ? builtin_version(ctx, evalctx) : evalctx->getArgValue(0); double y, m, d = 0; if (!val.getVec3(y, m, d)) { if (!val.getVec2(y, m)) { diff --git a/src/import.cc b/src/import.cc index 927c9d8..bd8f830 100644 --- a/src/import.cc +++ b/src/import.cc @@ -60,10 +60,10 @@ class ImportModule : public AbstractModule public: import_type_e type; ImportModule(import_type_e type = TYPE_UNKNOWN) : type(type) { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; }; -AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { AssignmentList args; args += Assignment("file", NULL), Assignment("layer", NULL), Assignment("convexity", NULL), Assignment("origin", NULL), Assignment("scale", NULL); @@ -93,10 +93,10 @@ AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiati Value v = c.lookup_variable("file"); if (v.isUndefined()) { v = c.lookup_variable("filename"); - if (!v.isUndefined()) - PRINT("WARNING: filename= is deprecated. Please use file="); + if (!v.isUndefined()) { + PRINT("DEPRECATED: filename= is deprecated. Please use file="); + } } - std::string filename = inst->getAbsolutePath(v.isUndefined() ? "" : v.toString()); import_type_e actualtype = this->type; if (actualtype == TYPE_UNKNOWN) { @@ -116,9 +116,10 @@ AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiati node->filename = filename; Value layerval = c.lookup_variable("layer", true); if (layerval.isUndefined()) { - layerval = c.lookup_variable("layername",true); - if (!layerval.isUndefined()) - PRINT("WARNING: layername= is deprecated. Please use layer="); + layerval = c.lookup_variable("layername"); + if (!layerval.isUndefined()) { + PRINT("DEPRECATED: layername= is deprecated. Please use layer="); + } } node->layername = layerval.isUndefined() ? "" : layerval.toString(); node->convexity = c.lookup_variable("convexity", true).toDouble(); diff --git a/src/lexer.l b/src/lexer.l index 6766abc..6dfe9bc 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -55,7 +55,7 @@ static void yyunput(int, char*) __attribute__((unused)); #endif extern const char *parser_input_buffer; extern std::string parser_source_path; -extern Module *currmodule; +extern FileModule *rootmodule; #define YY_INPUT(buf,result,max_size) { \ if (yyin && yyin != stdin) { \ @@ -248,7 +248,7 @@ void includefile() path_stack.push_back(finfo.parent_path()); handle_dep(fullname); - currmodule->registerInclude(fullname); + rootmodule->registerInclude(fullname); yyin = fopen(fullname.c_str(), "r"); if (!yyin) { PRINTB("WARNING: Can't open 'include' file '%s'.", filename); diff --git a/src/linearextrude.cc b/src/linearextrude.cc index f4c1112..c64a235 100644 --- a/src/linearextrude.cc +++ b/src/linearextrude.cc @@ -45,10 +45,10 @@ class LinearExtrudeModule : public AbstractModule { public: LinearExtrudeModule() { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; }; -AbstractNode *LinearExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { LinearExtrudeNode *node = new LinearExtrudeNode(inst); @@ -80,10 +80,10 @@ AbstractNode *LinearExtrudeModule::evaluate(const Context *ctx, const ModuleInst // if height not given, and first argument is a number, // then assume it should be the height. if (c.lookup_variable("height").isUndefined() && - evalctx->eval_arguments.size() > 0 && - evalctx->eval_arguments[0].first == "" && - evalctx->eval_arguments[0].second.type() == Value::NUMBER) { - height = Value(evalctx->eval_arguments[0].second); + evalctx->numArgs() > 0 && + evalctx->getArgName(0) == "" && + evalctx->getArgValue(0).type() == Value::NUMBER) { + height = evalctx->getArgValue(0); } node->layername = layer.isUndefined() ? "" : layer.toString(); @@ -116,8 +116,8 @@ AbstractNode *LinearExtrudeModule::evaluate(const Context *ctx, const ModuleInst } if (node->filename.empty()) { - std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx); - node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); + std::vector<AbstractNode *> instantiatednodes = inst->instantiateChildren(evalctx); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); } return node; diff --git a/src/localscope.cc b/src/localscope.cc new file mode 100644 index 0000000..eecff91 --- /dev/null +++ b/src/localscope.cc @@ -0,0 +1,73 @@ +#include "localscope.h" +#include "modcontext.h" +#include "module.h" +#include "typedefs.h" +#include "expression.h" +#include "function.h" + +#include <boost/foreach.hpp> + +LocalScope::LocalScope() +{ +} + +LocalScope::~LocalScope() +{ + BOOST_FOREACH (ModuleInstantiation *v, children) delete v; + BOOST_FOREACH (const Assignment &v, assignments) delete v.second; + BOOST_FOREACH (FunctionContainer::value_type &f, functions) delete f.second; + BOOST_FOREACH (AbstractModuleContainer::value_type &m, modules) delete m.second; +} + +void LocalScope::addChild(ModuleInstantiation *ch) +{ + assert(ch != NULL); + this->children.push_back(ch); +} + +std::string LocalScope::dump(const std::string &indent) const +{ + std::stringstream dump; + BOOST_FOREACH(const FunctionContainer::value_type &f, this->functions) { + dump << f.second->dump(indent, f.first); + } + BOOST_FOREACH(const AbstractModuleContainer::value_type &m, this->modules) { + dump << m.second->dump(indent, m.first); + } + BOOST_FOREACH(const Assignment &ass, this->assignments) { + dump << indent << ass.first << " = " << *ass.second << ";\n"; + } + BOOST_FOREACH(const ModuleInstantiation *inst, this->children) { + dump << inst->dump(indent); + } + return dump.str(); +} + +// FIXME: Two parameters here is a hack. Rather have separate types of scopes, or check the type of the first parameter. Note const vs. non-const +std::vector<AbstractNode*> LocalScope::instantiateChildren(const Context *evalctx, FileContext *filectx) const +{ + Context *c = filectx; + + if (!c) { + c = new Context(evalctx); + + // FIXME: If we make c a ModuleContext, child() doesn't work anymore + // c->functions_p = &this->functions; + // c->modules_p = &this->modules; + + BOOST_FOREACH (const Assignment &ass, this->assignments) { + c->set_variable(ass.first, ass.second->evaluate(c)); + } + } + + std::vector<AbstractNode*> childnodes; + BOOST_FOREACH (ModuleInstantiation *modinst, this->children) { + AbstractNode *node = modinst->evaluate(c); + if (node) childnodes.push_back(node); + } + + if (c != filectx) delete c; + + return childnodes; +} + diff --git a/src/localscope.h b/src/localscope.h new file mode 100644 index 0000000..d81a27c --- /dev/null +++ b/src/localscope.h @@ -0,0 +1,26 @@ +#ifndef LOCALSCOPE_H_ +#define LOCALSCOPE_H_ + +#include "typedefs.h" +#include <boost/unordered_map.hpp> + +class LocalScope +{ +public: + LocalScope(); + ~LocalScope(); + + size_t numElements() const { return assignments.size() + children.size(); } + std::string dump(const std::string &indent) const; + std::vector<class AbstractNode*> instantiateChildren(const class Context *evalctx, class FileContext *filectx = NULL) const; + void addChild(ModuleInstantiation *ch); + + AssignmentList assignments; + ModuleInstantiationList children; + typedef boost::unordered_map<std::string, class AbstractFunction*> FunctionContainer; + FunctionContainer functions; + typedef boost::unordered_map<std::string, class AbstractModule*> AbstractModuleContainer; + AbstractModuleContainer modules; +}; + +#endif diff --git a/src/mainwin.cc b/src/mainwin.cc index 2e69ec2..027f72a 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -168,7 +168,7 @@ MainWindow::MainWindow(const QString &filename) this, SLOT(actionRenderCGALDone(CGAL_Nef_polyhedron *))); #endif - root_ctx.registerBuiltin(); + top_ctx.registerBuiltin(); this->openglbox = NULL; root_module = NULL; @@ -506,7 +506,7 @@ MainWindow::setFileName(const QString &filename) { if (filename.isEmpty()) { this->fileName.clear(); - this->root_ctx.setDocumentPath(currentdir); + this->top_ctx.setDocumentPath(currentdir); setWindowTitle("OpenSCAD - New Document[*]"); } else { @@ -522,7 +522,7 @@ MainWindow::setFileName(const QString &filename) this->fileName = fileinfo.fileName(); } - this->root_ctx.setDocumentPath(fileinfo.dir().absolutePath().toLocal8Bit().constData()); + this->top_ctx.setDocumentPath(fileinfo.dir().absolutePath().toLocal8Bit().constData()); QDir::setCurrent(fileinfo.dir().absolutePath()); } @@ -644,7 +644,7 @@ bool MainWindow::compile(bool reload, bool procevents) AbstractNode::resetIndexCounter(); this->root_inst = ModuleInstantiation("group"); - this->absolute_root_node = this->root_module->evaluate(&this->root_ctx, &this->root_inst, NULL); + this->absolute_root_node = this->root_module->instantiate(&top_ctx, &this->root_inst, NULL); if (this->absolute_root_node) { // Do we have an explicit root node (! modifier)? @@ -979,19 +979,19 @@ void MainWindow::pasteViewportRotation() void MainWindow::updateTemporalVariables() { - this->root_ctx.set_variable("$t", Value(this->e_tval->text().toDouble())); + this->top_ctx.set_variable("$t", Value(this->e_tval->text().toDouble())); Value::VectorType vpt; vpt.push_back(Value(-qglview->cam.object_trans.x())); vpt.push_back(Value(-qglview->cam.object_trans.y())); vpt.push_back(Value(-qglview->cam.object_trans.z())); - this->root_ctx.set_variable("$vpt", Value(vpt)); + this->top_ctx.set_variable("$vpt", Value(vpt)); Value::VectorType vpr; vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.x() + 90, 360))); vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.y(), 360))); vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.z(), 360))); - root_ctx.set_variable("$vpr", Value(vpr)); + top_ctx.set_variable("$vpr", Value(vpr)); } bool MainWindow::fileChangedOnDisk() @@ -1022,7 +1022,7 @@ static bool is_modified(const std::string &filename, const time_t &mtime) bool MainWindow::includesChanged() { if (this->root_module) { - BOOST_FOREACH(const Module::IncludeContainer::value_type &item, this->root_module->includes) { + BOOST_FOREACH(const FileModule::IncludeContainer::value_type &item, this->root_module->includes) { if (is_modified(item.first, item.second)) return true; } } diff --git a/src/modcontext.cc b/src/modcontext.cc index 7d123b8..44c2002 100644 --- a/src/modcontext.cc +++ b/src/modcontext.cc @@ -7,46 +7,24 @@ #include <boost/foreach.hpp> -ModuleContext::ModuleContext(const class Module *module, const Context *parent, const EvalContext *evalctx) - : Context(parent) +ModuleContext::ModuleContext(const Context *parent, const EvalContext *evalctx) + : Context(parent), functions_p(NULL), modules_p(NULL), evalctx(evalctx) { - if (module) { - setModule(*module, evalctx); - } - else { - this->functions_p = NULL; - this->modules_p = NULL; - this->usedlibs_p = NULL; - } } ModuleContext::~ModuleContext() { } -void ModuleContext::setModule(const Module &module, const EvalContext *evalctx) +void ModuleContext::initializeModule(const class Module &module) { this->setVariables(module.definition_arguments, evalctx); - this->evalctx = evalctx; - // FIXME: Don't access module members directly - this->functions_p = &module.functions; - this->modules_p = &module.modules; - this->usedlibs_p = &module.usedlibs; - BOOST_FOREACH(const std::string &var, module.assignments_var) { - this->set_variable(var, module.assignments.at(var)->evaluate(this)); + this->functions_p = &module.scope.functions; + this->modules_p = &module.scope.modules; + BOOST_FOREACH(const Assignment &ass, module.scope.assignments) { + this->set_variable(ass.first, ass.second->evaluate(this)); } - - if (!module.modulePath().empty()) this->document_path = module.modulePath(); -} - -/*! - Only used to initialize builtins for the top-level root context -*/ -void ModuleContext::registerBuiltin() -{ - this->setModule(Builtins::instance()->getRootModule()); - this->set_constant("PI",Value(M_PI)); } class RecursionGuard @@ -62,61 +40,65 @@ private: const std::string &name; }; -Value ModuleContext::evaluate_function(const std::string &name, const EvalContext *evalctx) const + +/*! + Only used to initialize builtins for the top-level root context +*/ +void ModuleContext::registerBuiltin() { - RecursionGuard g(*this, name); - if (g.recursion_detected()) { - PRINTB("Recursion detected calling function '%s'", name); - return Value(); + const LocalScope &scope = Builtins::instance()->getGlobalScope(); + + // FIXME: Don't access module members directly + this->functions_p = &scope.functions; + this->modules_p = &scope.modules; + BOOST_FOREACH(const Assignment &ass, scope.assignments) { + this->set_variable(ass.first, ass.second->evaluate(this)); } + this->set_constant("PI",Value(M_PI)); +} + +const AbstractFunction *ModuleContext::findLocalFunction(const std::string &name) const +{ if (this->functions_p && this->functions_p->find(name) != this->functions_p->end()) { - return this->functions_p->find(name)->second->evaluate(this, evalctx); - } - - if (this->usedlibs_p) { - BOOST_FOREACH(const ModuleContainer::value_type &m, *this->usedlibs_p) { - if (m.second->functions.find(name) != m.second->functions.end()) { - ModuleContext ctx(m.second, this->parent); - // FIXME: Set document path -#if 0 && DEBUG - PRINTB("New lib Context for %s func:", name); - ctx.dump(NULL, NULL); -#endif - return m.second->functions[name]->evaluate(&ctx, evalctx); - } - } + return this->functions_p->find(name)->second; } - return Context::evaluate_function(name, evalctx); + return NULL; } -AbstractNode *ModuleContext::evaluate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) const +const AbstractModule *ModuleContext::findLocalModule(const std::string &name) const { - if (this->modules_p && this->modules_p->find(inst.name()) != this->modules_p->end()) { - AbstractModule *m = this->modules_p->find(inst.name())->second; - std::string replacement = Builtins::instance()->isDeprecated(inst.name()); + if (this->modules_p && this->modules_p->find(name) != this->modules_p->end()) { + AbstractModule *m = this->modules_p->find(name)->second; + std::string replacement = Builtins::instance()->isDeprecated(name); if (!replacement.empty()) { - PRINTB("DEPRECATED: The %s() module will be removed in future releases. Use %s() instead.", inst.name() % replacement); + PRINTB("DEPRECATED: The %s() module will be removed in future releases. Use %s() instead.", name % replacement); } - return m->evaluate(this, &inst, evalctx); + return m; } + return NULL; +} - if (this->usedlibs_p) { - BOOST_FOREACH(const ModuleContainer::value_type &m, *this->usedlibs_p) { - assert(m.second); - if (m.second->modules.find(inst.name()) != m.second->modules.end()) { - ModuleContext ctx(m.second, this->parent); - // FIXME: Set document path -#if 0 && DEBUG - PRINT("New lib Context:"); - ctx.dump(NULL, &inst); -#endif - return m.second->modules[inst.name()]->evaluate(&ctx, &inst, evalctx); - } - } +Value ModuleContext::evaluate_function(const std::string &name, const EvalContext *evalctx) const +{ + RecursionGuard g(*this, name); + if (g.recursion_detected()) { + PRINTB("Recursion detected calling function '%s'", name); + return Value(); } - return Context::evaluate_module(inst, evalctx); + const AbstractFunction *foundf = findLocalFunction(name); + if (foundf) return foundf->evaluate(this, evalctx); + + return Context::evaluate_function(name, evalctx); +} + +AbstractNode *ModuleContext::instantiate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) const +{ + const AbstractModule *foundm = this->findLocalModule(inst.name()); + if (foundm) return foundm->instantiate(this, &inst, evalctx); + + return Context::instantiate_module(inst, evalctx); } #ifdef DEBUG @@ -150,3 +132,58 @@ void ModuleContext::dump(const AbstractModule *mod, const ModuleInstantiation *i } #endif + +FileContext::FileContext(const class FileModule &module, const Context *parent) + : usedlibs(module.usedlibs), ModuleContext(parent) +{ + if (!module.modulePath().empty()) this->document_path = module.modulePath(); +} + +Value FileContext::evaluate_function(const std::string &name, const EvalContext *evalctx) const +{ + RecursionGuard g(*this, name); + if (g.recursion_detected()) { + PRINTB("Recursion detected calling function '%s'", name); + return Value(); + } + + const AbstractFunction *foundf = findLocalFunction(name); + if (foundf) return foundf->evaluate(this, evalctx); + + BOOST_FOREACH(const FileModule::ModuleContainer::value_type &m, this->usedlibs) { + if (m.second->scope.functions.find(name) != m.second->scope.functions.end()) { + FileContext ctx(*m.second, this->parent); + ctx.initializeModule(*m.second); + // FIXME: Set document path +#if 0 && DEBUG + PRINTB("New lib Context for %s func:", name); + ctx.dump(NULL, NULL); +#endif + return m.second->scope.functions[name]->evaluate(&ctx, evalctx); + } + } + + return ModuleContext::evaluate_function(name, evalctx); +} + +AbstractNode *FileContext::instantiate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) const +{ + const AbstractModule *foundm = this->findLocalModule(inst.name()); + if (foundm) return foundm->instantiate(this, &inst, evalctx); + + BOOST_FOREACH(const FileModule::ModuleContainer::value_type &m, this->usedlibs) { + assert(m.second); + if (m.second->scope.modules.find(inst.name()) != m.second->scope.modules.end()) { + FileContext ctx(*m.second, this->parent); + ctx.initializeModule(*m.second); + // FIXME: Set document path +#if 0 && DEBUG + PRINT("New file Context:"); + ctx.dump(NULL, &inst); +#endif + return m.second->scope.modules[inst.name()]->instantiate(&ctx, &inst, evalctx); + } + } + + return ModuleContext::instantiate_module(inst, evalctx); +} diff --git a/src/modcontext.h b/src/modcontext.h index 5fce88f..4479051 100644 --- a/src/modcontext.h +++ b/src/modcontext.h @@ -2,30 +2,33 @@ #define FILECONTEXT_H_ #include "context.h" +#include "module.h" #include <boost/unordered_map.hpp> /*! This holds the context for a Module definition; keeps track of global variables, submodules and functions defined inside a module. - NB! every .scad file defines an implicit unnamed module holding the contents of the file. + NB! every .scad file defines a FileModule holding the contents of the file. */ class ModuleContext : public Context { public: - ModuleContext(const class Module *module = NULL, const Context *parent = NULL, const EvalContext *evalctx = NULL); + ModuleContext(const Context *parent = NULL, const EvalContext *evalctx = NULL); virtual ~ModuleContext(); - void setModule(const Module &module, const EvalContext *evalctx = NULL); + void initializeModule(const Module &m); void registerBuiltin(); + virtual Value evaluate_function(const std::string &name, + const EvalContext *evalctx) const; + virtual AbstractNode *instantiate_module(const ModuleInstantiation &inst, + const EvalContext *evalctx) const; - virtual Value evaluate_function(const std::string &name, const EvalContext *evalctx) const; - virtual AbstractNode *evaluate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) const; + const AbstractModule *findLocalModule(const std::string &name) const; + const AbstractFunction *findLocalFunction(const std::string &name) const; - const boost::unordered_map<std::string, class AbstractFunction*> *functions_p; - const boost::unordered_map<std::string, class AbstractModule*> *modules_p; - typedef boost::unordered_map<std::string, class Module*> ModuleContainer; - const ModuleContainer *usedlibs_p; + const LocalScope::FunctionContainer *functions_p; + const LocalScope::AbstractModuleContainer *modules_p; // FIXME: Points to the eval context for the call to this module. Not sure where it belongs const class EvalContext *evalctx; @@ -37,4 +40,17 @@ public: mutable boost::unordered_map<std::string, int> recursioncount; }; +class FileContext : public ModuleContext +{ +public: + FileContext(const class FileModule &module, const Context *parent); + virtual ~FileContext() {} + virtual Value evaluate_function(const std::string &name, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate_module(const ModuleInstantiation &inst, + const EvalContext *evalctx) const; + +private: + const FileModule::ModuleContainer &usedlibs; +}; + #endif diff --git a/src/module.cc b/src/module.cc index 7d83975..9503f05 100644 --- a/src/module.cc +++ b/src/module.cc @@ -44,11 +44,11 @@ AbstractModule::~AbstractModule() { } -AbstractNode *AbstractModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *AbstractModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { AbstractNode *node = new AbstractNode(inst); - node->children = inst->evaluateChildren(evalctx); + node->children = inst->instantiateChildren(evalctx); return node; } @@ -63,12 +63,10 @@ std::string AbstractModule::dump(const std::string &indent, const std::string &n ModuleInstantiation::~ModuleInstantiation() { BOOST_FOREACH(const Assignment &arg, this->arguments) delete arg.second; - BOOST_FOREACH(ModuleInstantiation *v, children) delete v; } IfElseModuleInstantiation::~IfElseModuleInstantiation() { - BOOST_FOREACH (ModuleInstantiation *v, else_children) delete v; } /*! @@ -95,83 +93,58 @@ std::string ModuleInstantiation::dump(const std::string &indent) const if (!arg.first.empty()) dump << arg.first << " = "; dump << *arg.second; } - if (children.size() == 0) { + if (scope.numElements() == 0) { dump << ");\n"; - } else if (children.size() == 1) { + } else if (scope.numElements() == 1) { dump << ")\n"; - dump << children[0]->dump(indent + "\t"); + dump << scope.dump(indent + "\t"); } else { dump << ") {\n"; - for (size_t i = 0; i < children.size(); i++) { - dump << children[i]->dump(indent + "\t"); - } + scope.dump(indent + "\t"); dump << indent << "}\n"; } return dump.str(); } -AbstractNode *ModuleInstantiation::evaluate_instance(const Context *ctx) const +AbstractNode *ModuleInstantiation::evaluate(const Context *ctx) const { - EvalContext c(ctx); - BOOST_FOREACH(const Assignment &arg, this->arguments) { - c.eval_arguments.push_back(std::make_pair(arg.first, - arg.second ? - arg.second->evaluate(ctx) : - Value())); - } - c.children = this->children; + EvalContext c(ctx, this->arguments, &this->scope); #if 0 && DEBUG PRINT("New eval ctx:"); c.dump(NULL, this); #endif - AbstractNode *node = ctx->evaluate_module(*this, &c); // Passes c as evalctx + AbstractNode *node = ctx->instantiate_module(*this, &c); // Passes c as evalctx return node; } -std::vector<AbstractNode*> ModuleInstantiation::evaluateChildren(const Context *evalctx) const +std::vector<AbstractNode*> ModuleInstantiation::instantiateChildren(const Context *evalctx) const { - std::vector<AbstractNode*> childnodes; - BOOST_FOREACH (ModuleInstantiation *modinst, this->children) { - AbstractNode *node = modinst->evaluate_instance(evalctx); - if (node) childnodes.push_back(node); - } - return childnodes; + return this->scope.instantiateChildren(evalctx); } -std::vector<AbstractNode*> IfElseModuleInstantiation::evaluateElseChildren(const Context *evalctx) const +std::vector<AbstractNode*> IfElseModuleInstantiation::instantiateElseChildren(const Context *evalctx) const { - std::vector<AbstractNode*> childnodes; - BOOST_FOREACH (ModuleInstantiation *modinst, this->else_children) { - AbstractNode *node = modinst->evaluate_instance(evalctx); - if (node != NULL) childnodes.push_back(node); - } - return childnodes; + return this->else_scope.instantiateChildren(evalctx); } Module::~Module() { - BOOST_FOREACH (const Assignment &v, assignments) delete v.second; - BOOST_FOREACH (FunctionContainer::value_type &f, functions) delete f.second; - BOOST_FOREACH (AbstractModuleContainer::value_type &m, modules) delete m.second; - BOOST_FOREACH (ModuleInstantiation *v, children) delete v; } -AbstractNode *Module::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *Module::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { - ModuleContext c(this, ctx, evalctx); + ModuleContext c(ctx, evalctx); + c.initializeModule(*this); + c.set_variable("$children", Value(double(inst->scope.children.size()))); // FIXME: Set document path to the path of the module - c.set_variable("$children", Value(double(inst->children.size()))); #if 0 && DEBUG c.dump(this, inst); #endif AbstractNode *node = new AbstractNode(inst); - for (size_t i = 0; i < children.size(); i++) { - AbstractNode *n = children[i]->evaluate_instance(&c); - if (n != NULL) - node->children.push_back(n); - } + std::vector<AbstractNode *> instantiatednodes = this->scope.instantiateChildren(&c); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); return node; } @@ -191,25 +164,14 @@ std::string Module::dump(const std::string &indent, const std::string &name) con dump << ") {\n"; tab = "\t"; } - BOOST_FOREACH(const FunctionContainer::value_type &f, functions) { - dump << f.second->dump(indent + tab, f.first); - } - BOOST_FOREACH(const AbstractModuleContainer::value_type &m, modules) { - dump << m.second->dump(indent + tab, m.first); - } - BOOST_FOREACH(const std::string &var, assignments_var) { - dump << indent << tab << var << " = " << *assignments.at(var) << ";\n"; - } - for (size_t i = 0; i < children.size(); i++) { - dump << children[i]->dump(indent + tab); - } + dump << scope.dump(indent + tab); if (!name.empty()) { dump << indent << "}\n"; } return dump.str(); } -void Module::registerInclude(const std::string &filename) +void FileModule::registerInclude(const std::string &filename) { struct stat st; memset(&st, 0, sizeof(struct stat)); @@ -221,17 +183,17 @@ void Module::registerInclude(const std::string &filename) Check if any dependencies have been modified and recompile them. Returns true if anything was recompiled. */ -bool Module::handleDependencies() +bool FileModule::handleDependencies() { if (this->is_handling_dependencies) return false; this->is_handling_dependencies = true; bool changed = false; // Iterating manually since we want to modify the container while iterating - Module::ModuleContainer::iterator iter = this->usedlibs.begin(); + FileModule::ModuleContainer::iterator iter = this->usedlibs.begin(); while (iter != this->usedlibs.end()) { - Module::ModuleContainer::iterator curr = iter++; - Module *oldmodule = curr->second; + FileModule::ModuleContainer::iterator curr = iter++; + FileModule *oldmodule = curr->second; curr->second = ModuleCache::instance()->evaluate(curr->first); if (curr->second != oldmodule) { changed = true; @@ -248,3 +210,20 @@ bool Module::handleDependencies() this->is_handling_dependencies = false; return changed; } + +AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +{ + assert(evalctx == NULL); + FileContext c(*this, ctx); + c.initializeModule(*this); + // FIXME: Set document path to the path of the module +#if 0 && DEBUG + c.dump(this, inst); +#endif + + AbstractNode *node = new AbstractNode(inst); + std::vector<AbstractNode *> instantiatednodes = this->scope.instantiateChildren(ctx, &c); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); + + return node; +} diff --git a/src/module.h b/src/module.h index ace3c1b..8f1ccb7 100644 --- a/src/module.h +++ b/src/module.h @@ -7,6 +7,7 @@ #include <boost/unordered_map.hpp> #include "value.h" #include "typedefs.h" +#include "localscope.h" class ModuleInstantiation { @@ -16,8 +17,8 @@ public: virtual ~ModuleInstantiation(); std::string dump(const std::string &indent) const; - class AbstractNode *evaluate_instance(const class Context *ctx) const; - std::vector<AbstractNode*> evaluateChildren(const Context *evalctx) const; + class AbstractNode *evaluate(const class Context *ctx) const; + std::vector<AbstractNode*> instantiateChildren(const Context *evalctx) const; void setPath(const std::string &path) { this->modpath = path; } const std::string &path() const { return this->modpath; } @@ -29,7 +30,7 @@ public: bool isRoot() const { return this->tag_root; } AssignmentList arguments; - std::vector<ModuleInstantiation*> children; + LocalScope scope; bool tag_root; bool tag_highlight; @@ -45,57 +46,54 @@ class IfElseModuleInstantiation : public ModuleInstantiation { public: IfElseModuleInstantiation() : ModuleInstantiation("if") { } virtual ~IfElseModuleInstantiation(); - std::vector<AbstractNode*> evaluateElseChildren(const Context *evalctx) const; + std::vector<AbstractNode*> instantiateElseChildren(const Context *evalctx) const; - std::vector<ModuleInstantiation*> else_children; + LocalScope else_scope; }; class AbstractModule { public: virtual ~AbstractModule(); - virtual class AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const class EvalContext *evalctx = NULL) const; + virtual class AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const class EvalContext *evalctx = NULL) const; virtual std::string dump(const std::string &indent, const std::string &name) const; }; class Module : public AbstractModule { public: - Module() : is_handling_dependencies(false) { } + Module() { } virtual ~Module(); - void setModulePath(const std::string &path) { this->path = path; } - const std::string &modulePath() const { return this->path; } - - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx = NULL) const; virtual std::string dump(const std::string &indent, const std::string &name) const; - void addChild(ModuleInstantiation *ch) { this->children.push_back(ch); } - - typedef boost::unordered_map<std::string, class Module*> ModuleContainer; - ModuleContainer usedlibs; - void registerInclude(const std::string &filename); - typedef boost::unordered_map<std::string, time_t> IncludeContainer; - IncludeContainer includes; - bool is_handling_dependencies; - bool handleDependencies(); - - std::list<std::string> assignments_var; - typedef boost::unordered_map<std::string, Expression*> AssignmentMap; - AssignmentMap assignments; + AssignmentList definition_arguments; - typedef boost::unordered_map<std::string, class AbstractFunction*> FunctionContainer; - FunctionContainer functions; - typedef boost::unordered_map<std::string, AbstractModule*> AbstractModuleContainer; - AbstractModuleContainer modules; + LocalScope scope; +}; - std::vector<ModuleInstantiation*> children; +// FIXME: A FileModule doesn't have definition arguments, so we shouldn't really +// inherit from a Module +class FileModule : public Module +{ +public: + FileModule() : is_handling_dependencies(false) {} + virtual ~FileModule() {} - std::vector<Assignment> definition_arguments; + void setModulePath(const std::string &path) { this->path = path; } + const std::string &modulePath() const { return this->path; } + void registerInclude(const std::string &filename); + bool handleDependencies(); + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx = NULL) const; -protected: + typedef boost::unordered_map<std::string, class FileModule*> ModuleContainer; + ModuleContainer usedlibs; + typedef boost::unordered_map<std::string, time_t> IncludeContainer; + IncludeContainer includes; private: + bool is_handling_dependencies; std::string path; }; diff --git a/src/openscad.cc b/src/openscad.cc index 3b960f3..6a0d057 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -327,7 +327,15 @@ int main(int argc, char **argv) if (!filename) help(argv[0]); - Module *root_module; + // Top context - this context only holds builtins + ModuleContext top_ctx; + top_ctx.registerBuiltin(); + PRINT("Root Context:"); +#if 0 && DEBUG + top_ctx.dump(NULL, NULL); +#endif + + FileModule *root_module; ModuleInstantiation root_inst("group"); AbstractNode *root_node; AbstractNode *absolute_root_node; @@ -348,18 +356,12 @@ int main(int argc, char **argv) if (!root_module) exit(1); root_module->handleDependencies(); - ModuleContext root_ctx; - root_ctx.registerBuiltin(); - PRINT("Root Context:"); -#if 0 && DEBUG - root_ctx.dump(NULL, NULL); -#endif fs::path fpath = boosty::absolute(fs::path(filename)); fs::path fparent = fpath.parent_path(); fs::current_path(fparent); AbstractNode::resetIndexCounter(); - absolute_root_node = root_module->evaluate(&root_ctx, &root_inst, NULL); + absolute_root_node = root_module->instantiate(&top_ctx, &root_inst, NULL); // Do we have an explicit root node (! modifier)? if (!(root_node = find_root_tag(absolute_root_node))) diff --git a/src/openscad.h b/src/openscad.h index 8d9a01e..dd379a9 100644 --- a/src/openscad.h +++ b/src/openscad.h @@ -27,7 +27,7 @@ #ifndef OPENSCAD_H #define OPENSCAD_H -extern class Module *parse(const char *text, const char *path, int debug); +extern class FileModule *parse(const char *text, const char *path, int debug); extern int get_fragments_from_r(double r, double fn, double fs, double fa); #include <string> diff --git a/src/parser.y b/src/parser.y index fd6b164..272f801 100644 --- a/src/parser.y +++ b/src/parser.y @@ -56,8 +56,8 @@ int lexerlex_destroy(void); int lexerlex(void); - std::vector<Module*> module_stack; - Module *currmodule; + std::stack<LocalScope *> scope_stack; + FileModule *rootmodule; extern void lexerdestroy(); extern FILE *lexerin; @@ -73,7 +73,6 @@ class Value *value; class Expression *expr; class ModuleInstantiation *inst; - std::vector<ModuleInstantiation*> *instvec; class IfElseModuleInstantiation *ifelse; Assignment *arg; AssignmentList *args; @@ -114,8 +113,6 @@ %type <inst> module_instantiation %type <ifelse> if_statement %type <ifelse> ifelse_statement -%type <instvec> children_instantiation -%type <instvec> module_instantiation_list %type <inst> single_module_instantiation %type <args> arguments_call @@ -130,91 +127,73 @@ input: /* empty */ | -TOK_USE { currmodule->usedlibs[$1] = NULL; } input | +TOK_USE { rootmodule->usedlibs[$1] = NULL; } input | statement input ; inner_input: /* empty */ | statement inner_input ; +assignment: +TOK_ID '=' expr ';' { + for (AssignmentList::iterator iter = scope_stack.top()->assignments.begin(); + iter != scope_stack.top()->assignments.end(); + iter++) { + if (iter->first == $1) { + scope_stack.top()->assignments.erase(iter); + break; + } + } + scope_stack.top()->assignments.push_back(Assignment($1, $3)); +} ; + statement: ';' | '{' inner_input '}' | module_instantiation { - if ($1) { - currmodule->addChild($1); - } else { - delete $1; - } -} | -TOK_ID '=' expr ';' { - std::list<std::string>::iterator found = std::find(currmodule->assignments_var.begin(), currmodule->assignments_var.end(),$1); - if (found != currmodule->assignments_var.end()) currmodule->assignments_var.erase(found); - currmodule->assignments_var.push_back($1); - currmodule->assignments[$1] = $3; + if ($1) scope_stack.top()->addChild($1); } | +assignment | TOK_MODULE TOK_ID '(' arguments_decl optional_commas ')' { - Module *p = currmodule; - module_stack.push_back(currmodule); - currmodule = new Module(); - p->modules[$2] = currmodule; - currmodule->definition_arguments = *$4; + Module *newmodule = new Module(); + newmodule->definition_arguments = *$4; + scope_stack.top()->modules[$2] = newmodule; + scope_stack.push(&newmodule->scope); free($2); delete $4; } statement { - currmodule = module_stack.back(); - module_stack.pop_back(); + scope_stack.pop(); } | TOK_FUNCTION TOK_ID '(' arguments_decl optional_commas ')' '=' expr { Function *func = new Function(); func->definition_arguments = *$4; func->expr = $8; - currmodule->functions[$2] = func; + scope_stack.top()->functions[$2] = func; free($2); delete $4; } ';' ; -/* Will return a dummy parent node with zero or more children */ -children_instantiation: -module_instantiation { - $$ = new std::vector<ModuleInstantiation*>; - if ($1) { - $$->push_back($1); - } -} | -'{' module_instantiation_list '}' { - $$ = $2; -} ; - if_statement: -TOK_IF '(' expr ')' children_instantiation { - $$ = new IfElseModuleInstantiation(); - $$->arguments.push_back(Assignment("", $3)); - $$->setPath(parser_source_path); - - if ($$) { - $$->children = *$5; - } else { - for (size_t i = 0; i < $5->size(); i++) - delete (*$5)[i]; - } - delete $5; -} ; +TOK_IF '(' expr ')' { + $<ifelse>if = new IfElseModuleInstantiation(); + $<ifelse>if->arguments.push_back(Assignment("", $3)); + $<ifelse>if->setPath(parser_source_path); + scope_stack.push(&$<ifelse>if->scope); +}[if] child_statement { + scope_stack.pop(); + $$ = $<ifelse>if; + } ; ifelse_statement: if_statement { $$ = $1; } | -if_statement TOK_ELSE children_instantiation { +if_statement TOK_ELSE { + scope_stack.push(&$1->else_scope); +}[else] child_statement { + scope_stack.pop(); $$ = $1; - if ($$) { - $$->else_children = *$3; - } else { - for (size_t i = 0; i < $3->size(); i++) - delete (*$3)[i]; - } - delete $3; -} ; + } ; module_instantiation: '!' module_instantiation { @@ -233,35 +212,28 @@ module_instantiation: delete $2; $$ = NULL; } | -single_module_instantiation ';' { - $$ = $1; -} | -single_module_instantiation children_instantiation { - $$ = $1; - if ($$) { - $$->children = *$2; - } else { - for (size_t i = 0; i < $2->size(); i++) - delete (*$2)[i]; - } - delete $2; +single_module_instantiation { + $<inst>inst = $1; + scope_stack.push(&$<inst>inst->scope); +}[inst] child_statement { + scope_stack.pop(); + $$ = $<inst>inst; + } | + ifelse_statement { + $$ = $1; + } ; + +child_statement: +';' | +'{' child_statements '}' | +module_instantiation { + if ($1) scope_stack.top()->addChild($1); } | -ifelse_statement { - $$ = $1; -} ; +assignment ; -module_instantiation_list: -/* empty */ { - $$ = new std::vector<ModuleInstantiation*>; -} | -module_instantiation_list module_instantiation { - $$ = $1; - if ($$) { - if ($2) $$->push_back($2); - } else { - delete $2; - } -} ; +child_statements: +/* empty */ | +child_statements child_statement ; single_module_instantiation: TOK_ID '(' arguments_call ')' { @@ -289,9 +261,7 @@ TOK_ID { free($1); } | expr '.' TOK_ID { - $$ = new Expression(); - $$->type = "N"; - $$->children.push_back($1); + $$ = new Expression("N", $1); $$->var_name = $3; free($3); } | @@ -324,95 +294,52 @@ TOK_NUMBER { $$ = $2; } | expr '*' expr { - $$ = new Expression(); - $$->type = "*"; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression("*", $1, $3); } | expr '/' expr { - $$ = new Expression(); - $$->type = "/"; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression("/", $1, $3); } | expr '%' expr { - $$ = new Expression(); - $$->type = "%"; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression("%", $1, $3); } | expr '+' expr { - $$ = new Expression(); - $$->type = "+"; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression("+", $1, $3); } | expr '-' expr { - $$ = new Expression(); - $$->type = "-"; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression("-", $1, $3); } | expr '<' expr { - $$ = new Expression(); - $$->type = "<"; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression("<", $1, $3); } | expr LE expr { - $$ = new Expression(); - $$->type = "<="; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression("<=", $1, $3); } | expr EQ expr { - $$ = new Expression(); - $$->type = "=="; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression("==", $1, $3); } | expr NE expr { - $$ = new Expression(); - $$->type = "!="; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression("!=", $1, $3); } | expr GE expr { - $$ = new Expression(); - $$->type = ">="; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression(">=", $1, $3); } | expr '>' expr { - $$ = new Expression(); - $$->type = ">"; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression(">", $1, $3); } | expr AND expr { - $$ = new Expression(); - $$->type = "&&"; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression("&&", $1, $3); } | expr OR expr { - $$ = new Expression(); - $$->type = "||"; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression("||", $1, $3); } | '+' expr { $$ = $2; } | '-' expr { - $$ = new Expression(); - $$->type = "I"; - $$->children.push_back($2); + $$ = new Expression("I", $2); } | '!' expr { - $$ = new Expression(); - $$->type = "!"; - $$->children.push_back($2); + $$ = new Expression("!", $2); } | '(' expr ')' { $$ = $2; @@ -425,10 +352,7 @@ expr '?' expr ':' expr { $$->children.push_back($5); } | expr '[' expr ']' { - $$ = new Expression(); - $$->type = "[]"; - $$->children.push_back($1); - $$->children.push_back($3); + $$ = new Expression("[]", $1, $3); } | TOK_ID '(' arguments_call ')' { $$ = new Expression(); @@ -444,9 +368,7 @@ optional_commas: vector_expr: expr { - $$ = new Expression(); - $$->type = 'V'; - $$->children.push_back($1); + $$ = new Expression("V", $1); } | vector_expr ',' optional_commas expr { $$ = $1; @@ -513,19 +435,18 @@ void yyerror (char const *s) { // FIXME: We leak memory on parser errors... PRINTB("Parser error in line %d: %s\n", lexerget_lineno() % s); - currmodule = NULL; } -Module *parse(const char *text, const char *path, int debug) +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(); - module_stack.clear(); - Module *rootmodule = currmodule = new Module(); + rootmodule = new FileModule(); rootmodule->setModulePath(path); + scope_stack.push(&rootmodule->scope); // PRINTB_NOCACHE("New module: %s %p", "root" % rootmodule); parserdebug = debug; @@ -536,5 +457,6 @@ Module *parse(const char *text, const char *path, int debug) if (parserretval != 0) return NULL; parser_error_pos = -1; + scope_stack.pop(); return rootmodule; } diff --git a/src/primitives.cc b/src/primitives.cc index 184b40c..be1ec6f 100644 --- a/src/primitives.cc +++ b/src/primitives.cc @@ -56,7 +56,7 @@ class PrimitiveModule : public AbstractModule public: primitive_type_e type; PrimitiveModule(primitive_type_e type) : type(type) { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; }; class PrimitiveNode : public AbstractPolyNode @@ -105,7 +105,7 @@ public: virtual PolySet *evaluate_polyset(class PolySetEvaluator *) const; }; -AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { PrimitiveNode *node = new PrimitiveNode(inst, this->type); @@ -137,7 +137,7 @@ AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstanti args += Assignment("points", NULL), Assignment("paths", NULL), Assignment("convexity", NULL); break; default: - assert(false && "PrimitiveModule::evaluate(): Unknown node type"); + assert(false && "PrimitiveModule::instantiate(): Unknown node type"); } Context c(ctx); diff --git a/src/projection.cc b/src/projection.cc index eeed411..8d8cee6 100644 --- a/src/projection.cc +++ b/src/projection.cc @@ -41,10 +41,10 @@ class ProjectionModule : public AbstractModule { public: ProjectionModule() { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; }; -AbstractNode *ProjectionModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *ProjectionModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { ProjectionNode *node = new ProjectionNode(inst); @@ -62,8 +62,8 @@ AbstractNode *ProjectionModule::evaluate(const Context *ctx, const ModuleInstant if (cut.type() == Value::BOOL) node->cut_mode = cut.toBool(); - std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx); - node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); + std::vector<AbstractNode *> instantiatednodes = inst->instantiateChildren(evalctx); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); return node; } diff --git a/src/render.cc b/src/render.cc index bb08c0c..5097661 100644 --- a/src/render.cc +++ b/src/render.cc @@ -38,10 +38,10 @@ class RenderModule : public AbstractModule { public: RenderModule() { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; }; -AbstractNode *RenderModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *RenderModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { RenderNode *node = new RenderNode(inst); @@ -55,8 +55,8 @@ AbstractNode *RenderModule::evaluate(const Context *ctx, const ModuleInstantiati if (v.type() == Value::NUMBER) node->convexity = (int)v.toDouble(); - std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx); - node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); + std::vector<AbstractNode *> instantiatednodes = inst->instantiateChildren(evalctx); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); return node; } diff --git a/src/rotateextrude.cc b/src/rotateextrude.cc index b6edb8b..2f9a28b 100644 --- a/src/rotateextrude.cc +++ b/src/rotateextrude.cc @@ -45,10 +45,10 @@ class RotateExtrudeModule : public AbstractModule { public: RotateExtrudeModule() { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; }; -AbstractNode *RotateExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *RotateExtrudeModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { RotateExtrudeNode *node = new RotateExtrudeNode(inst); @@ -85,8 +85,8 @@ AbstractNode *RotateExtrudeModule::evaluate(const Context *ctx, const ModuleInst node->scale = 1; if (node->filename.empty()) { - std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx); - node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); + std::vector<AbstractNode *> instantiatednodes = inst->instantiateChildren(evalctx); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); } return node; diff --git a/src/surface.cc b/src/surface.cc index d8fa422..b3246c1 100644 --- a/src/surface.cc +++ b/src/surface.cc @@ -50,7 +50,7 @@ class SurfaceModule : public AbstractModule { public: SurfaceModule() { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; }; class SurfaceNode : public AbstractPolyNode @@ -69,7 +69,7 @@ public: virtual PolySet *evaluate_polyset(class PolySetEvaluator *) const; }; -AbstractNode *SurfaceModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *SurfaceModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { SurfaceNode *node = new SurfaceNode(inst); node->center = false; diff --git a/src/transform.cc b/src/transform.cc index cc026fb..ddf222a 100644 --- a/src/transform.cc +++ b/src/transform.cc @@ -50,10 +50,10 @@ class TransformModule : public AbstractModule public: transform_type_e type; TransformModule(transform_type_e type) : type(type) { } - virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; }; -AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const { TransformNode *node = new TransformNode(inst); @@ -175,8 +175,8 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti } } - std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx); - node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end()); + std::vector<AbstractNode *> instantiatednodes = inst->instantiateChildren(evalctx); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); return node; } diff --git a/src/typedefs.h b/src/typedefs.h index a6e9077..fd676e2 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -6,5 +6,6 @@ typedef std::pair<std::string, class Expression*> Assignment; typedef std::vector<Assignment> AssignmentList; +typedef std::vector<class ModuleInstantiation*> ModuleInstantiationList; #endif diff --git a/testdata/scad/features/child-tests.scad b/testdata/scad/features/child-tests.scad index e4e3572..cf983b4 100644 --- a/testdata/scad/features/child-tests.scad +++ b/testdata/scad/features/child-tests.scad @@ -1,7 +1,7 @@ $fn=16; -module parent() { - for (i=[0:2]) { +module parent(range=[0:2]) { + for (i=range) { translate([2.5*i,0,0]) child(i); } } @@ -32,3 +32,6 @@ module parent3() { } translate([5,3,0]) parent3() { cube(); sphere(); } + +// Leaking variables to child list is not allowed +translate([0,6,0]) parent(range=[0:1], testvar=10) { sphere(); cube(testvar, center=true);} diff --git a/testdata/scad/misc/localfiles_dir/localfiles_module.scad b/testdata/scad/misc/localfiles_dir/localfiles_module.scad index b98a49b..2611e71 100644 --- a/testdata/scad/misc/localfiles_dir/localfiles_module.scad +++ b/testdata/scad/misc/localfiles_dir/localfiles_module.scad @@ -5,6 +5,5 @@ module localfiles_module() translate([0,350,0]) rotate_extrude(file="localfile.dxf"); translate([250,0,0]) scale([200,200,50]) surface("localfile.dat"); - // This is not supported: - // echo(dxf_dim(file="localfile.dxf", name="localfile")); + translate([0,-200,0]) sphere(r=dxf_dim(file="localfile.dxf", name="localfile")/2); } diff --git a/testdata/scad/misc/variable-scope-sub.scad b/testdata/scad/misc/variable-scope-sub.scad new file mode 100644 index 0000000..fda9520 --- /dev/null +++ b/testdata/scad/misc/variable-scope-sub.scad @@ -0,0 +1,24 @@ +sub_global = 15; + +module submodule() { + echo($children); + echo(submodule_var); + submodule_var = 16; + module subsubmodule() { + echo($children); + subsubmodule_var = 17; + echo(subsubmodule_var); + child(0); + } + subsubmodule() {child(0); sphere();} +} + +module submodule2() { + echo(sub_global); + echo($children); +} + +module submain() { + echo(global_var); // Undefined global var + submodule() {submodule2() sphere(); cube();} +} diff --git a/testdata/scad/misc/variable-scope-tests.scad b/testdata/scad/misc/variable-scope-tests.scad new file mode 100644 index 0000000..8426fbb --- /dev/null +++ b/testdata/scad/misc/variable-scope-tests.scad @@ -0,0 +1,53 @@ +echo("special variable inheritance"); +module special_module(a) { + echo(a, $fn); + special_module2(a); +} + +module special_module2(b) { + echo(a); + echo(b, $fn); +} + +special_module(23, $fn=5); + +echo("inner variables shadows parameter"); +module inner_variables(a, b) { + b = 24; + echo(a, b); +} + +inner_variables(5, 6); + +echo("user-defined special variables as parameter"); +module user_defined_special($b) { + echo($b); + user_defined_special2(); +} + +module user_defined_special2() { + echo($b); +} + +user_defined_special(7); + +echo("assign only visible in children's scope"); +module assigning() { + echo(c); +} + +module assigning2(c) { + echo(c); +} + +assign(c=5) { + assigning(); + assigning2(c); +} + +echo("undeclared variable can still be passed and used"); +module undeclared_var() { + echo(d); +} + +undeclared_var(d=6); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index de4bab7..51abd06 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,7 +13,11 @@ include(CMakeParseArguments.cmake) # Detect Lion and force gcc IF (APPLE) EXECUTE_PROCESS(COMMAND sw_vers -productVersion OUTPUT_VARIABLE MACOSX_VERSION) - IF (NOT ${MACOSX_VERSION} VERSION_LESS "10.7.0") + IF (NOT ${MACOSX_VERSION} VERSION_LESS "10.8.0") + message("Detected Mountain Lion or later") + set(CMAKE_C_COMPILER "gcc") + set(CMAKE_CXX_COMPILER "g++") + ELSEIF (NOT ${MACOSX_VERSION} VERSION_LESS "10.7.0") message("Detected Lion or later") set(CMAKE_C_COMPILER "gcc") set(CMAKE_CXX_COMPILER "g++") @@ -325,9 +329,8 @@ if (WIN32) set(FLEX_UNISTD_FLAG "-DYY_NO_UNISTD_H") endif() FLEX_TARGET(OpenSCADlexer ../src/lexer.l ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp COMPILE_FLAGS "-Plexer ${FLEX_UNISTD_FLAG}") -BISON_TARGET(OpenSCADparser ../src/parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser_yacc.c COMPILE_FLAGS "-p parser") +BISON_TARGET(OpenSCADparser ../src/parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp COMPILE_FLAGS "-p parser") ADD_FLEX_BISON_DEPENDENCY(OpenSCADlexer OpenSCADparser) -set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/parser_yacc.c PROPERTIES LANGUAGE "CXX") # CGAL @@ -402,6 +405,7 @@ set(CORE_SOURCES ../src/value.cc ../src/expr.cc ../src/func.cc + ../src/localscope.cc ../src/module.cc ../src/ModuleCache.cc ../src/node.cc @@ -751,7 +755,8 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES} ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/vector-values.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/recursion-tests.scad - ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests.scad) + ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests.scad + ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/variable-scope-tests.scad) list(APPEND DUMPTEST_FILES ${MINIMAL_FILES} ${FEATURES_FILES} ${EXAMPLE_FILES}) list(APPEND DUMPTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test.scad diff --git a/tests/cgalcachetest.cc b/tests/cgalcachetest.cc index b65a2c8..67d3313 100644 --- a/tests/cgalcachetest.cc +++ b/tests/cgalcachetest.cc @@ -129,10 +129,10 @@ int main(int argc, char **argv) parser_init(boosty::stringy(fs::path(argv[0]).branch_path())); add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); - ModuleContext root_ctx; - root_ctx.registerBuiltin(); + ModuleContext top_ctx; + top_ctx.registerBuiltin(); - AbstractModule *root_module; + FileModule *root_module; ModuleInstantiation root_inst("group"); root_module = parsefile(filename); @@ -145,7 +145,7 @@ int main(int argc, char **argv) } AbstractNode::resetIndexCounter(); - AbstractNode *absolute_root_node = root_module->evaluate(&root_ctx, &root_inst); + AbstractNode *absolute_root_node = root_module->instantiate(&top_ctx, &root_inst); AbstractNode *root_node; // Do we have an explicit root node (! modifier)? if (!(root_node = find_root_tag(absolute_root_node))) root_node = absolute_root_node; diff --git a/tests/cgalpngtest.cc b/tests/cgalpngtest.cc index afc3128..81fc6b4 100644 --- a/tests/cgalpngtest.cc +++ b/tests/cgalpngtest.cc @@ -102,10 +102,10 @@ int main(int argc, char **argv) parser_init(boosty::stringy(fs::path(argv[0]).branch_path())); add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); - ModuleContext root_ctx; - root_ctx.registerBuiltin(); + ModuleContext top_ctx; + top_ctx.registerBuiltin(); - AbstractModule *root_module; + FileModule *root_module; ModuleInstantiation root_inst("group"); root_module = parsefile(filename); @@ -118,7 +118,7 @@ int main(int argc, char **argv) } AbstractNode::resetIndexCounter(); - AbstractNode *absolute_root_node = root_module->evaluate(&root_ctx, &root_inst); + AbstractNode *absolute_root_node = root_module->instantiate(&top_ctx, &root_inst); AbstractNode *root_node; // Do we have an explicit root node (! modifier)? if (!(root_node = find_root_tag(absolute_root_node))) root_node = absolute_root_node; diff --git a/tests/cgalstlsanitytest.cc b/tests/cgalstlsanitytest.cc index 49a3f8e..4be7767 100644 --- a/tests/cgalstlsanitytest.cc +++ b/tests/cgalstlsanitytest.cc @@ -84,10 +84,10 @@ int main(int argc, char **argv) parser_init(boosty::stringy(fs::path(argv[0]).branch_path())); add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); - ModuleContext root_ctx; - root_ctx.registerBuiltin(); + ModuleContext top_ctx; + top_ctx.registerBuiltin(); - AbstractModule *root_module; + FileModule *root_module; ModuleInstantiation root_inst("group"); root_module = parsefile(filename); @@ -100,7 +100,7 @@ int main(int argc, char **argv) } AbstractNode::resetIndexCounter(); - AbstractNode *absolute_root_node = root_module->evaluate(&root_ctx, &root_inst); + AbstractNode *absolute_root_node = root_module->instantiate(&top_ctx, &root_inst); AbstractNode *root_node; // Do we have an explicit root node (! modifier)? if (!(root_node = find_root_tag(absolute_root_node))) root_node = absolute_root_node; diff --git a/tests/cgaltest.cc b/tests/cgaltest.cc index b7ae669..d750da9 100644 --- a/tests/cgaltest.cc +++ b/tests/cgaltest.cc @@ -81,10 +81,10 @@ int main(int argc, char **argv) parser_init(boosty::stringy(fs::path(argv[0]).branch_path())); add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); - ModuleContext root_ctx; - root_ctx.registerBuiltin(); + ModuleContext top_ctx; + top_ctx.registerBuiltin(); - AbstractModule *root_module; + FileModule *root_module; ModuleInstantiation root_inst("group"); root_module = parsefile(filename); @@ -97,7 +97,7 @@ int main(int argc, char **argv) } AbstractNode::resetIndexCounter(); - AbstractNode *absolute_root_node = root_module->evaluate(&root_ctx, &root_inst); + AbstractNode *absolute_root_node = root_module->instantiate(&top_ctx, &root_inst); AbstractNode *root_node; // Do we have an explicit root node (! modifier)? if (!(root_node = find_root_tag(absolute_root_node))) root_node = absolute_root_node; diff --git a/tests/csgtermtest.cc b/tests/csgtermtest.cc index f4a88e0..1460bbd 100644 --- a/tests/csgtermtest.cc +++ b/tests/csgtermtest.cc @@ -76,10 +76,10 @@ int main(int argc, char **argv) parser_init(boosty::stringy(fs::path(argv[0]).branch_path())); add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); - ModuleContext root_ctx; - root_ctx.registerBuiltin(); + ModuleContext top_ctx; + top_ctx.registerBuiltin(); - AbstractModule *root_module; + FileModule *root_module; ModuleInstantiation root_inst("group"); const AbstractNode *root_node; @@ -93,7 +93,7 @@ int main(int argc, char **argv) } AbstractNode::resetIndexCounter(); - root_node = root_module->evaluate(&root_ctx, &root_inst); + root_node = root_module->instantiate(&top_ctx, &root_inst); Tree tree(root_node); diff --git a/tests/csgtestcore.cc b/tests/csgtestcore.cc index 6da6411..320b533 100644 --- a/tests/csgtestcore.cc +++ b/tests/csgtestcore.cc @@ -132,10 +132,10 @@ int csgtestcore(int argc, char *argv[], test_type_e test_type) parser_init(boosty::stringy(fs::path(argv[0]).branch_path())); add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); - ModuleContext root_ctx; - root_ctx.registerBuiltin(); + ModuleContext top_ctx; + top_ctx.registerBuiltin(); - AbstractModule *root_module; + FileModule *root_module; ModuleInstantiation root_inst("group"); if (sysinfo_dump) @@ -154,7 +154,7 @@ int csgtestcore(int argc, char *argv[], test_type_e test_type) } AbstractNode::resetIndexCounter(); - AbstractNode *absolute_root_node = root_module->evaluate(&root_ctx, &root_inst); + AbstractNode *absolute_root_node = root_module->instantiate(&top_ctx, &root_inst); AbstractNode *root_node; // Do we have an explicit root node (! modifier)? if (!(root_node = find_root_tag(absolute_root_node))) root_node = absolute_root_node; diff --git a/tests/csgtexttest.cc b/tests/csgtexttest.cc index 3e26814..97902f6 100644 --- a/tests/csgtexttest.cc +++ b/tests/csgtexttest.cc @@ -80,10 +80,10 @@ int main(int argc, char **argv) parser_init(boosty::stringy(fs::path(argv[0]).branch_path())); add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); - ModuleContext root_ctx; - root_ctx.registerBuiltin(); + ModuleContext top_ctx; + top_ctx.registerBuiltin(); - AbstractModule *root_module; + FileModule *root_module; ModuleInstantiation root_inst("group"); AbstractNode *root_node; @@ -97,7 +97,7 @@ int main(int argc, char **argv) } AbstractNode::resetIndexCounter(); - root_node = root_module->evaluate(&root_ctx, &root_inst); + root_node = root_module->instantiate(&top_ctx, &root_inst); Tree tree; tree.setRoot(root_node); diff --git a/tests/dumptest.cc b/tests/dumptest.cc index e0d2776..e4876fa 100644 --- a/tests/dumptest.cc +++ b/tests/dumptest.cc @@ -74,7 +74,6 @@ int main(int argc, char **argv) const char *filename = argv[1]; const char *outfilename = argv[2]; - int rc = 0; Builtins::instance()->initialize(); @@ -86,10 +85,10 @@ int main(int argc, char **argv) parser_init(boosty::stringy(fs::path(argv[0]).branch_path())); add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); - ModuleContext root_ctx; - root_ctx.registerBuiltin(); + ModuleContext top_ctx; + top_ctx.registerBuiltin(); - AbstractModule *root_module; + FileModule *root_module; ModuleInstantiation root_inst("group"); AbstractNode *root_node; @@ -103,7 +102,7 @@ int main(int argc, char **argv) } AbstractNode::resetIndexCounter(); - root_node = root_module->evaluate(&root_ctx, &root_inst); + root_node = root_module->instantiate(&top_ctx, &root_inst); Tree tree; tree.setRoot(root_node); @@ -115,10 +114,17 @@ int main(int argc, char **argv) exit(1); } + fs::current_path(original_path); std::ofstream outfile; outfile.open(outfilename); + if (!outfile.is_open()) { + fprintf(stderr, "Error: Unable to open output file %s\n", outfilename); + exit(1); + } + std::cout << "Opened " << outfilename << "\n"; outfile << dumpstdstr << "\n"; outfile.close(); + if (outfile.fail()) fprintf(stderr, "Failed to close file\n"); delete root_node; delete root_module; @@ -131,7 +137,7 @@ int main(int argc, char **argv) } AbstractNode::resetIndexCounter(); - root_node = root_module->evaluate(&root_ctx, &root_inst); + root_node = root_module->instantiate(&top_ctx, &root_inst); tree.setRoot(root_node); diff --git a/tests/echotest.cc b/tests/echotest.cc index 9924d11..3051751 100644 --- a/tests/echotest.cc +++ b/tests/echotest.cc @@ -88,10 +88,10 @@ int main(int argc, char **argv) parser_init(boosty::stringy(fs::path(argv[0]).branch_path())); add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); - ModuleContext root_ctx; - root_ctx.registerBuiltin(); + ModuleContext top_ctx; + top_ctx.registerBuiltin(); - AbstractModule *root_module; + FileModule *root_module; ModuleInstantiation root_inst("group"); AbstractNode *root_node; @@ -105,7 +105,7 @@ int main(int argc, char **argv) } AbstractNode::resetIndexCounter(); - root_node = root_module->evaluate(&root_ctx, &root_inst); + root_node = root_module->instantiate(&top_ctx, &root_inst); delete root_node; delete root_module; diff --git a/tests/modulecachetest.cc b/tests/modulecachetest.cc index 62f9543..5531461 100644 --- a/tests/modulecachetest.cc +++ b/tests/modulecachetest.cc @@ -76,14 +76,13 @@ int main(int argc, char **argv) parser_init(boosty::stringy(fs::path(argv[0]).branch_path())); add_librarydir(boosty::stringy(fs::path(argv[0]).branch_path() / "../libraries")); - ModuleContext root_ctx; - root_ctx.registerBuiltin(); + ModuleContext top_ctx; + top_ctx.registerBuiltin(); - AbstractModule *root_module; ModuleInstantiation root_inst("group"); AbstractNode *root_node; - root_module = parsefile(filename); + FileModule *root_module = parsefile(filename); if (!root_module) { fprintf(stderr, "Error: Unable to parse input file\n"); exit(1); @@ -94,7 +93,7 @@ int main(int argc, char **argv) } AbstractNode::resetIndexCounter(); - root_node = root_module->evaluate(&root_ctx, &root_inst); + root_node = root_module->instantiate(&top_ctx, &root_inst); delete root_node; delete root_module; @@ -109,7 +108,7 @@ int main(int argc, char **argv) } AbstractNode::resetIndexCounter(); - root_node = root_module->evaluate(&root_ctx, &root_inst); + root_node = root_module->instantiate(&top_ctx, &root_inst); delete root_node; delete root_module; diff --git a/tests/regression/cgalpngtest/child-tests-expected.png b/tests/regression/cgalpngtest/child-tests-expected.png Binary files differindex ed6207c..eb34f18 100644 --- a/tests/regression/cgalpngtest/child-tests-expected.png +++ b/tests/regression/cgalpngtest/child-tests-expected.png diff --git a/tests/regression/cgalpngtest/localfiles-test-expected.png b/tests/regression/cgalpngtest/localfiles-test-expected.png Binary files differindex 3ad3d96..d0cfd50 100644 --- a/tests/regression/cgalpngtest/localfiles-test-expected.png +++ b/tests/regression/cgalpngtest/localfiles-test-expected.png diff --git a/tests/regression/dumptest/child-tests-expected.txt b/tests/regression/dumptest/child-tests-expected.txt index 9a886fe..e1a7557 100644 --- a/tests/regression/dumptest/child-tests-expected.txt +++ b/tests/regression/dumptest/child-tests-expected.txt @@ -37,4 +37,16 @@ multmatrix([[1, 0, 0, 5], [0, 1, 0, 3], [0, 0, 1, 0], [0, 0, 0, 1]]) { group(); } + multmatrix([[1, 0, 0, 0], [0, 1, 0, 6], [0, 0, 1, 0], [0, 0, 0, 1]]) { + group() { + group() { + multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + sphere($fn = 16, $fa = 12, $fs = 2, r = 1); + } + multmatrix([[1, 0, 0, 2.5], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) { + cube(size = [1, 1, 1], center = true); + } + } + } + } diff --git a/tests/regression/dumptest/localfiles-test-expected.txt b/tests/regression/dumptest/localfiles-test-expected.txt index acdf7e7..7a84e88 100644 --- a/tests/regression/dumptest/localfiles-test-expected.txt +++ b/tests/regression/dumptest/localfiles-test-expected.txt @@ -13,5 +13,8 @@ surface(file = "localfiles_dir/localfile.dat", center = false); } } + multmatrix([[1, 0, 0, 0], [0, 1, 0, -200], [0, 0, 1, 0], [0, 0, 0, 1]]) { + sphere($fn = 0, $fa = 12, $fs = 2, r = 100); + } } diff --git a/tests/regression/echotest/variable-scope-tests-expected.txt b/tests/regression/echotest/variable-scope-tests-expected.txt new file mode 100644 index 0000000..92db05d --- /dev/null +++ b/tests/regression/echotest/variable-scope-tests-expected.txt @@ -0,0 +1,16 @@ +ECHO: "special variable inheritance" +ECHO: 23, 5 +WARNING: Ignoring unknown variable 'a'. +ECHO: undef +ECHO: 23, 5 +ECHO: "inner variables shadows parameter" +ECHO: 5, 24 +ECHO: "user-defined special variables as parameter" +ECHO: 7 +ECHO: 7 +ECHO: "assign only visible in children's scope" +WARNING: Ignoring unknown variable 'c'. +ECHO: undef +ECHO: 5 +ECHO: "undeclared variable can still be passed and used" +ECHO: 6 diff --git a/tests/regression/opencsgtest/child-tests-expected.png b/tests/regression/opencsgtest/child-tests-expected.png Binary files differindex e8ea39b..2ff902c 100644 --- a/tests/regression/opencsgtest/child-tests-expected.png +++ b/tests/regression/opencsgtest/child-tests-expected.png diff --git a/tests/regression/opencsgtest/localfiles-test-expected.png b/tests/regression/opencsgtest/localfiles-test-expected.png Binary files differindex 7bc7909..f280efd 100644 --- a/tests/regression/opencsgtest/localfiles-test-expected.png +++ b/tests/regression/opencsgtest/localfiles-test-expected.png diff --git a/tests/regression/throwntogethertest/child-tests-expected.png b/tests/regression/throwntogethertest/child-tests-expected.png Binary files differindex 561334e..2ff902c 100644 --- a/tests/regression/throwntogethertest/child-tests-expected.png +++ b/tests/regression/throwntogethertest/child-tests-expected.png diff --git a/tests/regression/throwntogethertest/localfiles-test-expected.png b/tests/regression/throwntogethertest/localfiles-test-expected.png Binary files differindex 7bc7909..f280efd 100644 --- a/tests/regression/throwntogethertest/localfiles-test-expected.png +++ b/tests/regression/throwntogethertest/localfiles-test-expected.png diff --git a/tests/tests-common.cc b/tests/tests-common.cc index 703e1c5..ac85e37 100644 --- a/tests/tests-common.cc +++ b/tests/tests-common.cc @@ -7,9 +7,9 @@ #include <sstream> #include <fstream> -Module *parsefile(const char *filename) +FileModule *parsefile(const char *filename) { - Module *root_module = NULL; + FileModule *root_module = NULL; handle_dep(filename); std::ifstream ifs(filename); diff --git a/tests/tests-common.h b/tests/tests-common.h index 0047562..3393884 100644 --- a/tests/tests-common.h +++ b/tests/tests-common.h @@ -1,6 +1,6 @@ #ifndef TESTS_COMMON_H_ #define TESTS_COMMON_H_ -class Module *parsefile(const char *filename); +class FileModule *parsefile(const char *filename); #endif |