diff --git a/releases/2014.QX.md b/releases/2014.QX.md index 17d0fa94..15500944 100644 --- a/releases/2014.QX.md +++ b/releases/2014.QX.md @@ -6,6 +6,7 @@ * min() and max() can now take a wvector argument * concat() * 2D minkowski and holes +* chr() **Program Features:** * Added --viewall cmd-line parameter @@ -14,6 +15,12 @@ * Qt5, retina * SVG import/export * AMF import/export +* Color schemes for viewer and editor can be user-edited +* Improved editor +* Splash screen +* Docking of GUI components +* Toolbar +* More robust STL export **Bugfixes/improvements:** * Internal cavity fix diff --git a/src/builtin.cc b/src/builtin.cc index 76b76b90..e495f4a5 100644 --- a/src/builtin.cc +++ b/src/builtin.cc @@ -77,11 +77,12 @@ void Builtins::initialize() register_builtin_dxf_rotate_extrude(); register_builtin_text(); - this->deprecations["dxf_linear_extrude"] = "linear_extrude"; - this->deprecations["dxf_rotate_extrude"] = "rotate_extrude"; - this->deprecations["import_stl"] = "import"; - this->deprecations["import_dxf"] = "import"; - this->deprecations["import_off"] = "import"; + this->deprecations["dxf_linear_extrude"] = "linear_extrude()"; + this->deprecations["dxf_rotate_extrude"] = "rotate_extrude()"; + this->deprecations["import_stl"] = "import()"; + this->deprecations["import_dxf"] = "import()"; + this->deprecations["import_off"] = "import()"; + this->deprecations["assign"] = "a regular assignment"; } std::string Builtins::isDeprecated(const std::string &name) diff --git a/src/cgaladv.cc b/src/cgaladv.cc index 55610621..20addb78 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 *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; }; -AbstractNode *CgaladvModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *CgaladvModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { CgaladvNode *node = new CgaladvNode(inst, type); @@ -62,6 +62,7 @@ AbstractNode *CgaladvModule::instantiate(const Context *ctx, const ModuleInstant Context c(ctx); c.setVariables(args, evalctx); + inst->scope.apply(*evalctx); ValuePtr convexity = ValuePtr::undefined; ValuePtr path = ValuePtr::undefined; diff --git a/src/color.cc b/src/color.cc index ced7a0b9..c7e9526e 100644 --- a/src/color.cc +++ b/src/color.cc @@ -42,7 +42,7 @@ class ColorModule : public AbstractModule public: ColorModule(); virtual ~ColorModule(); - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; private: boost::unordered_map webcolors; @@ -205,7 +205,7 @@ ColorModule::~ColorModule() { } -AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { ColorNode *node = new ColorNode(inst); @@ -218,6 +218,7 @@ AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantia Context c(ctx); c.setVariables(args, evalctx); + inst->scope.apply(*evalctx); ValuePtr v = c.lookup_variable("c"); if (v->type() == Value::VECTOR) { diff --git a/src/context.cc b/src/context.cc index 29914d9a..74dfc3cd 100644 --- a/src/context.cc +++ b/src/context.cc @@ -167,7 +167,7 @@ ValuePtr Context::evaluate_function(const std::string &name, const EvalContext * return ValuePtr::undefined; } -AbstractNode *Context::instantiate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) const +AbstractNode *Context::instantiate_module(const ModuleInstantiation &inst, EvalContext *evalctx) const { if (this->parent) return this->parent->instantiate_module(inst, evalctx); print_ignore_warning("module", inst.name().c_str()); diff --git a/src/context.h b/src/context.h index 639973bb..696eaa85 100644 --- a/src/context.h +++ b/src/context.h @@ -16,7 +16,7 @@ public: const Context *getParent() const { return this->parent; } virtual ValuePtr evaluate_function(const std::string &name, const class EvalContext *evalctx) const; - virtual class AbstractNode *instantiate_module(const class ModuleInstantiation &inst, const EvalContext *evalctx) const; + virtual class AbstractNode *instantiate_module(const class ModuleInstantiation &inst, EvalContext *evalctx) const; void setVariables(const AssignmentList &args, const class EvalContext *evalctx = NULL); diff --git a/src/control.cc b/src/control.cc index ee8c7e54..bcbae4a5 100644 --- a/src/control.cc +++ b/src/control.cc @@ -29,6 +29,7 @@ #include "node.h" #include "evalcontext.h" #include "modcontext.h" +#include "expression.h" #include "builtin.h" #include "printutils.h" #include @@ -55,7 +56,7 @@ public: // methods : type(type) { } - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; static void for_eval(AbstractNode &node, const ModuleInstantiation &inst, size_t l, const Context *ctx, const EvalContext *evalctx); @@ -99,7 +100,14 @@ void ControlModule::for_eval(AbstractNode &node, const ModuleInstantiation &inst for_eval(node, inst, l+1, &c, evalctx); } } else if (l > 0) { - std::vector instantiatednodes = inst.instantiateChildren(ctx); + // At this point, the for loop variables have been set and we can initialize + // the local scope (as they may depend on the for loop variables + Context c(ctx); + BOOST_FOREACH(const Assignment &ass, inst.scope.assignments) { + c.set_variable(ass.first, ass.second->evaluate(&c)); + } + + std::vector instantiatednodes = inst.instantiateChildren(&c); node.children.insert(node.children.end(), instantiatednodes.begin(), instantiatednodes.end()); } } @@ -155,12 +163,12 @@ AbstractNode* ControlModule::getChild(const Value& value, const EvalContext* mod return modulectx->getChild(n)->evaluate(modulectx); } -AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleInstantiation *inst, EvalContext *evalctx) const { AbstractNode *node = NULL; - if (type == CHILD) - { + switch (this->type) { + case CHILD: { printDeprecation("DEPRECATED: child() will be removed in future releases. Use children() instead."); int n = 0; if (evalctx->numArgs() > 0) { @@ -192,9 +200,9 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns } return node; } + break; - if (type == CHILDREN) - { + case CHILDREN: { const EvalContext *modulectx = getLastModuleCtx(evalctx); if (modulectx==NULL) { return NULL; @@ -251,14 +259,10 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns } return NULL; } + break; - if (type == INT_FOR) - node = new AbstractIntersectionNode(inst); - else + case ECHO: { node = new AbstractNode(inst); - - if (type == ECHO) - { std::stringstream msg; msg << "ECHO: "; for (size_t i = 0; i < inst->arguments.size(); i++) { @@ -273,36 +277,50 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns } PRINTB("%s", msg.str()); } + break; - if (type == ASSIGN) - { + case ASSIGN: { + node = new AbstractNode(inst); + // We create a new context to avoid parameters from influencing each other + // -> parallel evaluation. This is to be backwards compatible. Context c(evalctx); for (size_t i = 0; i < evalctx->numArgs(); i++) { if (!evalctx->getArgName(i).empty()) c.set_variable(evalctx->getArgName(i), evalctx->getArgValue(i)); } + // Let any local variables override the parameters + inst->scope.apply(c); std::vector instantiatednodes = inst->instantiateChildren(&c); node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); } + break; - if (type == FOR || type == INT_FOR) - { + case FOR: + node = new AbstractNode(inst); for_eval(*node, *inst, 0, evalctx, evalctx); - } + break; - if (type == IF) - { + case INT_FOR: + node = new AbstractIntersectionNode(inst); + for_eval(*node, *inst, 0, evalctx, evalctx); + break; + + case IF: { + node = new AbstractNode(inst); const IfElseModuleInstantiation *ifelse = dynamic_cast(inst); if (evalctx->numArgs() > 0 && evalctx->getArgValue(0)->toBool()) { + inst->scope.apply(*evalctx); std::vector instantiatednodes = ifelse->instantiateChildren(evalctx); node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); } else { + ifelse->else_scope.apply(*evalctx); std::vector instantiatednodes = ifelse->instantiateElseChildren(evalctx); node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); } } - + break; + } return node; } diff --git a/src/csgops.cc b/src/csgops.cc index 28d15157..038348b9 100644 --- a/src/csgops.cc +++ b/src/csgops.cc @@ -38,11 +38,12 @@ class CsgModule : public AbstractModule public: OpenSCADOperator type; CsgModule(OpenSCADOperator type) : type(type) { } - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; }; -AbstractNode *CsgModule::instantiate(const Context*, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *CsgModule::instantiate(const Context*, const ModuleInstantiation *inst, EvalContext *evalctx) const { + inst->scope.apply(*evalctx); CsgNode *node = new CsgNode(inst, type); std::vector instantiatednodes = inst->instantiateChildren(evalctx); node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); diff --git a/src/evalcontext.cc b/src/evalcontext.cc index 395a2329..223b4ce6 100644 --- a/src/evalcontext.cc +++ b/src/evalcontext.cc @@ -9,6 +9,12 @@ #include +EvalContext::EvalContext(const Context *parent, + const AssignmentList &args, const class LocalScope *const scope) + : Context(parent), eval_arguments(args), scope(scope) +{ +} + const std::string &EvalContext::getArgName(size_t i) const { assert(i < this->eval_arguments.size()); diff --git a/src/evalcontext.h b/src/evalcontext.h index 108de1cf..f5054264 100644 --- a/src/evalcontext.h +++ b/src/evalcontext.h @@ -12,8 +12,7 @@ public: typedef std::vector InstanceList; EvalContext(const Context *parent, - const AssignmentList &args, const class LocalScope *const scope = NULL) - : Context(parent), eval_arguments(args), scope(scope) {} + const AssignmentList &args, const class LocalScope *const scope = NULL); virtual ~EvalContext() {} size_t numArgs() const { return this->eval_arguments.size(); } @@ -29,6 +28,5 @@ public: private: const AssignmentList &eval_arguments; - std::vector > eval_values; const LocalScope *const scope; }; diff --git a/src/import.cc b/src/import.cc index 79ebb4bc..ddc4a950 100644 --- a/src/import.cc +++ b/src/import.cc @@ -61,10 +61,10 @@ class ImportModule : public AbstractModule public: import_type_e type; ImportModule(import_type_e type = TYPE_UNKNOWN) : type(type) { } - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; }; -AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { AssignmentList args; args += Assignment("file"), Assignment("layer"), Assignment("convexity"), Assignment("origin"), Assignment("scale"); diff --git a/src/linearextrude.cc b/src/linearextrude.cc index c6bd8256..e535c28d 100644 --- a/src/linearextrude.cc +++ b/src/linearextrude.cc @@ -46,10 +46,10 @@ class LinearExtrudeModule : public AbstractModule { public: LinearExtrudeModule() { } - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; }; -AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { LinearExtrudeNode *node = new LinearExtrudeNode(inst); @@ -58,6 +58,7 @@ AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleI Context c(ctx); c.setVariables(args, evalctx); + inst->scope.apply(*evalctx); node->fn = c.lookup_variable("$fn")->toDouble(); node->fs = c.lookup_variable("$fs")->toDouble(); diff --git a/src/localscope.cc b/src/localscope.cc index e8f9947c..2c4185f2 100644 --- a/src/localscope.cc +++ b/src/localscope.cc @@ -42,13 +42,28 @@ std::string LocalScope::dump(const std::string &indent) const return dump.str(); } -std::vector LocalScope::instantiateChildren(const Context *evalctx, FileContext *filectx) const +// 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 LocalScope::instantiateChildren(const Context *evalctx) const { std::vector childnodes; BOOST_FOREACH (ModuleInstantiation *modinst, this->children) { - AbstractNode *node = modinst->evaluate(filectx ? filectx : evalctx); + AbstractNode *node = modinst->evaluate(evalctx); if (node) childnodes.push_back(node); } return childnodes; } + +/*! + When instantiating a module which can take a scope as parameter (i.e. non-leaf nodes), + use this method to apply the local scope definitions to the evaluation context. + This will enable variables defined in local blocks. + NB! for loops are special as the local block may depend on variables evaluated by the + for loop parameters. The for loop code will handle this specially. +*/ +void LocalScope::apply(Context &ctx) const +{ + BOOST_FOREACH(const Assignment &ass, this->assignments) { + ctx.set_variable(ass.first, ass.second->evaluate(&ctx)); + } +} diff --git a/src/localscope.h b/src/localscope.h index 83e4f105..52808d6b 100644 --- a/src/localscope.h +++ b/src/localscope.h @@ -11,8 +11,9 @@ public: size_t numElements() const { return assignments.size() + children.size(); } std::string dump(const std::string &indent) const; - std::vector instantiateChildren(const class Context *evalctx, class FileContext *filectx = NULL) const; + std::vector instantiateChildren(const class Context *evalctx) const; void addChild(ModuleInstantiation *ch); + void apply(Context &ctx) const; AssignmentList assignments; ModuleInstantiationList children; diff --git a/src/modcontext.cc b/src/modcontext.cc index 7269f6e0..04d46e60 100644 --- a/src/modcontext.cc +++ b/src/modcontext.cc @@ -71,6 +71,7 @@ void ModuleContext::initializeModule(const class Module &module) BOOST_FOREACH(const Assignment &ass, module.scope.assignments) { this->set_variable(ass.first, ass.second->evaluate(this)); } + // Experimental code. See issue #399 // evaluateAssignments(module.scope.assignments); } @@ -115,7 +116,7 @@ const AbstractModule *ModuleContext::findLocalModule(const std::string &name) co } std::string replacement = Builtins::instance()->isDeprecated(name); if (!replacement.empty()) { - PRINT_DEPRECATION("DEPRECATED: The %s() module will be removed in future releases. Use %s() instead.", name % replacement); + PRINT_DEPRECATION("DEPRECATED: The %s() module will be removed in future releases. Use %s instead.", name % replacement); } return m; } @@ -131,7 +132,7 @@ ValuePtr ModuleContext::evaluate_function(const std::string &name, return Context::evaluate_function(name, evalctx); } -AbstractNode *ModuleContext::instantiate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) const +AbstractNode *ModuleContext::instantiate_module(const ModuleInstantiation &inst, EvalContext *evalctx) const { const AbstractModule *foundm = this->findLocalModule(inst.name()); if (foundm) return foundm->instantiate(this, &inst, evalctx); @@ -209,7 +210,7 @@ ValuePtr FileContext::evaluate_function(const std::string &name, return ModuleContext::evaluate_function(name, evalctx); } -AbstractNode *FileContext::instantiate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) const +AbstractNode *FileContext::instantiate_module(const ModuleInstantiation &inst, EvalContext *evalctx) const { const AbstractModule *foundm = this->findLocalModule(inst.name()); if (foundm) return foundm->instantiate(this, &inst, evalctx); diff --git a/src/modcontext.h b/src/modcontext.h index ce6a9d51..400c4142 100644 --- a/src/modcontext.h +++ b/src/modcontext.h @@ -21,7 +21,7 @@ public: virtual ValuePtr evaluate_function(const std::string &name, const EvalContext *evalctx) const; virtual AbstractNode *instantiate_module(const ModuleInstantiation &inst, - const EvalContext *evalctx) const; + EvalContext *evalctx) const; const AbstractModule *findLocalModule(const std::string &name) const; const AbstractFunction *findLocalFunction(const std::string &name) const; @@ -48,7 +48,7 @@ public: virtual ValuePtr evaluate_function(const std::string &name, const EvalContext *evalctx) const; virtual AbstractNode *instantiate_module(const ModuleInstantiation &inst, - const EvalContext *evalctx) const; + EvalContext *evalctx) const; private: const FileModule::ModuleContainer &usedlibs; diff --git a/src/module.cc b/src/module.cc index f2c738a0..ac31b5e9 100644 --- a/src/module.cc +++ b/src/module.cc @@ -48,7 +48,7 @@ AbstractModule::~AbstractModule() { } -AbstractNode *AbstractModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *AbstractModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { (void)ctx; // avoid unusued parameter warning @@ -173,13 +173,17 @@ Module::~Module() { } -AbstractNode *Module::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *Module::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { if (StackCheck::inst()->check()) { throw RecursionException("module", inst->name()); return NULL; } + // At this point we know that nobody will modify the dependencies of the local scope + // passed to this instance, so we can populate the context + inst->scope.apply(*evalctx); + ModuleContext c(ctx, evalctx); // set $children first since we might have variables depending on it c.set_variable("$children", ValuePtr(double(inst->scope.children.size()))); @@ -329,7 +333,7 @@ bool FileModule::handleDependencies() return somethingchanged; } -AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { assert(evalctx == NULL); FileContext c(*this, ctx); @@ -341,7 +345,7 @@ AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiat AbstractNode *node = new AbstractNode(inst); try { - std::vector instantiatednodes = this->scope.instantiateChildren(ctx, &c); + std::vector instantiatednodes = this->scope.instantiateChildren(&c); node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); } catch (RecursionException &e) { diff --git a/src/module.h b/src/module.h index 597c3b95..e7030be8 100644 --- a/src/module.h +++ b/src/module.h @@ -67,7 +67,7 @@ public: virtual ~AbstractModule(); virtual bool is_experimental() const { return feature != NULL; } virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); } - virtual class AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const class EvalContext *evalctx = NULL) const; + virtual class AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, class EvalContext *evalctx = NULL) const; virtual std::string dump(const std::string &indent, const std::string &name) const; virtual double lookup_double_variable_with_default(Context &c, std::string variable, double def) const; virtual std::string lookup_string_variable_with_default(Context &c, std::string variable, std::string def) const; @@ -80,7 +80,7 @@ public: Module(const Feature& feature) : AbstractModule(feature) { } virtual ~Module(); - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx = NULL) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx = NULL) const; virtual std::string dump(const std::string &indent, const std::string &name) const; static const std::string& stack_element(int n) { return module_stack[n]; }; static int stack_size() { return module_stack.size(); }; @@ -107,7 +107,7 @@ public: void registerInclude(const std::string &localpath, const std::string &fullpath); bool includesChanged() const; bool handleDependencies(); - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx = NULL) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx = NULL) const; bool hasIncludes() const { return !this->includes.empty(); } bool usesLibraries() const { return !this->usedlibs.empty(); } bool isHandlingDependencies() const { return this->is_handling_dependencies; } diff --git a/src/offset.cc b/src/offset.cc index a5803430..08705907 100644 --- a/src/offset.cc +++ b/src/offset.cc @@ -46,10 +46,10 @@ class OffsetModule : public AbstractModule { public: OffsetModule() { } - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; }; -AbstractNode *OffsetModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *OffsetModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { OffsetNode *node = new OffsetNode(inst); @@ -58,6 +58,7 @@ AbstractNode *OffsetModule::instantiate(const Context *ctx, const ModuleInstanti Context c(ctx); c.setVariables(args, evalctx); + inst->scope.apply(*evalctx); node->fn = c.lookup_variable("$fn")->toDouble(); node->fs = c.lookup_variable("$fs")->toDouble(); diff --git a/src/parser.y b/src/parser.y index 8ed27b85..f1505de1 100644 --- a/src/parser.y +++ b/src/parser.y @@ -270,6 +270,7 @@ if_statement: child_statements: /* empty */ | child_statements child_statement + | child_statements assignment ; child_statement: @@ -281,13 +282,6 @@ child_statement: } ; -/* - FIXME: This allows for variable declaration in child blocks, not activated yet - | -assignment ; -*/ - - // "for" is a valid module identifier module_id: TOK_ID { $$ = $1; } diff --git a/src/primitives.cc b/src/primitives.cc index ef816b28..cadb13ea 100644 --- a/src/primitives.cc +++ b/src/primitives.cc @@ -61,7 +61,7 @@ class PrimitiveModule : public AbstractModule public: primitive_type_e type; PrimitiveModule(primitive_type_e type) : type(type) { } - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; private: Value lookup_radius(const Context &ctx, const std::string &radius_var, const std::string &diameter_var) const; }; @@ -141,7 +141,7 @@ Value PrimitiveModule::lookup_radius(const Context &ctx, const std::string &diam } } -AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { PrimitiveNode *node = new PrimitiveNode(inst, this->type); diff --git a/src/projection.cc b/src/projection.cc index ef9de3df..8929c07a 100644 --- a/src/projection.cc +++ b/src/projection.cc @@ -41,10 +41,10 @@ class ProjectionModule : public AbstractModule { public: ProjectionModule() { } - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; }; -AbstractNode *ProjectionModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *ProjectionModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { ProjectionNode *node = new ProjectionNode(inst); @@ -53,6 +53,7 @@ AbstractNode *ProjectionModule::instantiate(const Context *ctx, const ModuleInst Context c(ctx); c.setVariables(args, evalctx); + inst->scope.apply(*evalctx); ValuePtr convexity = c.lookup_variable("convexity", true); ValuePtr cut = c.lookup_variable("cut", true); diff --git a/src/render.cc b/src/render.cc index f2bbf22a..12c5a567 100644 --- a/src/render.cc +++ b/src/render.cc @@ -38,10 +38,10 @@ class RenderModule : public AbstractModule { public: RenderModule() { } - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; }; -AbstractNode *RenderModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *RenderModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { RenderNode *node = new RenderNode(inst); @@ -50,6 +50,7 @@ AbstractNode *RenderModule::instantiate(const Context *ctx, const ModuleInstanti Context c(ctx); c.setVariables(args, evalctx); + inst->scope.apply(*evalctx); ValuePtr v = c.lookup_variable("convexity"); if (v->type() == Value::NUMBER) diff --git a/src/rotateextrude.cc b/src/rotateextrude.cc index eac1d7b9..700fb7c9 100644 --- a/src/rotateextrude.cc +++ b/src/rotateextrude.cc @@ -44,10 +44,10 @@ class RotateExtrudeModule : public AbstractModule { public: RotateExtrudeModule() { } - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; }; -AbstractNode *RotateExtrudeModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *RotateExtrudeModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { RotateExtrudeNode *node = new RotateExtrudeNode(inst); @@ -56,6 +56,7 @@ AbstractNode *RotateExtrudeModule::instantiate(const Context *ctx, const ModuleI Context c(ctx); c.setVariables(args, evalctx); + inst->scope.apply(*evalctx); node->fn = c.lookup_variable("$fn")->toDouble(); node->fs = c.lookup_variable("$fs")->toDouble(); diff --git a/src/surface.cc b/src/surface.cc index dbfac901..264f9027 100644 --- a/src/surface.cc +++ b/src/surface.cc @@ -52,7 +52,7 @@ class SurfaceModule : public AbstractModule { public: SurfaceModule() { } - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; }; typedef boost::unordered_map,double> img_data_t; @@ -80,7 +80,7 @@ private: img_data_t read_png_or_dat(std::string filename) const; }; -AbstractNode *SurfaceModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *SurfaceModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { SurfaceNode *node = new SurfaceNode(inst); node->center = false; diff --git a/src/text.cc b/src/text.cc index 0cc12889..3b4f1a7c 100644 --- a/src/text.cc +++ b/src/text.cc @@ -41,10 +41,10 @@ class TextModule : public AbstractModule { public: TextModule() : AbstractModule(Feature::ExperimentalTextModule) { } - virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; }; -AbstractNode *TextModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *TextModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { TextNode *node = new TextNode(inst); diff --git a/src/transform.cc b/src/transform.cc index 74a7f7be..c86f0301 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 *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const; + virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const; }; -AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const +AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { TransformNode *node = new TransformNode(inst); @@ -83,6 +83,7 @@ AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInsta Context c(ctx); c.setVariables(args, evalctx); + inst->scope.apply(*evalctx); if (this->type == SCALE) { diff --git a/testdata/scad/misc/scope-assignment-tests.scad b/testdata/scad/misc/scope-assignment-tests.scad new file mode 100644 index 00000000..08ce921d --- /dev/null +++ b/testdata/scad/misc/scope-assignment-tests.scad @@ -0,0 +1,85 @@ +echo("union scope"); +a = 4; +union() { + a = 5; + echo("local a (5):", a); +} +echo("global a (4):", a); + + +echo("module scope:"); +module mymodule(b=6) { + b = 7; + echo("local b (7)", b); +} +mymodule(); +mymodule(8); + + +echo("module children scope:"); +module mymodule2(b2=6) { + b2 = 2; + children(0); +} +mymodule2(b2=7) { + b2 = 3; + echo("b2 (3)", b2); +} + +echo("for loop (c = 0,1,25):"); +for (i=[0:2]) { + c = (i > 1) ? i + 23 : i; + echo("c", c); +} + +echo("if scope:"); +if (true) { + d = 8; + echo("d (8)", d); +} + +echo("else scope:"); +if (false) { +} else { + d = 9; + echo("d (9)", d); +} + +echo("anonymous inner scope (scope ignored):"); +union() { + e = 2; + echo("outer e (3)", e); + { + e = 3; + echo("inner e (3)", e); + } +} + +echo("anonymous scope (scope ignored):"); +f=1; +echo("outer f (2)", f); +{ + f=2; + echo("inner f (2)", f); +} + +echo("anonymous scope reassign:"); +{ + g=1; + echo("g (2)", g); + g=2; +} + +echo("anonymous reassign using outer (scope ignored)", h); +h=5; +{ + h=h*2; // Not allowed + echo("h (undef)", h); +} + +echo("override variable in assign scope:"); +assign(i=9) { + i=10; + echo("i (10)", i); +} + diff --git a/testdata/scad/misc/search-tests-unicode.scad b/testdata/scad/misc/search-tests-unicode.scad index d863eff9..f9f841a6 100644 --- a/testdata/scad/misc/search-tests-unicode.scad +++ b/testdata/scad/misc/search-tests-unicode.scad @@ -2,16 +2,13 @@ //Helper function that pretty prints our search test //Expected result is checked against execution of a search() invocation and OK/FAIL is indicated -module test_search_and_echo( exp_res, search_to_find, search_to_search, search_up_to_num_matches = undef) -{ - if(undef != search_up_to_num_matches) - { - assign( test_res = search(search_to_find, search_to_search, search_up_to_num_matches) ) +module test_search_and_echo( exp_res, search_to_find, search_to_search, search_up_to_num_matches = undef) { + if (undef != search_up_to_num_matches) { + test_res = search(search_to_find, search_to_search, search_up_to_num_matches); echo(str("Expect ", exp_res, " for search(", search_to_find, ", ", search_to_search, ", ", search_up_to_num_matches, ")=", test_res, ". ", (exp_res == test_res)?"OK":"FAIL" )); } - else - { - assign( test_res = search(search_to_find, search_to_search) ) + else { + test_res = search(search_to_find, search_to_search); echo(str("Expect ", exp_res, " for search(", search_to_find, ", ", search_to_search, ")=", test_res, ". ", (exp_res == test_res)?"OK":"FAIL" )); } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 88253bb9..2d5f05f1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1046,6 +1046,7 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES} ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests2.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/variable-scope-tests.scad + ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/scope-assignment-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/lookup-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/expression-shortcircuit-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/parent_module-tests.scad diff --git a/tests/regression/echotest/scope-assignment-tests-expected.echo b/tests/regression/echotest/scope-assignment-tests-expected.echo new file mode 100644 index 00000000..5b457901 --- /dev/null +++ b/tests/regression/echotest/scope-assignment-tests-expected.echo @@ -0,0 +1,30 @@ +WARNING: Ignoring unknown variable 'h'. +ECHO: "union scope" +ECHO: "local a (5):", 5 +ECHO: "global a (4):", 4 +ECHO: "module scope:" +ECHO: "local b (7)", 7 +ECHO: "local b (7)", 7 +ECHO: "module children scope:" +ECHO: "b2 (3)", 3 +ECHO: "for loop (c = 0,1,25):" +ECHO: "c", 0 +ECHO: "c", 1 +ECHO: "c", 25 +ECHO: "if scope:" +ECHO: "d (8)", 8 +ECHO: "else scope:" +ECHO: "d (9)", 9 +ECHO: "anonymous inner scope (scope ignored):" +ECHO: "outer e (3)", 3 +ECHO: "inner e (3)", 3 +ECHO: "anonymous scope (scope ignored):" +ECHO: "outer f (2)", 2 +ECHO: "inner f (2)", 2 +ECHO: "anonymous scope reassign:" +ECHO: "g (2)", 2 +ECHO: "anonymous reassign using outer (scope ignored)", undef +ECHO: "h (undef)", undef +ECHO: "override variable in assign scope:" +DEPRECATED: The assign() module will be removed in future releases. Use a regular assignment instead. +ECHO: "i (10)", 10 diff --git a/tests/regression/echotest/variable-scope-tests-expected.echo b/tests/regression/echotest/variable-scope-tests-expected.echo index 2a820905..7f18cf47 100644 --- a/tests/regression/echotest/variable-scope-tests-expected.echo +++ b/tests/regression/echotest/variable-scope-tests-expected.echo @@ -18,6 +18,7 @@ ECHO: "user-defined special variables as parameter" ECHO: 7 ECHO: 7 ECHO: "assign only visible in children's scope" +DEPRECATED: The assign() module will be removed in future releases. Use a regular assignment instead. WARNING: Ignoring unknown variable 'c'. ECHO: undef ECHO: 5