summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md12
-rw-r--r--RELEASE_NOTES2
-rw-r--r--doc/OpenSCAD-polygons.grafflebin4215 -> 4982 bytes
-rw-r--r--openscad.pro7
-rwxr-xr-xscripts/macosx-build-dependencies.sh2
-rw-r--r--src/MainWindow.h4
-rw-r--r--src/builtin.cc31
-rw-r--r--src/builtin.h8
-rw-r--r--src/cgaladv.cc21
-rw-r--r--src/color.cc15
-rw-r--r--src/context.cc150
-rw-r--r--src/context.h34
-rw-r--r--src/control.cc93
-rw-r--r--src/csgops.cc7
-rw-r--r--src/dxfdim.cc63
-rw-r--r--src/evalcontext.cc40
-rw-r--r--src/evalcontext.h24
-rw-r--r--src/expr.cc24
-rw-r--r--src/expression.h3
-rw-r--r--src/func.cc230
-rw-r--r--src/function.h12
-rw-r--r--src/import.cc38
-rw-r--r--src/lexer.l1
-rw-r--r--src/linearextrude.cc25
-rw-r--r--src/mainwin.cc8
-rw-r--r--src/modcontext.cc152
-rw-r--r--src/modcontext.h40
-rw-r--r--src/module.cc107
-rw-r--r--src/module.h38
-rw-r--r--src/openscad.cc15
-rw-r--r--src/parser.y115
-rw-r--r--src/primitives.cc26
-rw-r--r--src/printutils.cc2
-rw-r--r--src/printutils.h3
-rw-r--r--src/projection.cc15
-rw-r--r--src/render.cc15
-rw-r--r--src/rotateextrude.cc17
-rw-r--r--src/surface.cc15
-rw-r--r--src/transform.cc23
-rw-r--r--src/typedefs.h10
-rw-r--r--testdata/scad/features/child-child-test.scad12
-rw-r--r--testdata/scad/features/module-recursion.scad15
-rw-r--r--testdata/scad/features/modulevariables.scad7
-rw-r--r--testdata/scad/misc/localfiles-test.scad3
-rw-r--r--testdata/scad/misc/localfiles_dir/localfile.dat2
-rw-r--r--testdata/scad/misc/localfiles_dir/localfile.dxf1968
-rw-r--r--testdata/scad/misc/localfiles_dir/localfiles_module.scad10
-rw-r--r--tests/CMakeLists.txt9
-rw-r--r--tests/cgalcachetest.cc8
-rw-r--r--tests/cgalpngtest.cc8
-rw-r--r--tests/cgalstlsanitytest.cc8
-rw-r--r--tests/cgaltest.cc8
-rw-r--r--tests/csgtermtest.cc8
-rw-r--r--tests/csgtestcore.cc8
-rw-r--r--tests/csgtexttest.cc8
-rw-r--r--tests/dumptest.cc18
-rw-r--r--tests/echotest.cc8
-rw-r--r--tests/modulecachetest.cc8
-rw-r--r--tests/regression/cgalpngtest/child-child-test-expected.pngbin0 -> 8017 bytes
-rw-r--r--tests/regression/cgalpngtest/localfiles-test-expected.pngbin0 -> 8454 bytes
-rw-r--r--tests/regression/cgalpngtest/module-recursion-expected.pngbin0 -> 9814 bytes
-rw-r--r--tests/regression/cgalpngtest/modulevariables-expected.pngbin0 -> 9534 bytes
-rw-r--r--tests/regression/dumptest/child-child-test-expected.txt59
-rw-r--r--tests/regression/dumptest/localfiles-test-expected.txt17
-rw-r--r--tests/regression/dumptest/module-recursion-expected.txt244
-rw-r--r--tests/regression/dumptest/modulevariables-expected.txt4
-rw-r--r--tests/regression/opencsgtest/child-child-test-expected.pngbin0 -> 8105 bytes
-rw-r--r--tests/regression/opencsgtest/localfiles-test-expected.pngbin0 -> 8844 bytes
-rw-r--r--tests/regression/opencsgtest/module-recursion-expected.pngbin0 -> 10321 bytes
-rw-r--r--tests/regression/opencsgtest/modulevariables-expected.pngbin0 -> 9854 bytes
-rw-r--r--tests/regression/throwntogethertest/child-child-test-expected.pngbin0 -> 8105 bytes
-rw-r--r--tests/regression/throwntogethertest/localfiles-test-expected.pngbin0 -> 8844 bytes
-rw-r--r--tests/regression/throwntogethertest/module-recursion-expected.pngbin0 -> 10321 bytes
-rw-r--r--tests/regression/throwntogethertest/modulevariables-expected.pngbin0 -> 9854 bytes
74 files changed, 3259 insertions, 628 deletions
diff --git a/README.md b/README.md
index d7560de..0f4d8d7 100644
--- a/README.md
+++ b/README.md
@@ -84,15 +84,15 @@ libraries from aptitude. If you're using Mac, or an older Linux/BSD, there
are build scripts that download and compile the libraries from source.
Follow the instructions for the platform you're compiling on below.
-* [Qt4 (4.4 - 4.7)](http://www.qt.nokia.com/)
-* [CGAL (3.6 - 4.0.2)](http://www.cgal.org/)
- * [GMP (5.0.x)](http://www.gmplib.org/)
- * [cmake (2.6 - 2.8, required by CGAL and the test framework)](http://www.cmake.org/)
+* [Qt4 (4.4 - 4.8)](http://www.qt.nokia.com/)
+* [CGAL (3.6 - 4.1)](http://www.cgal.org/)
+ * [GMP (5.x)](http://www.gmplib.org/)
+ * [cmake (2.8, required by CGAL and the test framework)](http://www.cmake.org/)
* [MPFR (3.x)](http://www.mpfr.org/)
- * [boost (1.35 - 1.47)](http://www.boost.org/)
+ * [boost (1.35 - 1.53)](http://www.boost.org/)
* [OpenCSG (1.3.2)](http://www.opencsg.org/)
* [GLEW (1.5.4 ->)](http://glew.sourceforge.net/)
-* [Eigen (2.0.13->3.1.1)](http://eigen.tuxfamily.org/)
+* [Eigen (2.0.x->3.x)](http://eigen.tuxfamily.org/)
* [GCC C++ Compiler (4.2 ->)](http://gcc.gnu.org/)
* [Bison (2.4)](http://www.gnu.org/software/bison/)
* [Flex (2.5.35)](http://flex.sourceforge.net/)
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 252b38b..65ff3a3 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -2,6 +2,7 @@ OpenSCAD 2013.XX
================
Features:
+o Recursive modules and functions is now supported (including cascading child() operations)
o Console output is now enabled on Windows through the openscad.com executable
o Added basic syntax highlighting in the editor
o Mac: Added document icon
@@ -12,6 +13,7 @@ o Regression test now creates single monolithic .html file for easier uploading
o value reassignment is now less strict
Bugfixes:
+o Importing files is now always relative to the importing script, also for libraries
o OpenCSG rendering sometimes crashed when rendering large models
o We didn't always print a warning when CSG normalization created too many elements
o Binary STLs can now be read on big endian architectures
diff --git a/doc/OpenSCAD-polygons.graffle b/doc/OpenSCAD-polygons.graffle
index 758d575..63985dc 100644
--- a/doc/OpenSCAD-polygons.graffle
+++ b/doc/OpenSCAD-polygons.graffle
Binary files differ
diff --git a/openscad.pro b/openscad.pro
index 42b8ef0..0f3394e 100644
--- a/openscad.pro
+++ b/openscad.pro
@@ -187,7 +187,8 @@ FORMS += src/MainWindow.ui \
src/AboutDialog.ui \
src/ProgressWidget.ui
-HEADERS += src/version_check.h \
+HEADERS += src/typedefs.h \
+ src/version_check.h \
src/ProgressWidget.h \
src/parsersettings.h \
src/renderer.h \
@@ -203,6 +204,8 @@ HEADERS += src/version_check.h \
src/AboutDialog.h \
src/builtin.h \
src/context.h \
+ src/modcontext.h \
+ src/evalcontext.h \
src/csgterm.h \
src/csgtermnormalizer.h \
src/dxfdata.h \
@@ -272,6 +275,8 @@ SOURCES += src/version_check.cc \
src/module.cc \
src/node.cc \
src/context.cc \
+ src/modcontext.cc \
+ src/evalcontext.cc \
src/csgterm.cc \
src/csgtermnormalizer.cc \
src/polyset.cc \
diff --git a/scripts/macosx-build-dependencies.sh b/scripts/macosx-build-dependencies.sh
index f3fbc2b..088d8b4 100755
--- a/scripts/macosx-build-dependencies.sh
+++ b/scripts/macosx-build-dependencies.sh
@@ -437,7 +437,7 @@ mkdir -p $SRCDIR $DEPLOYDIR
build_qt 4.8.4
build_eigen 3.1.2
build_gmp 5.1.1
-build_mpfr 3.1.1
+build_mpfr 3.1.2
build_boost 1.53.0
# NB! For CGAL, also update the actual download URL in the function
build_cgal 4.1
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 8745b8b..65deb15 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -4,7 +4,7 @@
#include <QMainWindow>
#include "ui_MainWindow.h"
#include "openscad.h"
-#include "context.h"
+#include "modcontext.h"
#include "module.h"
#include "Tree.h"
#include "memory.h"
@@ -29,7 +29,7 @@ public:
QTimer *autoReloadTimer;
std::string autoReloadId;
- Context root_ctx;
+ ModuleContext root_ctx;
Module *root_module; // Result of parsing
ModuleInstantiation root_inst; // Top level instance
AbstractNode *absolute_root_node; // Result of tree evaluation
diff --git a/src/builtin.cc b/src/builtin.cc
index 6eb32b6..bdd2d3b 100644
--- a/src/builtin.cc
+++ b/src/builtin.cc
@@ -1,6 +1,7 @@
#include "builtin.h"
#include "function.h"
#include "module.h"
+#include "expression.h"
#include <boost/foreach.hpp>
Builtins *Builtins::instance(bool erase)
@@ -15,12 +16,12 @@ Builtins *Builtins::instance(bool erase)
void Builtins::init(const char *name, class AbstractModule *module)
{
- Builtins::instance()->builtinmodules[name] = module;
+ Builtins::instance()->rootmodule.modules[name] = module;
}
void Builtins::init(const char *name, class AbstractFunction *function)
{
- Builtins::instance()->builtinfunctions[name] = function;
+ Builtins::instance()->rootmodule.functions[name] = function;
}
extern void register_builtin_functions();
@@ -77,10 +78,28 @@ std::string Builtins::isDeprecated(const std::string &name)
return std::string();
}
+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));
+
+ 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);
+}
+
Builtins::~Builtins()
{
- BOOST_FOREACH(FunctionContainer::value_type &f, this->builtinfunctions) delete f.second;
- this->builtinfunctions.clear();
- BOOST_FOREACH(ModuleContainer::value_type &m, this->builtinmodules) delete m.second;
- this->builtinmodules.clear();
}
diff --git a/src/builtin.h b/src/builtin.h
index bc096e5..564c951 100644
--- a/src/builtin.h
+++ b/src/builtin.h
@@ -3,6 +3,7 @@
#include <string>
#include <boost/unordered_map.hpp>
+#include "module.h"
class Builtins
{
@@ -19,16 +20,17 @@ public:
const FunctionContainer &functions() { return this->builtinfunctions; }
const ModuleContainer &modules() { return this->builtinmodules; }
+ const Module &getRootModule() { return this->rootmodule; }
+
private:
- Builtins() { }
+ Builtins();
~Builtins();
+ Module rootmodule;
FunctionContainer builtinfunctions;
ModuleContainer builtinmodules;
boost::unordered_map<std::string, std::string> deprecations;
};
-extern void register_builtin(class Context &ctx);
-
#endif
diff --git a/src/cgaladv.cc b/src/cgaladv.cc
index a4cb5ec..aad3a95 100644
--- a/src/cgaladv.cc
+++ b/src/cgaladv.cc
@@ -26,7 +26,7 @@
#include "cgaladvnode.h"
#include "module.h"
-#include "context.h"
+#include "evalcontext.h"
#include "builtin.h"
#include "PolySetEvaluator.h"
#include <sstream>
@@ -39,30 +39,29 @@ 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;
+ virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
-AbstractNode *CgaladvModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
+AbstractNode *CgaladvModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
CgaladvNode *node = new CgaladvNode(inst, type);
- std::vector<std::string> argnames;
- std::vector<Expression*> argexpr;
+ AssignmentList args;
if (type == MINKOWSKI)
- argnames += "convexity";
+ args += Assignment("convexity", NULL);
if (type == GLIDE)
- argnames += "path", "convexity";
+ args += Assignment("path", NULL), Assignment("convexity", NULL);
if (type == SUBDIV)
- argnames += "type", "level", "convexity";
+ args += Assignment("type", NULL), Assignment("level", NULL), Assignment("convexity", NULL);
if (type == RESIZE)
- argnames += "newsize", "auto";
+ args += Assignment("newsize", NULL), Assignment("auto", NULL);
Context c(ctx);
- c.args(argnames, argexpr, inst->argnames, inst->argvalues);
+ c.setVariables(args, evalctx);
Value convexity, path, subdiv_type, level;
@@ -111,7 +110,7 @@ AbstractNode *CgaladvModule::evaluate(const Context *ctx, const ModuleInstantiat
if (node->level <= 1)
node->level = 1;
- std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx);
node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
return node;
diff --git a/src/color.cc b/src/color.cc
index acca652..6c3aaef 100644
--- a/src/color.cc
+++ b/src/color.cc
@@ -26,7 +26,7 @@
#include "colornode.h"
#include "module.h"
-#include "context.h"
+#include "evalcontext.h"
#include "builtin.h"
#include "printutils.h"
#include <sstream>
@@ -40,27 +40,26 @@ class ColorModule : public AbstractModule
{
public:
ColorModule() { }
- virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
+ virtual AbstractNode *evaluate(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
+AbstractNode *ColorModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
ColorNode *node = new ColorNode(inst);
node->color[0] = node->color[1] = node->color[2] = -1.0;
node->color[3] = 1.0;
- std::vector<std::string> argnames;
- std::vector<Expression*> argexpr;
+ AssignmentList args;
- argnames += "c", "alpha";
+ args += Assignment("c", NULL), Assignment("alpha", NULL);
Context c(ctx);
- c.args(argnames, argexpr, inst->argnames, inst->argvalues);
+ c.setVariables(args, evalctx);
Value v = c.lookup_variable("c");
if (v.type() == Value::VECTOR) {
@@ -88,7 +87,7 @@ AbstractNode *ColorModule::evaluate(const Context *ctx, const ModuleInstantiatio
node->color[3] = alpha.toDouble();
}
- std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx);
node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
return node;
diff --git a/src/context.cc b/src/context.cc
index 97ea5b9..706407c 100644
--- a/src/context.cc
+++ b/src/context.cc
@@ -25,6 +25,7 @@
*/
#include "context.h"
+#include "evalcontext.h"
#include "expression.h"
#include "function.h"
#include "module.h"
@@ -40,26 +41,11 @@ std::vector<const Context*> Context::ctx_stack;
/*!
Initializes this context. Optionally initializes a context for an external library
*/
-Context::Context(const Context *parent, const Module *library)
- : parent(parent), inst_p(NULL)
+Context::Context(const Context *parent)
+ : parent(parent)
{
- if (parent) recursioncount = parent->recursioncount;
ctx_stack.push_back(this);
if (parent) document_path = parent->document_path;
- if (library) {
- // FIXME: Don't access module members directly
- this->functions_p = &library->functions;
- this->modules_p = &library->modules;
- this->usedlibs_p = &library->usedlibs;
- BOOST_FOREACH(const std::string &var, library->assignments_var) {
- this->set_variable(var, library->assignments.at(var)->evaluate(this));
- }
- }
- else {
- functions_p = NULL;
- modules_p = NULL;
- usedlibs_p = NULL;
- }
}
Context::~Context()
@@ -68,25 +54,26 @@ Context::~Context()
}
/*!
- Initialize context from argument lists (function call/module instantiation)
- */
-void Context::args(const std::vector<std::string> &argnames,
- const std::vector<Expression*> &argexpr,
- const std::vector<std::string> &call_argnames,
- const std::vector<Value> &call_argvalues)
+ Initialize context from a module argument list and a evaluation context
+ which may pass variables which will be preferred over default values.
+*/
+void Context::setVariables(const AssignmentList &args,
+ const EvalContext *evalctx)
{
- for (size_t i=0; i<argnames.size(); i++) {
- set_variable(argnames[i], i < argexpr.size() && argexpr[i] ?
- argexpr[i]->evaluate(this->parent) : Value());
+ BOOST_FOREACH(const Assignment &arg, args) {
+ set_variable(arg.first, arg.second ? arg.second->evaluate(this->parent) : Value());
}
- size_t posarg = 0;
- for (size_t i=0; i<call_argnames.size(); i++) {
- if (call_argnames[i].empty()) {
- if (posarg < argnames.size())
- set_variable(argnames[posarg++], call_argvalues[i]);
- } else {
- set_variable(call_argnames[i], call_argvalues[i]);
+ 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;
+ if (name.empty()) {
+ if (posarg < args.size()) this->set_variable(args[posarg++].first, val);
+ } else {
+ this->set_variable(name, val);
+ }
}
}
}
@@ -130,61 +117,16 @@ Value Context::lookup_variable(const std::string &name, bool silent) const
return Value();
}
-class RecursionGuard
-{
-public:
- RecursionGuard(const Context &c, const std::string &name) : c(c), name(name) { c.recursioncount[name]++; }
- ~RecursionGuard() { if (--c.recursioncount[name] == 0) c.recursioncount.erase(name); }
- bool recursion_detected() const { return (c.recursioncount[name] > 100); }
-private:
- const Context &c;
- const std::string &name;
-};
-
-Value Context::evaluate_function(const std::string &name,
- const std::vector<std::string> &argnames,
- const std::vector<Value> &argvalues) const
+Value Context::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();
- }
- if (this->functions_p && this->functions_p->find(name) != this->functions_p->end())
- return this->functions_p->find(name)->second->evaluate(this, argnames, argvalues);
- if (this->usedlibs_p) {
- BOOST_FOREACH(const ModuleContainer::value_type &m, *this->usedlibs_p) {
- if (m.second->functions.find(name) != m.second->functions.end()) {
- Context ctx(this->parent, m.second);
- return m.second->functions[name]->evaluate(&ctx, argnames, argvalues);
- }
- }
- }
- if (this->parent) return this->parent->evaluate_function(name, argnames, argvalues);
+ if (this->parent) return this->parent->evaluate_function(name, evalctx);
PRINTB("WARNING: Ignoring unknown function '%s'.", name);
return Value();
}
-AbstractNode *Context::evaluate_module(const ModuleInstantiation &inst) const
+AbstractNode *Context::evaluate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) 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 (!replacement.empty()) {
- PRINTB("DEPRECATED: The %s() module will be removed in future releases. Use %s() instead.", inst.name() % replacement);
- }
- return m->evaluate(this, &inst);
- }
- 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()) {
- Context ctx(this->parent, m.second);
- return m.second->modules[inst.name()]->evaluate(&ctx, &inst);
- }
- }
- }
- if (this->parent) return this->parent->evaluate_module(inst);
+ if (this->parent) return this->parent->evaluate_module(inst, evalctx);
PRINTB("WARNING: Ignoring unknown module '%s'.", inst.name());
return NULL;
}
@@ -202,22 +144,34 @@ std::string Context::getAbsolutePath(const std::string &filename) const
}
}
-void register_builtin(Context &ctx)
+#ifdef DEBUG
+void Context::dump(const AbstractModule *mod, const ModuleInstantiation *inst)
{
- ctx.functions_p = &Builtins::instance()->functions();
- ctx.modules_p = &Builtins::instance()->modules();
- ctx.set_variable("$fn", Value(0.0));
- ctx.set_variable("$fs", Value(2.0));
- ctx.set_variable("$fa", Value(12.0));
- ctx.set_variable("$t", 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);
- ctx.set_variable("$vpt", zero3val);
- ctx.set_variable("$vpr", zero3val);
+ if (inst)
+ PRINTB("ModuleContext %p (%p) for %s inst (%p)", this % this->parent % inst->name() % inst);
+ else
+ PRINTB("Context: %p (%p)", this % this->parent);
+ PRINTB(" document path: %s", this->document_path);
+ if (mod) {
+ const Module *m = dynamic_cast<const Module*>(mod);
+ if (m) {
+ PRINT(" module args:");
+ BOOST_FOREACH(const Assignment &arg, m->definition_arguments) {
+ PRINTB(" %s = %s", arg.first % variables[arg.first]);
+ }
+ }
+ }
+ typedef std::pair<std::string, Value> ValueMapType;
+ PRINT(" vars:");
+ BOOST_FOREACH(const ValueMapType &v, constants) {
+ PRINTB(" %s = %s", v.first % v.second);
+ }
+ BOOST_FOREACH(const ValueMapType &v, variables) {
+ PRINTB(" %s = %s", v.first % v.second);
+ }
+ BOOST_FOREACH(const ValueMapType &v, config_variables) {
+ PRINTB(" %s = %s", v.first % v.second);
+ }
- ctx.set_constant("PI",Value(M_PI));
}
+#endif
diff --git a/src/context.h b/src/context.h
index eb9a175..51fc45c 100644
--- a/src/context.h
+++ b/src/context.h
@@ -5,48 +5,46 @@
#include <vector>
#include <boost/unordered_map.hpp>
#include "value.h"
+#include "typedefs.h"
class Context
{
public:
- Context(const Context *parent = NULL, const class Module *library = NULL);
- ~Context();
+ Context(const Context *parent = NULL);
+ virtual ~Context();
- void args(const std::vector<std::string> &argnames,
- const std::vector<class Expression*> &argexpr,
- const std::vector<std::string> &call_argnames,
- const std::vector<Value> &call_argvalues);
+ 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;
+
+ void setVariables(const AssignmentList &args,
+ const class EvalContext *evalctx = NULL);
void set_variable(const std::string &name, const Value &value);
void set_constant(const std::string &name, const Value &value);
Value lookup_variable(const std::string &name, bool silent = false) const;
- Value evaluate_function(const std::string &name,
- const std::vector<std::string> &argnames,
- const std::vector<Value> &argvalues) const;
- class AbstractNode *evaluate_module(const class ModuleInstantiation &inst) const;
void setDocumentPath(const std::string &path) { this->document_path = path; }
+ const std::string &documentPath() const { return this->document_path; }
std::string getAbsolutePath(const std::string &filename) const;
public:
const Context *parent;
- 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 ModuleInstantiation *inst_p;
static std::vector<const Context*> ctx_stack;
- mutable boost::unordered_map<std::string, int> recursioncount;
-
-private:
+protected:
typedef boost::unordered_map<std::string, Value> ValueMap;
ValueMap constants;
ValueMap variables;
ValueMap config_variables;
+
std::string document_path;
+
+#ifdef DEBUG
+public:
+ virtual void dump(const class AbstractModule *mod, const ModuleInstantiation *inst);
+#endif
};
#endif
diff --git a/src/control.cc b/src/control.cc
index 44847f5..d47eec0 100644
--- a/src/control.cc
+++ b/src/control.cc
@@ -26,7 +26,8 @@
#include "module.h"
#include "node.h"
-#include "context.h"
+#include "evalcontext.h"
+#include "modcontext.h"
#include "builtin.h"
#include "printutils.h"
#include <sstream>
@@ -45,18 +46,16 @@ 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;
+ virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
void for_eval(AbstractNode &node, const ModuleInstantiation &inst, size_t l,
- const std::vector<std::string> &call_argnames,
- const std::vector<Value> &call_argvalues,
- const Context *arg_context)
+ const Context *ctx, const EvalContext *evalctx)
{
- if (call_argnames.size() > l) {
- const std::string &it_name = call_argnames[l];
- const Value &it_values = call_argvalues[l];
- Context c(arg_context);
+ 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;
+ Context c(ctx);
if (it_values.type() == Value::RANGE) {
Value::RangeType range = it_values.toRange();
if (range.end < range.begin) {
@@ -67,55 +66,69 @@ void for_eval(AbstractNode &node, const ModuleInstantiation &inst, size_t l,
if (range.step > 0 && (range.begin-range.end)/range.step < 10000) {
for (double i = range.begin; i <= range.end; i += range.step) {
c.set_variable(it_name, Value(i));
- for_eval(node, inst, l+1, call_argnames, call_argvalues, &c);
+ for_eval(node, inst, l+1, &c, evalctx);
}
}
}
else if (it_values.type() == Value::VECTOR) {
for (size_t i = 0; i < it_values.toVector().size(); i++) {
c.set_variable(it_name, it_values.toVector()[i]);
- for_eval(node, inst, l+1, call_argnames, call_argvalues, &c);
+ for_eval(node, inst, l+1, &c, evalctx);
}
}
else if (it_values.type() != Value::UNDEFINED) {
c.set_variable(it_name, it_values);
- for_eval(node, inst, l+1, call_argnames, call_argvalues, &c);
+ for_eval(node, inst, l+1, &c, evalctx);
}
} else if (l > 0) {
- std::vector<AbstractNode *> evaluatednodes = inst.evaluateChildren(arg_context);
+ std::vector<AbstractNode *> evaluatednodes = inst.evaluateChildren(ctx);
node.children.insert(node.children.end(), evaluatednodes.begin(), evaluatednodes.end());
}
}
-AbstractNode *ControlModule::evaluate(const Context*, const ModuleInstantiation *inst) const
+AbstractNode *ControlModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
AbstractNode *node = NULL;
if (type == CHILD)
{
- size_t n = 0;
- if (inst->argvalues.size() > 0) {
+ int n = 0;
+ if (evalctx->eval_arguments.size() > 0) {
double v;
- if (inst->argvalues[0].getDouble(v)) {
- if (v < 0) return NULL; // Disallow negative child indices
+ if (evalctx->eval_arguments[0].second.getDouble(v)) {
n = trunc(v);
+ if (n < 0) {
+ PRINTB("WARNING: Negative child index (%d) not allowed", n);
+ return NULL; // Disallow negative child indices
+ }
}
}
- for (int i = Context::ctx_stack.size()-1; i >= 0; i--) {
- const Context *c = Context::ctx_stack[i];
- if (c->inst_p) {
- if (n < c->inst_p->children.size()) {
- node = c->inst_p->children[n]->evaluate(c->inst_p->ctx);
- // FIXME: We'd like to inherit any tags from the ModuleInstantiation
- // given as parameter to this method. However, the instantition which belongs
- // to the returned node cannot be changed. This causes the test
- // features/child-background.scad to fail.
+
+ // Find the last custom module invocation, which will contain
+ // an eval context with the children of the module invokation
+ const Context *tmpc = evalctx;
+ while (tmpc->parent) {
+ const ModuleContext *filectx = dynamic_cast<const ModuleContext*>(tmpc->parent);
+ if (filectx) {
+ // This will trigger if trying to invoke child from the root of any file
+ // assert(filectx->evalctx);
+
+ if (filectx->evalctx) {
+ if (n < filectx->evalctx->children.size()) {
+ node = filectx->evalctx->children[n]->evaluate_instance(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());
+ }
}
return node;
}
- c = c->parent;
+ tmpc = tmpc->parent;
}
- return NULL;
+ return node;
}
if (type == INT_FOR)
@@ -127,20 +140,20 @@ AbstractNode *ControlModule::evaluate(const Context*, const ModuleInstantiation
{
std::stringstream msg;
msg << "ECHO: ";
- for (size_t i = 0; i < inst->argnames.size(); i++) {
+ for (size_t i = 0; i < inst->arguments.size(); i++) {
if (i > 0) msg << ", ";
- if (!inst->argnames[i].empty()) msg << inst->argnames[i] << " = ";
- msg << inst->argvalues[i];
+ if (!evalctx->eval_arguments[i].first.empty()) msg << evalctx->eval_arguments[i].first << " = ";
+ msg << evalctx->eval_arguments[i].second;
}
PRINTB("%s", msg.str());
}
if (type == ASSIGN)
{
- Context c(inst->ctx);
- for (size_t i = 0; i < inst->argnames.size(); i++) {
- if (!inst->argnames[i].empty())
- c.set_variable(inst->argnames[i], inst->argvalues[i]);
+ 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);
}
std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(&c);
node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
@@ -148,18 +161,18 @@ AbstractNode *ControlModule::evaluate(const Context*, const ModuleInstantiation
if (type == FOR || type == INT_FOR)
{
- for_eval(*node, *inst, 0, inst->argnames, inst->argvalues, inst->ctx);
+ for_eval(*node, *inst, 0, evalctx, evalctx);
}
if (type == IF)
{
const IfElseModuleInstantiation *ifelse = dynamic_cast<const IfElseModuleInstantiation*>(inst);
- if (ifelse->argvalues.size() > 0 && ifelse->argvalues[0].toBool()) {
- std::vector<AbstractNode *> evaluatednodes = ifelse->evaluateChildren();
+ 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());
}
else {
- std::vector<AbstractNode *> evaluatednodes = ifelse->evaluateElseChildren();
+ std::vector<AbstractNode *> evaluatednodes = ifelse->evaluateElseChildren(evalctx);
node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
}
}
diff --git a/src/csgops.cc b/src/csgops.cc
index 7524559..425b747 100644
--- a/src/csgops.cc
+++ b/src/csgops.cc
@@ -26,6 +26,7 @@
#include "csgnode.h"
+#include "evalcontext.h"
#include "module.h"
#include "csgterm.h"
#include "builtin.h"
@@ -37,13 +38,13 @@ 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;
+ virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
-AbstractNode *CsgModule::evaluate(const Context*, const ModuleInstantiation *inst) const
+AbstractNode *CsgModule::evaluate(const Context*, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
CsgNode *node = new CsgNode(inst, type);
- std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx);
node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
return node;
}
diff --git a/src/dxfdim.cc b/src/dxfdim.cc
index 1ed37fa..fbc24c4 100644
--- a/src/dxfdim.cc
+++ b/src/dxfdim.cc
@@ -30,7 +30,7 @@
#include "dxfdata.h"
#include "builtin.h"
#include "printutils.h"
-#include "context.h"
+#include "evalcontext.h"
#include "mathc99.h"
#include <sstream>
@@ -40,7 +40,7 @@ boost::unordered_map<std::string,Value> dxf_dim_cache;
boost::unordered_map<std::string,Value> dxf_cross_cache;
namespace fs = boost::filesystem;
-Value builtin_dxf_dim(const Context *ctx, const std::vector<std::string> &argnames, const std::vector<Value> &args)
+Value builtin_dxf_dim(const Context *ctx, const EvalContext *evalctx)
{
std::string filename;
std::string layername;
@@ -49,23 +49,33 @@ Value builtin_dxf_dim(const Context *ctx, const std::vector<std::string> &argnam
double yorigin = 0;
double scale = 1;
- for (size_t i = 0; i < argnames.size() && i < args.size(); i++) {
- if (argnames[i] == "file")
- filename = ctx->getAbsolutePath(args[i].toString());
- if (argnames[i] == "layer")
- layername = args[i].toString();
- if (argnames[i] == "origin")
- args[i].getVec2(xorigin, yorigin);
- if (argnames[i] == "scale")
- args[i].getDouble(scale);
- if (argnames[i] == "name")
- name = args[i].toString();
+ // 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();
}
std::stringstream keystream;
+ fs::path filepath(filename);
+ uintmax_t filesize = -1;
+ time_t lastwritetime = -1;
+ if (fs::exists(filepath) && fs::is_regular_file(filepath)) {
+ filesize = fs::file_size(filepath);
+ lastwritetime = fs::last_write_time(filepath);
+ }
keystream << filename << "|" << layername << "|" << name << "|" << xorigin
- << "|" << yorigin <<"|" << scale << "|" << fs::last_write_time(filename)
- << "|" << fs::file_size(filename);
+ << "|" << yorigin <<"|" << scale << "|" << lastwritetime
+ << "|" << filesize;
std::string key = keystream.str();
if (dxf_dim_cache.find(key) != dxf_dim_cache.end())
return dxf_dim_cache.find(key)->second;
@@ -125,7 +135,7 @@ Value builtin_dxf_dim(const Context *ctx, const std::vector<std::string> &argnam
return Value();
}
-Value builtin_dxf_cross(const Context *ctx, const std::vector<std::string> &argnames, const std::vector<Value> &args)
+Value builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx)
{
std::string filename;
std::string layername;
@@ -133,15 +143,18 @@ Value builtin_dxf_cross(const Context *ctx, const std::vector<std::string> &argn
double yorigin = 0;
double scale = 1;
- for (size_t i = 0; i < argnames.size() && i < args.size(); i++) {
- if (argnames[i] == "file")
- filename = ctx->getAbsolutePath(args[i].toString());
- if (argnames[i] == "layer")
- layername = args[i].toString();
- if (argnames[i] == "origin")
- args[i].getVec2(xorigin, yorigin);
- if (argnames[i] == "scale")
- args[i].getDouble(scale);
+ // 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);
}
std::stringstream keystream;
diff --git a/src/evalcontext.cc b/src/evalcontext.cc
new file mode 100644
index 0000000..b7566e3
--- /dev/null
+++ b/src/evalcontext.cc
@@ -0,0 +1,40 @@
+#include "evalcontext.h"
+#include "module.h"
+#include "expression.h"
+#include "function.h"
+#include "printutils.h"
+#include "builtin.h"
+
+#include <boost/foreach.hpp>
+
+#ifdef DEBUG
+void EvalContext::dump(const AbstractModule *mod, const ModuleInstantiation *inst)
+{
+ if (inst)
+ PRINTB("EvalContext %p (%p) for %s inst (%p)", this % this->parent % inst->name() % inst);
+ else
+ PRINTB("Context: %p (%p)", this % this->parent);
+ PRINTB(" document path: %s", this->document_path);
+
+ PRINT(" eval args:");
+ 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) {
+ PRINT(" children:");
+ BOOST_FOREACH(const ModuleInstantiation *ch, this->children) {
+ PRINTB(" %s", ch->name());
+ }
+ }
+
+ if (mod) {
+ const Module *m = dynamic_cast<const Module*>(mod);
+ if (m) {
+ PRINT(" module args:");
+ BOOST_FOREACH(const Assignment &arg, m->definition_arguments) {
+ PRINTB(" %s = %s", arg.first % variables[arg.first]);
+ }
+ }
+ }
+}
+#endif
diff --git a/src/evalcontext.h b/src/evalcontext.h
new file mode 100644
index 0000000..3d7d222
--- /dev/null
+++ b/src/evalcontext.h
@@ -0,0 +1,24 @@
+#ifndef EVALCONTEXT_H_
+#define EVALCONTEXT_H_
+
+#include "context.h"
+
+/*!
+ This hold the evaluation context (the parameters actually sent
+ when calling a module or function, including the children).
+*/
+class EvalContext : public Context
+{
+public:
+ EvalContext(const Context *parent = NULL) : Context(parent) {}
+ virtual ~EvalContext() {}
+
+ std::vector<std::pair<std::string, Value> > eval_arguments;
+ std::vector<class ModuleInstantiation *> children;
+
+#ifdef DEBUG
+ virtual void dump(const class AbstractModule *mod, const ModuleInstantiation *inst);
+#endif
+};
+
+#endif
diff --git a/src/expr.cc b/src/expr.cc
index 75fc47a..7a8180f 100644
--- a/src/expr.cc
+++ b/src/expr.cc
@@ -26,7 +26,7 @@
#include "expression.h"
#include "value.h"
-#include "context.h"
+#include "evalcontext.h"
#include <assert.h>
#include <sstream>
#include <algorithm>
@@ -127,13 +127,18 @@ Value Expression::evaluate(const Context *context) const
return Value();
}
if (this->type == "F") {
- Value::VectorType argvalues;
- std::transform(this->children.begin(), this->children.end(),
- std::back_inserter(argvalues),
- boost::bind(&Expression::evaluate, _1, context));
+ 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));
- return context->evaluate_function(this->call_funcname, this->call_argnames, argvalues);
+ return context->evaluate_function(this->call_funcname, &c);
}
abort();
}
@@ -178,10 +183,11 @@ std::string Expression::toString() const
}
else if (this->type == "F") {
stream << this->call_funcname << "(";
- for (size_t i=0; i < this->children.size(); i++) {
+ for (size_t i=0; i < this->call_arguments.size(); i++) {
+ const Assignment &arg = this->call_arguments[i];
if (i > 0) stream << ", ";
- if (!this->call_argnames[i].empty()) stream << this->call_argnames[i] << " = ";
- stream << *this->children[i];
+ if (!arg.first.empty()) stream << arg.first << " = ";
+ stream << *arg.second;
}
stream << ")";
}
diff --git a/src/expression.h b/src/expression.h
index 2919b78..06becc0 100644
--- a/src/expression.h
+++ b/src/expression.h
@@ -4,6 +4,7 @@
#include <string>
#include <vector>
#include "value.h"
+#include "typedefs.h"
class Expression
{
@@ -14,7 +15,7 @@ public:
std::string var_name;
std::string call_funcname;
- std::vector<std::string> call_argnames;
+ AssignmentList call_arguments;
// Boolean: ! && ||
// Operators: * / % + -
diff --git a/src/func.cc b/src/func.cc
index 26d7e69..88bf3c8 100644
--- a/src/func.cc
+++ b/src/func.cc
@@ -26,7 +26,7 @@
#include "function.h"
#include "expression.h"
-#include "context.h"
+#include "evalcontext.h"
#include "builtin.h"
#include <sstream>
#include <ctime>
@@ -34,6 +34,7 @@
#include <algorithm>
#include "stl-utils.h"
#include "printutils.h"
+#include <boost/foreach.hpp>
/*
Random numbers
@@ -61,7 +62,7 @@ AbstractFunction::~AbstractFunction()
{
}
-Value AbstractFunction::evaluate(const Context*, const std::vector<std::string>&, const std::vector<Value>&) const
+Value AbstractFunction::evaluate(const Context*, const EvalContext *evalctx) const
{
return Value();
}
@@ -75,16 +76,14 @@ std::string AbstractFunction::dump(const std::string &indent, const std::string
Function::~Function()
{
- std::for_each(this->argexpr.begin(), this->argexpr.end(), del_fun<Expression>());
+ BOOST_FOREACH(const Assignment &arg, this->definition_arguments) delete arg.second;
delete expr;
}
-Value Function::evaluate(const Context *ctx,
- const std::vector<std::string> &call_argnames,
- const std::vector<Value> &call_argvalues) const
+Value Function::evaluate(const Context *ctx, const EvalContext *evalctx) const
{
Context c(ctx);
- c.args(argnames, argexpr, call_argnames, call_argvalues);
+ c.setVariables(definition_arguments, evalctx);
return expr ? expr->evaluate(&c) : Value();
}
@@ -92,10 +91,11 @@ std::string Function::dump(const std::string &indent, const std::string &name) c
{
std::stringstream dump;
dump << indent << "function " << name << "(";
- for (size_t i=0; i < argnames.size(); i++) {
+ for (size_t i=0; i < definition_arguments.size(); i++) {
+ const Assignment &arg = definition_arguments[i];
if (i > 0) dump << ", ";
- dump << argnames[i];
- if (argexpr[i]) dump << " = " << *argexpr[i];
+ dump << arg.first;
+ if (arg.second) dump << " = " << *arg.second;
}
dump << ") = " << *expr << ";\n";
return dump.str();
@@ -105,9 +105,9 @@ BuiltinFunction::~BuiltinFunction()
{
}
-Value BuiltinFunction::evaluate(const Context *ctx, const std::vector<std::string> &call_argnames, const std::vector<Value> &call_argvalues) const
+Value BuiltinFunction::evaluate(const Context *ctx, const EvalContext *evalctx) const
{
- return eval_func(ctx, call_argnames, call_argvalues);
+ return eval_func(ctx, evalctx);
}
std::string BuiltinFunction::dump(const std::string &indent, const std::string &name) const
@@ -127,37 +127,37 @@ static inline double rad2deg(double x)
return x * 180.0 / M_PI;
}
-Value builtin_abs(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_abs(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(fabs(args[0].toDouble()));
+ if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER)
+ return Value(fabs(evalctx->eval_arguments[0].second.toDouble()));
return Value();
}
-Value builtin_sign(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_sign(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value((args[0].toDouble()<0) ? -1.0 : ((args[0].toDouble()>0) ? 1.0 : 0.0));
+ 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));
return Value();
}
-Value builtin_rands(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_rands(const Context *, const EvalContext *evalctx)
{
bool deterministic = false;
- if (args.size() == 3 &&
- args[0].type() == Value::NUMBER &&
- args[1].type() == Value::NUMBER &&
- args[2].type() == Value::NUMBER)
+ 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)
{
deterministic = false;
}
- else if (args.size() == 4 &&
- args[0].type() == Value::NUMBER &&
- args[1].type() == Value::NUMBER &&
- args[2].type() == Value::NUMBER &&
- args[3].type() == Value::NUMBER)
+ 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)
{
- deterministic_rng.seed( (unsigned int) args[3].toDouble() );
+ deterministic_rng.seed( (unsigned int) evalctx->eval_arguments[3].second.toDouble() );
deterministic = true;
}
else
@@ -165,11 +165,11 @@ Value builtin_rands(const Context *, const std::vector<std::string>&, const std:
return Value();
}
- double min = std::min( args[0].toDouble(), args[1].toDouble() );
- double max = std::max( args[0].toDouble(), args[1].toDouble() );
+ 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() );
boost::uniform_real<> distributor( min, max );
Value::VectorType vec;
- for (int i=0; i<args[2].toDouble(); i++) {
+ for (int i=0; i<evalctx->eval_arguments[2].second.toDouble(); i++) {
if ( deterministic ) {
vec.push_back( Value( distributor( deterministic_rng ) ) );
} else {
@@ -181,169 +181,169 @@ Value builtin_rands(const Context *, const std::vector<std::string>&, const std:
}
-Value builtin_min(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_min(const Context *, const EvalContext *evalctx)
{
- if (args.size() >= 1 && args[0].type() == Value::NUMBER) {
- double val = args[0].toDouble();
- for (size_t i = 1; i < args.size(); i++)
- if (args[1].type() == Value::NUMBER)
- val = fmin(val, args[i].toDouble());
+ 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());
return Value(val);
}
return Value();
}
-Value builtin_max(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_max(const Context *, const EvalContext *evalctx)
{
- if (args.size() >= 1 && args[0].type() == Value::NUMBER) {
- double val = args[0].toDouble();
- for (size_t i = 1; i < args.size(); i++)
- if (args[1].type() == Value::NUMBER)
- val = fmax(val, args[i].toDouble());
+ 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());
return Value(val);
}
return Value();
}
-Value builtin_sin(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_sin(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(sin(deg2rad(args[0].toDouble())));
+ if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER)
+ return Value(sin(deg2rad(evalctx->eval_arguments[0].second.toDouble())));
return Value();
}
-Value builtin_cos(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_cos(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(cos(deg2rad(args[0].toDouble())));
+ if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER)
+ return Value(cos(deg2rad(evalctx->eval_arguments[0].second.toDouble())));
return Value();
}
-Value builtin_asin(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_asin(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(rad2deg(asin(args[0].toDouble())));
+ if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER)
+ return Value(rad2deg(asin(evalctx->eval_arguments[0].second.toDouble())));
return Value();
}
-Value builtin_acos(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_acos(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(rad2deg(acos(args[0].toDouble())));
+ if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER)
+ return Value(rad2deg(acos(evalctx->eval_arguments[0].second.toDouble())));
return Value();
}
-Value builtin_tan(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_tan(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(tan(deg2rad(args[0].toDouble())));
+ if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER)
+ return Value(tan(deg2rad(evalctx->eval_arguments[0].second.toDouble())));
return Value();
}
-Value builtin_atan(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_atan(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(rad2deg(atan(args[0].toDouble())));
+ if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER)
+ return Value(rad2deg(atan(evalctx->eval_arguments[0].second.toDouble())));
return Value();
}
-Value builtin_atan2(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_atan2(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 2 && args[0].type() == Value::NUMBER && args[1].type() == Value::NUMBER)
- return Value(rad2deg(atan2(args[0].toDouble(), args[1].toDouble())));
+ 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())));
return Value();
}
-Value builtin_pow(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_pow(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 2 && args[0].type() == Value::NUMBER && args[1].type() == Value::NUMBER)
- return Value(pow(args[0].toDouble(), args[1].toDouble()));
+ 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()));
return Value();
}
-Value builtin_round(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_round(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(round(args[0].toDouble()));
+ if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER)
+ return Value(round(evalctx->eval_arguments[0].second.toDouble()));
return Value();
}
-Value builtin_ceil(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_ceil(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(ceil(args[0].toDouble()));
+ if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER)
+ return Value(ceil(evalctx->eval_arguments[0].second.toDouble()));
return Value();
}
-Value builtin_floor(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_floor(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(floor(args[0].toDouble()));
+ if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER)
+ return Value(floor(evalctx->eval_arguments[0].second.toDouble()));
return Value();
}
-Value builtin_sqrt(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_sqrt(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(sqrt(args[0].toDouble()));
+ if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER)
+ return Value(sqrt(evalctx->eval_arguments[0].second.toDouble()));
return Value();
}
-Value builtin_exp(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_exp(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(exp(args[0].toDouble()));
+ if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER)
+ return Value(exp(evalctx->eval_arguments[0].second.toDouble()));
return Value();
}
-Value builtin_length(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_length(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1) {
- if (args[0].type() == Value::VECTOR) return Value(int(args[0].toVector().size()));
- if (args[0].type() == Value::STRING) return Value(int(args[0].toString().size()));
+ 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()));
}
return Value();
}
-Value builtin_log(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_log(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 2 && args[0].type() == Value::NUMBER && args[1].type() == Value::NUMBER)
- return Value(log(args[1].toDouble()) / log(args[0].toDouble()));
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(log(args[0].toDouble()) / log(10.0));
+ 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));
return Value();
}
-Value builtin_ln(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_ln(const Context *, const EvalContext *evalctx)
{
- if (args.size() == 1 && args[0].type() == Value::NUMBER)
- return Value(log(args[0].toDouble()));
+ if (evalctx->eval_arguments.size() == 1 && evalctx->eval_arguments[0].second.type() == Value::NUMBER)
+ return Value(log(evalctx->eval_arguments[0].second.toDouble()));
return Value();
}
-Value builtin_str(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_str(const Context *, const EvalContext *evalctx)
{
std::stringstream stream;
- for (size_t i = 0; i < args.size(); i++) {
- stream << args[i].toString();
+ for (size_t i = 0; i < evalctx->eval_arguments.size(); i++) {
+ stream << evalctx->eval_arguments[i].second.toString();
}
return Value(stream.str());
}
-Value builtin_lookup(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_lookup(const Context *, const EvalContext *evalctx)
{
double p, low_p, low_v, high_p, high_v;
- if (args.size() < 2 || // Needs two args
- !args[0].getDouble(p) || // First must be a number
- args[1].toVector().size() < 2 || // Second must be a vector of vectors
- args[1].toVector()[0].toVector().size() < 2)
+ 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)
return Value();
- if (!args[1].toVector()[0].getVec2(low_p, low_v) || !args[1].toVector()[0].getVec2(high_p, high_v))
+ 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))
return Value();
- for (size_t i = 1; i < args[1].toVector().size(); i++) {
+ for (size_t i = 1; i < evalctx->eval_arguments[1].second.toVector().size(); i++) {
double this_p, this_v;
- if (args[1].toVector()[i].getVec2(this_p, this_v)) {
+ if (evalctx->eval_arguments[1].second.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;
@@ -404,14 +404,14 @@ Value builtin_lookup(const Context *, const std::vector<std::string>&, const std
- returns [[0,4],[1,5],[2,6],[8]]
*/
-Value builtin_search(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+Value builtin_search(const Context *, const EvalContext *evalctx)
{
- if (args.size() < 2) return Value();
+ if (evalctx->eval_arguments.size() < 2) return Value();
- const Value &findThis = args[0];
- const Value &searchTable = args[1];
- unsigned int num_returns_per_match = (args.size() > 2) ? args[2].toDouble() : 1;
- unsigned int index_col_num = (args.size() > 3) ? args[3].toDouble() : 0;
+ 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;
Value::VectorType returnvec;
@@ -499,7 +499,7 @@ Value builtin_search(const Context *, const std::vector<std::string>&, const std
#define QUOTE(x__) # x__
#define QUOTED(x__) QUOTE(x__)
-Value builtin_version(const Context *, const std::vector<std::string>&, const std::vector<Value> &)
+Value builtin_version(const Context *, const EvalContext *evalctx)
{
Value::VectorType val;
val.push_back(Value(double(OPENSCAD_YEAR)));
@@ -510,9 +510,9 @@ Value builtin_version(const Context *, const std::vector<std::string>&, const st
return Value(val);
}
-Value builtin_version_num(const Context *ctx, const std::vector<std::string>& call_argnames, const std::vector<Value> &args)
+Value builtin_version_num(const Context *ctx, const EvalContext *evalctx)
{
- Value val = (args.size() == 0) ? builtin_version(ctx, call_argnames, args) : args[0];
+ Value val = (evalctx->eval_arguments.size() == 0) ? builtin_version(ctx, evalctx) : evalctx->eval_arguments[0].second;
double y, m, d = 0;
if (!val.getVec3(y, m, d)) {
if (!val.getVec2(y, m)) {
diff --git a/src/function.h b/src/function.h
index 623ef7e..a1fde3c 100644
--- a/src/function.h
+++ b/src/function.h
@@ -2,6 +2,7 @@
#define FUNCTION_H_
#include "value.h"
+#include "typedefs.h"
#include <string>
#include <vector>
@@ -9,35 +10,34 @@ class AbstractFunction
{
public:
virtual ~AbstractFunction();
- virtual Value evaluate(const class Context *ctx, const std::vector<std::string> &call_argnames, const std::vector<Value> &call_argvalues) const;
+ virtual Value evaluate(const class Context *ctx, const class EvalContext *evalctx) const;
virtual std::string dump(const std::string &indent, const std::string &name) const;
};
class BuiltinFunction : public AbstractFunction
{
public:
- typedef Value (*eval_func_t)(const Context *ctx, const std::vector<std::string> &argnames, const std::vector<Value> &args);
+ typedef Value (*eval_func_t)(const Context *ctx, const EvalContext *evalctx);
eval_func_t eval_func;
BuiltinFunction(eval_func_t f) : eval_func(f) { }
virtual ~BuiltinFunction();
- virtual Value evaluate(const Context *ctx, const std::vector<std::string> &call_argnames, const std::vector<Value> &call_argvalues) const;
+ virtual Value evaluate(const Context *ctx, const EvalContext *evalctx) const;
virtual std::string dump(const std::string &indent, const std::string &name) const;
};
class Function : public AbstractFunction
{
public:
- std::vector<std::string> argnames;
- std::vector<class Expression*> argexpr;
+ AssignmentList definition_arguments;
Expression *expr;
Function() { }
virtual ~Function();
- virtual Value evaluate(const Context *ctx, const std::vector<std::string> &call_argnames, const std::vector<Value> &call_argvalues) const;
+ virtual Value evaluate(const Context *ctx, const EvalContext *evalctx) const;
virtual std::string dump(const std::string &indent, const std::string &name) const;
};
diff --git a/src/import.cc b/src/import.cc
index e693401..927c9d8 100644
--- a/src/import.cc
+++ b/src/import.cc
@@ -28,7 +28,7 @@
#include "module.h"
#include "polyset.h"
-#include "context.h"
+#include "evalcontext.h"
#include "builtin.h"
#include "dxfdata.h"
#include "dxftess.h"
@@ -60,27 +60,44 @@ 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;
+ virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
-AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
+AbstractNode *ImportModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
- std::vector<std::string> argnames;
- argnames += "file", "layer", "convexity", "origin", "scale";
- std::vector<Expression*> argexpr;
+ AssignmentList args;
+ args += Assignment("file", NULL), Assignment("layer", NULL), Assignment("convexity", NULL), Assignment("origin", NULL), Assignment("scale", NULL);
+ args += Assignment("filename",NULL), Assignment("layername", NULL);
+ // FIXME: This is broken. Tag as deprecated and fix
// Map old argnames to new argnames for compatibility
+ // To fix:
+ // o after c.setVariables()
+ // - if "filename" in evalctx: deprecated-warning && v.set_variable("file", value);
+ // - if "layername" in evalctx: deprecated-warning && v.set_variable("layer", value);
+#if 0
std::vector<std::string> inst_argnames = inst->argnames;
for (size_t i=0; i<inst_argnames.size(); i++) {
if (inst_argnames[i] == "filename") inst_argnames[i] = "file";
if (inst_argnames[i] == "layername") inst_argnames[i] = "layer";
}
+#endif
Context c(ctx);
- c.args(argnames, argexpr, inst_argnames, inst->argvalues);
+ c.setDocumentPath(evalctx->documentPath());
+ c.setVariables(args, evalctx);
+#if 0 && DEBUG
+ c.dump(this, inst);
+#endif
Value v = c.lookup_variable("file");
- std::string filename = c.getAbsolutePath(v.isUndefined() ? "" : v.toString());
+ if (v.isUndefined()) {
+ v = c.lookup_variable("filename");
+ if (!v.isUndefined())
+ PRINT("WARNING: 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) {
std::string extraw = boosty::extension_str( fs::path(filename) );
@@ -98,6 +115,11 @@ 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=");
+ }
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 4dff654..6766abc 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -26,6 +26,7 @@
%{
+#include "typedefs.h"
#include "handle_dep.h"
#include "printutils.h"
#include "parsersettings.h"
diff --git a/src/linearextrude.cc b/src/linearextrude.cc
index 43db907..f4c1112 100644
--- a/src/linearextrude.cc
+++ b/src/linearextrude.cc
@@ -27,7 +27,7 @@
#include "linearextrudenode.h"
#include "module.h"
-#include "context.h"
+#include "evalcontext.h"
#include "printutils.h"
#include "builtin.h"
#include "PolySetEvaluator.h"
@@ -45,19 +45,18 @@ class LinearExtrudeModule : public AbstractModule
{
public:
LinearExtrudeModule() { }
- virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
+ virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
-AbstractNode *LinearExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
+AbstractNode *LinearExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
LinearExtrudeNode *node = new LinearExtrudeNode(inst);
- std::vector<std::string> argnames;
- argnames += "file", "layer", "height", "origin", "scale", "center", "twist", "slices";
- std::vector<Expression*> argexpr;
+ AssignmentList args;
+ args += Assignment("file", NULL), Assignment("layer", NULL), Assignment("height", NULL), Assignment("origin", NULL), Assignment("scale", NULL), Assignment("center", NULL), Assignment("twist", NULL), Assignment("slices", NULL);
Context c(ctx);
- c.args(argnames, argexpr, inst->argnames, inst->argvalues);
+ c.setVariables(args, evalctx);
node->fn = c.lookup_variable("$fn").toDouble();
node->fs = c.lookup_variable("$fs").toDouble();
@@ -75,16 +74,16 @@ AbstractNode *LinearExtrudeModule::evaluate(const Context *ctx, const ModuleInst
if (!file.isUndefined()) {
PRINT("DEPRECATED: Support for reading files in linear_extrude will be removed in future releases. Use a child import() instead.");
- node->filename = c.getAbsolutePath(file.toString());
+ node->filename = inst->getAbsolutePath(file.toString());
}
// if height not given, and first argument is a number,
// then assume it should be the height.
if (c.lookup_variable("height").isUndefined() &&
- inst->argnames.size() > 0 &&
- inst->argnames[0] == "" &&
- inst->argvalues[0].type() == Value::NUMBER) {
- height = Value(inst->argvalues[0]);
+ 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);
}
node->layername = layer.isUndefined() ? "" : layer.toString();
@@ -117,7 +116,7 @@ AbstractNode *LinearExtrudeModule::evaluate(const Context *ctx, const ModuleInst
}
if (node->filename.empty()) {
- std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx);
node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
}
diff --git a/src/mainwin.cc b/src/mainwin.cc
index dd855fb..2e69ec2 100644
--- a/src/mainwin.cc
+++ b/src/mainwin.cc
@@ -158,7 +158,7 @@ settings_valueList(const QString &key, const QList<int> &defaultList = QList<int
}
MainWindow::MainWindow(const QString &filename)
- : progresswidget(NULL)
+ : root_inst("group"), progresswidget(NULL)
{
setupUi(this);
@@ -168,7 +168,7 @@ MainWindow::MainWindow(const QString &filename)
this, SLOT(actionRenderCGALDone(CGAL_Nef_polyhedron *)));
#endif
- register_builtin(root_ctx);
+ root_ctx.registerBuiltin();
this->openglbox = NULL;
root_module = NULL;
@@ -643,8 +643,8 @@ bool MainWindow::compile(bool reload, bool procevents)
if (procevents) QApplication::processEvents();
AbstractNode::resetIndexCounter();
- this->root_inst = ModuleInstantiation();
- this->absolute_root_node = this->root_module->evaluate(&this->root_ctx, &this->root_inst);
+ this->root_inst = ModuleInstantiation("group");
+ this->absolute_root_node = this->root_module->evaluate(&this->root_ctx, &this->root_inst, NULL);
if (this->absolute_root_node) {
// Do we have an explicit root node (! modifier)?
diff --git a/src/modcontext.cc b/src/modcontext.cc
new file mode 100644
index 0000000..7d123b8
--- /dev/null
+++ b/src/modcontext.cc
@@ -0,0 +1,152 @@
+#include "modcontext.h"
+#include "module.h"
+#include "expression.h"
+#include "function.h"
+#include "printutils.h"
+#include "builtin.h"
+
+#include <boost/foreach.hpp>
+
+ModuleContext::ModuleContext(const class Module *module, const Context *parent, const EvalContext *evalctx)
+ : Context(parent)
+{
+ 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)
+{
+ 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));
+ }
+
+ 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
+{
+public:
+ RecursionGuard(const ModuleContext &c, const std::string &name) : c(c), name(name) {
+ c.recursioncount[name]++;
+ }
+ ~RecursionGuard() { if (--c.recursioncount[name] == 0) c.recursioncount.erase(name); }
+ bool recursion_detected() const { return (c.recursioncount[name] > 100); }
+private:
+ const ModuleContext &c;
+ const std::string &name;
+};
+
+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();
+ }
+
+ 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 Context::evaluate_function(name, evalctx);
+}
+
+AbstractNode *ModuleContext::evaluate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) 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 (!replacement.empty()) {
+ PRINTB("DEPRECATED: The %s() module will be removed in future releases. Use %s() instead.", inst.name() % replacement);
+ }
+ return m->evaluate(this, &inst, evalctx);
+ }
+
+ 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);
+ }
+ }
+ }
+
+ return Context::evaluate_module(inst, evalctx);
+}
+
+#ifdef DEBUG
+void ModuleContext::dump(const AbstractModule *mod, const ModuleInstantiation *inst)
+{
+ if (inst)
+ PRINTB("ModuleContext %p (%p) for %s inst (%p) ", this % this->parent % inst->name() % inst);
+ else
+ PRINTB("ModuleContext: %p (%p)", this % this->parent);
+ PRINTB(" document path: %s", this->document_path);
+ if (mod) {
+ const Module *m = dynamic_cast<const Module*>(mod);
+ if (m) {
+ PRINT(" module args:");
+ BOOST_FOREACH(const Assignment &arg, m->definition_arguments) {
+ PRINTB(" %s = %s", arg.first % variables[arg.first]);
+ }
+ }
+ }
+ typedef std::pair<std::string, Value> ValueMapType;
+ PRINT(" vars:");
+ BOOST_FOREACH(const ValueMapType &v, constants) {
+ PRINTB(" %s = %s", v.first % v.second);
+ }
+ BOOST_FOREACH(const ValueMapType &v, variables) {
+ PRINTB(" %s = %s", v.first % v.second);
+ }
+ BOOST_FOREACH(const ValueMapType &v, config_variables) {
+ PRINTB(" %s = %s", v.first % v.second);
+ }
+
+}
+#endif
diff --git a/src/modcontext.h b/src/modcontext.h
new file mode 100644
index 0000000..5fce88f
--- /dev/null
+++ b/src/modcontext.h
@@ -0,0 +1,40 @@
+#ifndef FILECONTEXT_H_
+#define FILECONTEXT_H_
+
+#include "context.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.
+*/
+class ModuleContext : public Context
+{
+public:
+ ModuleContext(const class Module *module = NULL, const Context *parent = NULL, const EvalContext *evalctx = NULL);
+ virtual ~ModuleContext();
+
+ void setModule(const Module &module, const EvalContext *evalctx = NULL);
+ void registerBuiltin();
+
+ virtual Value evaluate_function(const std::string &name, const EvalContext *evalctx) const;
+ virtual AbstractNode *evaluate_module(const ModuleInstantiation &inst, const EvalContext *evalctx) 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;
+
+ // FIXME: Points to the eval context for the call to this module. Not sure where it belongs
+ const class EvalContext *evalctx;
+
+#ifdef DEBUG
+ virtual void dump(const class AbstractModule *mod, const ModuleInstantiation *inst);
+#endif
+
+ mutable boost::unordered_map<std::string, int> recursioncount;
+};
+
+#endif
diff --git a/src/module.cc b/src/module.cc
index e6dcb57..7d83975 100644
--- a/src/module.cc
+++ b/src/module.cc
@@ -27,11 +27,15 @@
#include "module.h"
#include "ModuleCache.h"
#include "node.h"
-#include "context.h"
+#include "modcontext.h"
+#include "evalcontext.h"
#include "expression.h"
#include "function.h"
#include "printutils.h"
+#include <boost/filesystem.hpp>
+namespace fs = boost::filesystem;
+#include "boosty.h"
#include <boost/foreach.hpp>
#include <sstream>
#include <sys/stat.h>
@@ -40,11 +44,11 @@ AbstractModule::~AbstractModule()
{
}
-AbstractNode *AbstractModule::evaluate(const Context*, const ModuleInstantiation *inst) const
+AbstractNode *AbstractModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
AbstractNode *node = new AbstractNode(inst);
- node->children = inst->evaluateChildren();
+ node->children = inst->evaluateChildren(evalctx);
return node;
}
@@ -58,8 +62,8 @@ std::string AbstractModule::dump(const std::string &indent, const std::string &n
ModuleInstantiation::~ModuleInstantiation()
{
- BOOST_FOREACH (Expression *v, argexpr) delete v;
- BOOST_FOREACH (ModuleInstantiation *v, children) delete v;
+ BOOST_FOREACH(const Assignment &arg, this->arguments) delete arg.second;
+ BOOST_FOREACH(ModuleInstantiation *v, children) delete v;
}
IfElseModuleInstantiation::~IfElseModuleInstantiation()
@@ -67,15 +71,29 @@ IfElseModuleInstantiation::~IfElseModuleInstantiation()
BOOST_FOREACH (ModuleInstantiation *v, else_children) delete v;
}
+/*!
+ Returns the absolute path to the given filename, unless it's empty.
+ */
+std::string ModuleInstantiation::getAbsolutePath(const std::string &filename) const
+{
+ if (!filename.empty() && !boosty::is_absolute(fs::path(filename))) {
+ return boosty::absolute(fs::path(this->modpath) / filename).string();
+ }
+ else {
+ return filename;
+ }
+}
+
std::string ModuleInstantiation::dump(const std::string &indent) const
{
std::stringstream dump;
dump << indent;
dump << modname + "(";
- for (size_t i=0; i < argnames.size(); i++) {
+ for (size_t i=0; i < this->arguments.size(); i++) {
+ const Assignment &arg = this->arguments[i];
if (i > 0) dump << ", ";
- if (!argnames[i].empty()) dump << argnames[i] << " = ";
- dump << *argexpr[i];
+ if (!arg.first.empty()) dump << arg.first << " = ";
+ dump << *arg.second;
}
if (children.size() == 0) {
dump << ");\n";
@@ -92,43 +110,40 @@ std::string ModuleInstantiation::dump(const std::string &indent) const
return dump.str();
}
-AbstractNode *ModuleInstantiation::evaluate(const Context *ctx) const
+AbstractNode *ModuleInstantiation::evaluate_instance(const Context *ctx) const
{
- AbstractNode *node = NULL;
- if (this->ctx) {
- PRINTB("WARNING: Ignoring recursive module instantiation of '%s'.", modname);
- } else {
- // FIXME: Casting away const..
- ModuleInstantiation *that = (ModuleInstantiation*)this;
- that->argvalues.clear();
- BOOST_FOREACH (Expression *expr, that->argexpr) {
- that->argvalues.push_back(expr->evaluate(ctx));
- }
- that->ctx = ctx;
- node = ctx->evaluate_module(*this);
- that->ctx = NULL;
- that->argvalues.clear();
+ 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;
+
+#if 0 && DEBUG
+ PRINT("New eval ctx:");
+ c.dump(NULL, this);
+#endif
+ AbstractNode *node = ctx->evaluate_module(*this, &c); // Passes c as evalctx
return node;
}
-std::vector<AbstractNode*> ModuleInstantiation::evaluateChildren(const Context *ctx) const
+std::vector<AbstractNode*> ModuleInstantiation::evaluateChildren(const Context *evalctx) const
{
- if (!ctx) ctx = this->ctx;
std::vector<AbstractNode*> childnodes;
BOOST_FOREACH (ModuleInstantiation *modinst, this->children) {
- AbstractNode *node = modinst->evaluate(ctx);
+ AbstractNode *node = modinst->evaluate_instance(evalctx);
if (node) childnodes.push_back(node);
}
return childnodes;
}
-std::vector<AbstractNode*> IfElseModuleInstantiation::evaluateElseChildren(const Context *ctx) const
+std::vector<AbstractNode*> IfElseModuleInstantiation::evaluateElseChildren(const Context *evalctx) const
{
- if (!ctx) ctx = this->ctx;
std::vector<AbstractNode*> childnodes;
BOOST_FOREACH (ModuleInstantiation *modinst, this->else_children) {
- AbstractNode *node = modinst->evaluate(ctx);
+ AbstractNode *node = modinst->evaluate_instance(evalctx);
if (node != NULL) childnodes.push_back(node);
}
return childnodes;
@@ -136,35 +151,24 @@ std::vector<AbstractNode*> IfElseModuleInstantiation::evaluateElseChildren(const
Module::~Module()
{
- BOOST_FOREACH (const AssignmentContainer::value_type &v, assignments) delete v.second;
+ 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
+AbstractNode *Module::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
- Context c(ctx);
- c.args(argnames, argexpr, inst->argnames, inst->argvalues);
-
- c.inst_p = inst;
+ ModuleContext c(this, ctx, evalctx);
+ // FIXME: Set document path to the path of the module
c.set_variable("$children", Value(double(inst->children.size())));
-
- c.functions_p = &functions;
- c.modules_p = &modules;
-
- if (!usedlibs.empty())
- c.usedlibs_p = &usedlibs;
- else
- c.usedlibs_p = NULL;
-
- BOOST_FOREACH(const std::string &var, assignments_var) {
- c.set_variable(var, assignments.at(var)->evaluate(&c));
- }
+#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(&c);
+ AbstractNode *n = children[i]->evaluate_instance(&c);
if (n != NULL)
node->children.push_back(n);
}
@@ -178,10 +182,11 @@ std::string Module::dump(const std::string &indent, const std::string &name) con
std::string tab;
if (!name.empty()) {
dump << indent << "module " << name << "(";
- for (size_t i=0; i < argnames.size(); i++) {
+ for (size_t i=0; i < this->definition_arguments.size(); i++) {
+ const Assignment &arg = this->definition_arguments[i];
if (i > 0) dump << ", ";
- dump << argnames[i];
- if (argexpr[i]) dump << " = " << *argexpr[i];
+ dump << arg.first;
+ if (arg.second) dump << " = " << *arg.second;
}
dump << ") {\n";
tab = "\t";
diff --git a/src/module.h b/src/module.h
index cc82f81..ace3c1b 100644
--- a/src/module.h
+++ b/src/module.h
@@ -6,35 +6,37 @@
#include <list>
#include <boost/unordered_map.hpp>
#include "value.h"
+#include "typedefs.h"
class ModuleInstantiation
{
public:
- ModuleInstantiation(const std::string &name = "")
- : ctx(NULL),
- tag_root(false), tag_highlight(false), tag_background(false), modname(name) { }
+ ModuleInstantiation(const std::string &name = "")
+ : tag_root(false), tag_highlight(false), tag_background(false), modname(name) { }
virtual ~ModuleInstantiation();
std::string dump(const std::string &indent) const;
- class AbstractNode *evaluate(const class Context *ctx) const;
- std::vector<AbstractNode*> evaluateChildren(const Context *ctx = NULL) const;
+ class AbstractNode *evaluate_instance(const class Context *ctx) const;
+ std::vector<AbstractNode*> evaluateChildren(const Context *evalctx) const;
+
+ void setPath(const std::string &path) { this->modpath = path; }
+ const std::string &path() const { return this->modpath; }
+ std::string getAbsolutePath(const std::string &filename) const;
const std::string &name() const { return this->modname; }
bool isBackground() const { return this->tag_background; }
bool isHighlight() const { return this->tag_highlight; }
bool isRoot() const { return this->tag_root; }
- std::vector<std::string> argnames;
- std::vector<Value> argvalues;
- std::vector<class Expression*> argexpr;
+ AssignmentList arguments;
std::vector<ModuleInstantiation*> children;
- const Context *ctx;
bool tag_root;
bool tag_highlight;
bool tag_background;
protected:
std::string modname;
+ std::string modpath;
friend class Module;
};
@@ -43,7 +45,7 @@ class IfElseModuleInstantiation : public ModuleInstantiation {
public:
IfElseModuleInstantiation() : ModuleInstantiation("if") { }
virtual ~IfElseModuleInstantiation();
- std::vector<AbstractNode*> evaluateElseChildren(const Context *ctx = NULL) const;
+ std::vector<AbstractNode*> evaluateElseChildren(const Context *evalctx) const;
std::vector<ModuleInstantiation*> else_children;
};
@@ -52,7 +54,7 @@ class AbstractModule
{
public:
virtual ~AbstractModule();
- virtual class AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
+ virtual class AbstractNode *evaluate(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;
};
@@ -61,7 +63,11 @@ class Module : public AbstractModule
public:
Module() : is_handling_dependencies(false) { }
virtual ~Module();
- virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
+
+ 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 std::string dump(const std::string &indent, const std::string &name) const;
void addChild(ModuleInstantiation *ch) { this->children.push_back(ch); }
@@ -75,8 +81,8 @@ public:
bool handleDependencies();
std::list<std::string> assignments_var;
- typedef boost::unordered_map<std::string, Expression*> AssignmentContainer;
- AssignmentContainer assignments;
+ typedef boost::unordered_map<std::string, Expression*> AssignmentMap;
+ AssignmentMap assignments;
typedef boost::unordered_map<std::string, class AbstractFunction*> FunctionContainer;
FunctionContainer functions;
@@ -85,12 +91,12 @@ public:
std::vector<ModuleInstantiation*> children;
- std::vector<std::string> argnames;
- std::vector<Expression*> argexpr;
+ std::vector<Assignment> definition_arguments;
protected:
private:
+ std::string path;
};
#endif
diff --git a/src/openscad.cc b/src/openscad.cc
index 878c207..3b960f3 100644
--- a/src/openscad.cc
+++ b/src/openscad.cc
@@ -28,7 +28,7 @@
#include "MainWindow.h"
#include "node.h"
#include "module.h"
-#include "context.h"
+#include "modcontext.h"
#include "value.h"
#include "export.h"
#include "builtin.h"
@@ -327,11 +327,8 @@ int main(int argc, char **argv)
if (!filename) help(argv[0]);
- Context root_ctx;
- register_builtin(root_ctx);
-
Module *root_module;
- ModuleInstantiation root_inst;
+ ModuleInstantiation root_inst("group");
AbstractNode *root_node;
AbstractNode *absolute_root_node;
CGAL_Nef_polyhedron root_N;
@@ -351,12 +348,18 @@ 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);
+ absolute_root_node = root_module->evaluate(&root_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/parser.y b/src/parser.y
index 536f4ef..9b4f888 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -34,6 +34,7 @@
#include <unistd.h>
#endif
+#include "typedefs.h"
#include "module.h"
#include "expression.h"
#include "value.h"
@@ -58,16 +59,11 @@ int lexerlex(void);
std::vector<Module*> module_stack;
Module *currmodule;
-class ArgContainer {
-public:
- std::string argname;
- Expression *argexpr;
-};
-class ArgsContainer {
-public:
- std::vector<std::string> argnames;
- std::vector<Expression*> argexpr;
-};
+extern void lexerdestroy();
+extern FILE *lexerin;
+extern const char *parser_input_buffer;
+const char *parser_input_buffer;
+std::string parser_source_path;
%}
@@ -77,9 +73,10 @@ public:
class Value *value;
class Expression *expr;
class ModuleInstantiation *inst;
+ std::vector<ModuleInstantiation*> *instvec;
class IfElseModuleInstantiation *ifelse;
- class ArgContainer *arg;
- class ArgsContainer *args;
+ Assignment *arg;
+ AssignmentList *args;
}
%token TOK_MODULE
@@ -117,8 +114,8 @@ public:
%type <inst> module_instantiation
%type <ifelse> if_statement
%type <ifelse> ifelse_statement
-%type <inst> children_instantiation
-%type <inst> module_instantiation_list
+%type <instvec> children_instantiation
+%type <instvec> module_instantiation_list
%type <inst> single_module_instantiation
%type <args> arguments_call
@@ -161,8 +158,7 @@ statement:
module_stack.push_back(currmodule);
currmodule = new Module();
p->modules[$2] = currmodule;
- currmodule->argnames = $4->argnames;
- currmodule->argexpr = $4->argexpr;
+ currmodule->definition_arguments = *$4;
free($2);
delete $4;
} statement {
@@ -171,8 +167,7 @@ statement:
} |
TOK_FUNCTION TOK_ID '(' arguments_decl optional_commas ')' '=' expr {
Function *func = new Function();
- func->argnames = $4->argnames;
- func->argexpr = $4->argexpr;
+ func->definition_arguments = *$4;
func->expr = $8;
currmodule->functions[$2] = func;
free($2);
@@ -182,9 +177,9 @@ statement:
/* Will return a dummy parent node with zero or more children */
children_instantiation:
module_instantiation {
- $$ = new ModuleInstantiation();
+ $$ = new std::vector<ModuleInstantiation*>;
if ($1) {
- $$->children.push_back($1);
+ $$->push_back($1);
}
} |
'{' module_instantiation_list '}' {
@@ -194,16 +189,15 @@ children_instantiation:
if_statement:
TOK_IF '(' expr ')' children_instantiation {
$$ = new IfElseModuleInstantiation();
- $$->argnames.push_back("");
- $$->argexpr.push_back($3);
+ $$->arguments.push_back(Assignment("", $3));
+ $$->setPath(parser_source_path);
if ($$) {
- $$->children = $5->children;
+ $$->children = *$5;
} else {
- for (size_t i = 0; i < $5->children.size(); i++)
- delete $5->children[i];
+ for (size_t i = 0; i < $5->size(); i++)
+ delete (*$5)[i];
}
- $5->children.clear();
delete $5;
} ;
@@ -214,12 +208,11 @@ ifelse_statement:
if_statement TOK_ELSE children_instantiation {
$$ = $1;
if ($$) {
- $$->else_children = $3->children;
+ $$->else_children = *$3;
} else {
- for (size_t i = 0; i < $3->children.size(); i++)
- delete $3->children[i];
+ for (size_t i = 0; i < $3->size(); i++)
+ delete (*$3)[i];
}
- $3->children.clear();
delete $3;
} ;
@@ -246,12 +239,11 @@ module_instantiation:
single_module_instantiation children_instantiation {
$$ = $1;
if ($$) {
- $$->children = $2->children;
+ $$->children = *$2;
} else {
- for (size_t i = 0; i < $2->children.size(); i++)
- delete $2->children[i];
+ for (size_t i = 0; i < $2->size(); i++)
+ delete (*$2)[i];
}
- $2->children.clear();
delete $2;
} |
ifelse_statement {
@@ -260,12 +252,12 @@ module_instantiation:
module_instantiation_list:
/* empty */ {
- $$ = new ModuleInstantiation();
+ $$ = new std::vector<ModuleInstantiation*>;
} |
module_instantiation_list module_instantiation {
$$ = $1;
if ($$) {
- if ($2) $$->children.push_back($2);
+ if ($2) $$->push_back($2);
} else {
delete $2;
}
@@ -274,8 +266,8 @@ module_instantiation_list:
single_module_instantiation:
TOK_ID '(' arguments_call ')' {
$$ = new ModuleInstantiation($1);
- $$->argnames = $3->argnames;
- $$->argexpr = $3->argexpr;
+ $$->arguments = *$3;
+ $$->setPath(parser_source_path);
free($1);
delete $3;
}
@@ -442,8 +434,7 @@ expr:
$$ = new Expression();
$$->type = "F";
$$->call_funcname = $1;
- $$->call_argnames = $3->argnames;
- $$->children = $3->argexpr;
+ $$->call_arguments = *$3;
free($1);
delete $3;
} ;
@@ -464,61 +455,50 @@ vector_expr:
arguments_decl:
/* empty */ {
- $$ = new ArgsContainer();
+ $$ = new AssignmentList();
} |
argument_decl {
- $$ = new ArgsContainer();
- $$->argnames.push_back($1->argname);
- $$->argexpr.push_back($1->argexpr);
+ $$ = new AssignmentList();
+ $$->push_back(*$1);
delete $1;
} |
arguments_decl ',' optional_commas argument_decl {
$$ = $1;
- $$->argnames.push_back($4->argname);
- $$->argexpr.push_back($4->argexpr);
+ $$->push_back(*$4);
delete $4;
} ;
argument_decl:
TOK_ID {
- $$ = new ArgContainer();
- $$->argname = $1;
- $$->argexpr = NULL;
+ $$ = new Assignment($1, NULL);
free($1);
} |
TOK_ID '=' expr {
- $$ = new ArgContainer();
- $$->argname = $1;
- $$->argexpr = $3;
+ $$ = new Assignment($1, $3);
free($1);
} ;
arguments_call:
/* empty */ {
- $$ = new ArgsContainer();
+ $$ = new AssignmentList();
} |
argument_call {
- $$ = new ArgsContainer();
- $$->argnames.push_back($1->argname);
- $$->argexpr.push_back($1->argexpr);
+ $$ = new AssignmentList();
+ $$->push_back(*$1);
delete $1;
} |
arguments_call ',' optional_commas argument_call {
$$ = $1;
- $$->argnames.push_back($4->argname);
- $$->argexpr.push_back($4->argexpr);
+ $$->push_back(*$4);
delete $4;
} ;
argument_call:
expr {
- $$ = new ArgContainer();
- $$->argexpr = $1;
+ $$ = new Assignment("", $1);
} |
TOK_ID '=' expr {
- $$ = new ArgContainer();
- $$->argname = $1;
- $$->argexpr = $3;
+ $$ = new Assignment($1, $3);
free($1);
} ;
@@ -536,21 +516,16 @@ void yyerror (char const *s)
currmodule = NULL;
}
-extern void lexerdestroy();
-extern FILE *lexerin;
-extern const char *parser_input_buffer;
-const char *parser_input_buffer;
-std::string parser_source_path;
-
Module *parse(const char *text, const char *path, int debug)
{
lexerin = NULL;
parser_error_pos = -1;
parser_input_buffer = text;
- parser_source_path = std::string(path);
+ parser_source_path = boosty::absolute(std::string(path)).string();
module_stack.clear();
Module *rootmodule = currmodule = new Module();
+ rootmodule->setModulePath(path);
// PRINTB_NOCACHE("New module: %s %p", "root" % rootmodule);
parserdebug = debug;
diff --git a/src/primitives.cc b/src/primitives.cc
index e02df35..184b40c 100644
--- a/src/primitives.cc
+++ b/src/primitives.cc
@@ -27,12 +27,13 @@
#include "module.h"
#include "node.h"
#include "polyset.h"
-#include "context.h"
+#include "evalcontext.h"
#include "dxfdata.h"
#include "dxftess.h"
#include "builtin.h"
#include "printutils.h"
#include "visitor.h"
+#include "context.h"
#include <sstream>
#include <assert.h>
#include <boost/assign/std/vector.hpp>
@@ -55,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;
+ virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
class PrimitiveNode : public AbstractPolyNode
@@ -104,44 +105,43 @@ public:
virtual PolySet *evaluate_polyset(class PolySetEvaluator *) const;
};
-AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
+AbstractNode *PrimitiveModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
PrimitiveNode *node = new PrimitiveNode(inst, this->type);
node->center = false;
node->x = node->y = node->z = node->h = node->r1 = node->r2 = 1;
- std::vector<std::string> argnames;
- std::vector<Expression*> argexpr;
+ AssignmentList args;
switch (this->type) {
case CUBE:
- argnames += "size", "center";
+ args += Assignment("size", NULL), Assignment("center", NULL);
break;
case SPHERE:
- argnames += "r";
+ args += Assignment("r", NULL);
break;
case CYLINDER:
- argnames += "h", "r1", "r2", "center";
+ args += Assignment("h", NULL), Assignment("r1", NULL), Assignment("r2", NULL), Assignment("center", NULL);
break;
case POLYHEDRON:
- argnames += "points", "triangles", "convexity";
+ args += Assignment("points", NULL), Assignment("triangles", NULL), Assignment("convexity", NULL);
break;
case SQUARE:
- argnames += "size", "center";
+ args += Assignment("size", NULL), Assignment("center", NULL);
break;
case CIRCLE:
- argnames += "r";
+ args += Assignment("r", NULL);
break;
case POLYGON:
- argnames += "points", "paths", "convexity";
+ args += Assignment("points", NULL), Assignment("paths", NULL), Assignment("convexity", NULL);
break;
default:
assert(false && "PrimitiveModule::evaluate(): Unknown node type");
}
Context c(ctx);
- c.args(argnames, argexpr, inst->argnames, inst->argvalues);
+ c.setVariables(args, evalctx);
node->fn = c.lookup_variable("$fn").toDouble();
node->fs = c.lookup_variable("$fs").toDouble();
diff --git a/src/printutils.cc b/src/printutils.cc
index 698fffb..37092fa 100644
--- a/src/printutils.cc
+++ b/src/printutils.cc
@@ -69,5 +69,3 @@ std::string two_digit_exp_format( double x )
s << x;
return two_digit_exp_format( s.str() );
}
-
-
diff --git a/src/printutils.h b/src/printutils.h
index 439c884..18aadde 100644
--- a/src/printutils.h
+++ b/src/printutils.h
@@ -22,6 +22,9 @@ void PRINT(const std::string &msg);
void PRINT_NOCACHE(const std::string &msg);
#define PRINTB_NOCACHE(_fmt, _arg) do { PRINT_NOCACHE(str(boost::format(_fmt) % _arg)); } while (0)
+
+void PRINT_CONTEXT(const class Context *ctx, const class Module *mod, const class ModuleInstantiation *inst);
+
std::string two_digit_exp_format( std::string doublestr );
std::string two_digit_exp_format( double x );
diff --git a/src/projection.cc b/src/projection.cc
index 1fcf639..eeed411 100644
--- a/src/projection.cc
+++ b/src/projection.cc
@@ -26,7 +26,7 @@
#include "projectionnode.h"
#include "module.h"
-#include "context.h"
+#include "evalcontext.h"
#include "printutils.h"
#include "builtin.h"
#include "visitor.h"
@@ -41,19 +41,18 @@ class ProjectionModule : public AbstractModule
{
public:
ProjectionModule() { }
- virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
+ virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
-AbstractNode *ProjectionModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
+AbstractNode *ProjectionModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
ProjectionNode *node = new ProjectionNode(inst);
- std::vector<std::string> argnames;
- argnames += "cut";
- std::vector<Expression*> argexpr;
+ AssignmentList args;
+ args += Assignment("cut", NULL);
Context c(ctx);
- c.args(argnames, argexpr, inst->argnames, inst->argvalues);
+ c.setVariables(args, evalctx);
Value convexity = c.lookup_variable("convexity", true);
Value cut = c.lookup_variable("cut", true);
@@ -63,7 +62,7 @@ AbstractNode *ProjectionModule::evaluate(const Context *ctx, const ModuleInstant
if (cut.type() == Value::BOOL)
node->cut_mode = cut.toBool();
- std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx);
node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
return node;
diff --git a/src/render.cc b/src/render.cc
index 81c3f7b..bb08c0c 100644
--- a/src/render.cc
+++ b/src/render.cc
@@ -26,7 +26,7 @@
#include "rendernode.h"
#include "module.h"
-#include "context.h"
+#include "evalcontext.h"
#include "builtin.h"
#include "PolySetEvaluator.h"
@@ -38,25 +38,24 @@ class RenderModule : public AbstractModule
{
public:
RenderModule() { }
- virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
+ virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
-AbstractNode *RenderModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
+AbstractNode *RenderModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
RenderNode *node = new RenderNode(inst);
- std::vector<std::string> argnames;
- argnames += "convexity";
- std::vector<Expression*> argexpr;
+ AssignmentList args;
+ args += Assignment("convexity", NULL);
Context c(ctx);
- c.args(argnames, argexpr, inst->argnames, inst->argvalues);
+ c.setVariables(args, evalctx);
Value v = c.lookup_variable("convexity");
if (v.type() == Value::NUMBER)
node->convexity = (int)v.toDouble();
- std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx);
node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
return node;
diff --git a/src/rotateextrude.cc b/src/rotateextrude.cc
index dc8ea34..b6edb8b 100644
--- a/src/rotateextrude.cc
+++ b/src/rotateextrude.cc
@@ -26,7 +26,7 @@
#include "rotateextrudenode.h"
#include "module.h"
-#include "context.h"
+#include "evalcontext.h"
#include "printutils.h"
#include "builtin.h"
#include "polyset.h"
@@ -45,19 +45,18 @@ class RotateExtrudeModule : public AbstractModule
{
public:
RotateExtrudeModule() { }
- virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
+ virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
-AbstractNode *RotateExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
+AbstractNode *RotateExtrudeModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
RotateExtrudeNode *node = new RotateExtrudeNode(inst);
- std::vector<std::string> argnames;
- argnames += "file", "layer", "origin", "scale";
- std::vector<Expression*> argexpr;
+ AssignmentList args;
+ args += Assignment("file", NULL), Assignment("layer", NULL), Assignment("origin", NULL), Assignment("scale", NULL);
Context c(ctx);
- c.args(argnames, argexpr, inst->argnames, inst->argvalues);
+ c.setVariables(args, evalctx);
node->fn = c.lookup_variable("$fn").toDouble();
node->fs = c.lookup_variable("$fs").toDouble();
@@ -71,7 +70,7 @@ AbstractNode *RotateExtrudeModule::evaluate(const Context *ctx, const ModuleInst
if (!file.isUndefined()) {
PRINT("DEPRECATED: Support for reading files in rotate_extrude will be removed in future releases. Use a child import() instead.");
- node->filename = c.getAbsolutePath(file.toString());
+ node->filename = inst->getAbsolutePath(file.toString());
}
node->layername = layer.isUndefined() ? "" : layer.toString();
@@ -86,7 +85,7 @@ AbstractNode *RotateExtrudeModule::evaluate(const Context *ctx, const ModuleInst
node->scale = 1;
if (node->filename.empty()) {
- std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx);
node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
}
diff --git a/src/surface.cc b/src/surface.cc
index 4339ead..d8fa422 100644
--- a/src/surface.cc
+++ b/src/surface.cc
@@ -27,7 +27,7 @@
#include "module.h"
#include "node.h"
#include "polyset.h"
-#include "context.h"
+#include "evalcontext.h"
#include "builtin.h"
#include "printutils.h"
#include "handle_dep.h" // handle_dep()
@@ -50,7 +50,7 @@ class SurfaceModule : public AbstractModule
{
public:
SurfaceModule() { }
- virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst) const;
+ virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
class SurfaceNode : public AbstractPolyNode
@@ -69,21 +69,20 @@ public:
virtual PolySet *evaluate_polyset(class PolySetEvaluator *) const;
};
-AbstractNode *SurfaceModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
+AbstractNode *SurfaceModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
SurfaceNode *node = new SurfaceNode(inst);
node->center = false;
node->convexity = 1;
- std::vector<std::string> argnames;
- argnames += "file", "center", "convexity";
- std::vector<Expression*> argexpr;
+ AssignmentList args;
+ args += Assignment("file", NULL), Assignment("center", NULL), Assignment("convexity", NULL);
Context c(ctx);
- c.args(argnames, argexpr, inst->argnames, inst->argvalues);
+ c.setVariables(args, evalctx);
Value fileval = c.lookup_variable("file");
- node->filename = c.getAbsolutePath(fileval.isUndefined() ? "" : fileval.toString());
+ node->filename = inst->getAbsolutePath(fileval.isUndefined() ? "" : fileval.toString());
Value center = c.lookup_variable("center", true);
if (center.type() == Value::BOOL) {
diff --git a/src/transform.cc b/src/transform.cc
index b01827f..cc026fb 100644
--- a/src/transform.cc
+++ b/src/transform.cc
@@ -26,7 +26,7 @@
#include "transformnode.h"
#include "module.h"
-#include "context.h"
+#include "evalcontext.h"
#include "polyset.h"
#include "builtin.h"
#include "value.h"
@@ -50,40 +50,39 @@ 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;
+ virtual AbstractNode *evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
-AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstantiation *inst) const
+AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
TransformNode *node = new TransformNode(inst);
node->matrix = Transform3d::Identity();
- std::vector<std::string> argnames;
- std::vector<Expression*> argexpr;
+ AssignmentList args;
switch (this->type) {
case SCALE:
- argnames += "v";
+ args += Assignment("v", NULL);
break;
case ROTATE:
- argnames += "a", "v";
+ args += Assignment("a", NULL), Assignment("v", NULL);
break;
case MIRROR:
- argnames += "v";
+ args += Assignment("v", NULL);
break;
case TRANSLATE:
- argnames += "v";
+ args += Assignment("v", NULL);
break;
case MULTMATRIX:
- argnames += "m";
+ args += Assignment("m", NULL);
break;
default:
assert(false);
}
Context c(ctx);
- c.args(argnames, argexpr, inst->argnames, inst->argvalues);
+ c.setVariables(args, evalctx);
if (this->type == SCALE)
{
@@ -176,7 +175,7 @@ AbstractNode *TransformModule::evaluate(const Context *ctx, const ModuleInstanti
}
}
- std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren();
+ std::vector<AbstractNode *> evaluatednodes = inst->evaluateChildren(evalctx);
node->children.insert(node->children.end(), evaluatednodes.begin(), evaluatednodes.end());
return node;
diff --git a/src/typedefs.h b/src/typedefs.h
new file mode 100644
index 0000000..a6e9077
--- /dev/null
+++ b/src/typedefs.h
@@ -0,0 +1,10 @@
+#ifndef TYPEDEFS_H_
+#define TYPEDEFS_H_
+
+#include <string>
+#include <vector>
+
+typedef std::pair<std::string, class Expression*> Assignment;
+typedef std::vector<Assignment> AssignmentList;
+
+#endif
diff --git a/testdata/scad/features/child-child-test.scad b/testdata/scad/features/child-child-test.scad
new file mode 100644
index 0000000..e5e6d93
--- /dev/null
+++ b/testdata/scad/features/child-child-test.scad
@@ -0,0 +1,12 @@
+module up() {
+ translate([0,0,1]) child(0);
+}
+
+module red() {
+ color("Red") child(0);
+}
+
+up() cylinder(r=5);
+translate([5,0,0]) up() up() cylinder(r=5);
+translate([10,0,0]) up() up() up() red() cylinder(r=5);
+translate([15,0,0]) red() up() up() up() up() cylinder(r=5);
diff --git a/testdata/scad/features/module-recursion.scad b/testdata/scad/features/module-recursion.scad
new file mode 100644
index 0000000..f67a1d0
--- /dev/null
+++ b/testdata/scad/features/module-recursion.scad
@@ -0,0 +1,15 @@
+module tree(currentScale, levels)
+{
+ h = currentScale;
+ w = currentScale/5;
+ childScale = currentScale * 0.7;
+
+ if (levels > 0) {
+ cylinder(r=w, h=h);
+ translate([0,0,h]) for (i = [1:2]) {
+ rotate([40, 0, i * 180]) tree(childScale, levels-1);
+ }
+ }
+}
+
+tree(1, 4);
diff --git a/testdata/scad/features/modulevariables.scad b/testdata/scad/features/modulevariables.scad
new file mode 100644
index 0000000..fc7a183
--- /dev/null
+++ b/testdata/scad/features/modulevariables.scad
@@ -0,0 +1,7 @@
+module mymodule(modparam) {
+ inner_variable = 23;
+ inner_variable2 = modparam * 2;
+ cylinder(r1=inner_variable, r2=inner_variable2, h=10);
+}
+
+mymodule(5);
diff --git a/testdata/scad/misc/localfiles-test.scad b/testdata/scad/misc/localfiles-test.scad
new file mode 100644
index 0000000..31efe96
--- /dev/null
+++ b/testdata/scad/misc/localfiles-test.scad
@@ -0,0 +1,3 @@
+use <localfiles_dir/localfiles_module.scad>
+
+localfiles_module();
diff --git a/testdata/scad/misc/localfiles_dir/localfile.dat b/testdata/scad/misc/localfiles_dir/localfile.dat
new file mode 100644
index 0000000..32eba08
--- /dev/null
+++ b/testdata/scad/misc/localfiles_dir/localfile.dat
@@ -0,0 +1,2 @@
+0 1
+2 3
diff --git a/testdata/scad/misc/localfiles_dir/localfile.dxf b/testdata/scad/misc/localfiles_dir/localfile.dxf
new file mode 100644
index 0000000..933e263
--- /dev/null
+++ b/testdata/scad/misc/localfiles_dir/localfile.dxf
@@ -0,0 +1,1968 @@
+999
+dxflib 2.2.0.0
+ 0
+SECTION
+ 2
+HEADER
+ 9
+$ACADVER
+ 1
+AC1015
+ 9
+$HANDSEED
+ 5
+FFFF
+ 9
+$DIMASZ
+ 40
+2.5
+ 9
+$PLIMMIN
+ 10
+0.0
+ 20
+0.0
+ 9
+$DIMEXE
+ 40
+1.25
+ 9
+$DIMGAP
+ 40
+0.625
+ 9
+$PLIMMAX
+ 10
+210.0
+ 20
+297.0
+ 9
+$INSUNITS
+ 70
+4
+ 9
+$DIMSTYLE
+ 2
+Standard
+ 9
+$CLAYER
+ 8
+0
+ 9
+$DIMEXO
+ 40
+0.625
+ 9
+$DIMTXT
+ 40
+2.5
+ 9
+$CLAYER
+ 8
+0
+ 0
+ENDSEC
+ 0
+SECTION
+ 2
+TABLES
+ 0
+TABLE
+ 2
+VPORT
+ 5
+8
+100
+AcDbSymbolTable
+ 70
+1
+ 0
+VPORT
+ 5
+30
+100
+AcDbSymbolTableRecord
+100
+AcDbViewportTableRecord
+ 2
+*Active
+ 70
+0
+ 10
+0.0
+ 20
+0.0
+ 11
+1.0
+ 21
+1.0
+ 12
+286.3055555555554861
+ 22
+148.5
+ 13
+0.0
+ 23
+0.0
+ 14
+10.0
+ 24
+10.0
+ 15
+10.0
+ 25
+10.0
+ 16
+0.0
+ 26
+0.0
+ 36
+1.0
+ 17
+0.0
+ 27
+0.0
+ 37
+0.0
+ 40
+297.0
+ 41
+1.92798353909465
+ 42
+50.0
+ 43
+0.0
+ 44
+0.0
+ 50
+0.0
+ 51
+0.0
+ 71
+0
+ 72
+100
+ 73
+1
+ 74
+3
+ 75
+1
+ 76
+1
+ 77
+0
+ 78
+0
+281
+0
+ 65
+1
+110
+0.0
+120
+0.0
+130
+0.0
+111
+1.0
+121
+0.0
+131
+0.0
+112
+0.0
+122
+1.0
+132
+0.0
+ 79
+0
+146
+0.0
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+LTYPE
+ 5
+5
+100
+AcDbSymbolTable
+ 70
+21
+ 0
+LTYPE
+ 5
+14
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+ByBlock
+ 70
+0
+ 3
+
+ 72
+65
+ 73
+0
+ 40
+0.0
+ 0
+LTYPE
+ 5
+15
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+ByLayer
+ 70
+0
+ 3
+
+ 72
+65
+ 73
+0
+ 40
+0.0
+ 0
+LTYPE
+ 5
+16
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+CONTINUOUS
+ 70
+0
+ 3
+Solid line
+ 72
+65
+ 73
+0
+ 40
+0.0
+ 0
+LTYPE
+ 5
+31
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DOT
+ 70
+0
+ 3
+Dot . . . . . . . . . . . . . . . . . . . . . .
+ 72
+65
+ 73
+2
+ 40
+6.3499999999999996
+ 49
+0.0
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 0
+LTYPE
+ 5
+32
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DOT2
+ 70
+0
+ 3
+Dot (.5x) .....................................
+ 72
+65
+ 73
+2
+ 40
+3.1749999999999998
+ 49
+0.0
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 0
+LTYPE
+ 5
+33
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DOTX2
+ 70
+0
+ 3
+Dot (2x) . . . . . . . . . . . . .
+ 72
+65
+ 73
+2
+ 40
+12.6999999999999993
+ 49
+0.0
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 0
+LTYPE
+ 5
+34
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DASHED
+ 70
+0
+ 3
+Dashed __ __ __ __ __ __ __ __ __ __ __ __ __ _
+ 72
+65
+ 73
+2
+ 40
+19.0500000000000007
+ 49
+12.6999999999999993
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 0
+LTYPE
+ 5
+35
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DASHED2
+ 70
+0
+ 3
+Dashed (.5x) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ 72
+65
+ 73
+2
+ 40
+9.5250000000000004
+ 49
+6.3499999999999996
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 0
+LTYPE
+ 5
+36
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DASHEDX2
+ 70
+0
+ 3
+Dashed (2x) ____ ____ ____ ____ ____ ___
+ 72
+65
+ 73
+2
+ 40
+38.1000000000000014
+ 49
+25.3999999999999986
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 0
+LTYPE
+ 5
+37
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DASHDOT
+ 70
+0
+ 3
+Dash dot __ . __ . __ . __ . __ . __ . __ . __
+ 72
+65
+ 73
+4
+ 40
+25.3999999999999986
+ 49
+12.6999999999999993
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 0
+LTYPE
+ 5
+38
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DASHDOT2
+ 70
+0
+ 3
+Dash dot (.5x) _._._._._._._._._._._._._._._.
+ 72
+65
+ 73
+4
+ 40
+12.6999999999999993
+ 49
+6.3499999999999996
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 0
+LTYPE
+ 5
+39
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DASHDOTX2
+ 70
+0
+ 3
+Dash dot (2x) ____ . ____ . ____ . ___
+ 72
+65
+ 73
+4
+ 40
+50.7999999999999972
+ 49
+25.3999999999999986
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 0
+LTYPE
+ 5
+3A
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DIVIDE
+ 70
+0
+ 3
+Divide ____ . . ____ . . ____ . . ____ . . ____
+ 72
+65
+ 73
+6
+ 40
+31.75
+ 49
+12.6999999999999993
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 0
+LTYPE
+ 5
+3B
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DIVIDE2
+ 70
+0
+ 3
+Divide (.5x) __..__..__..__..__..__..__..__.._
+ 72
+65
+ 73
+6
+ 40
+15.875
+ 49
+6.3499999999999996
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 0
+LTYPE
+ 5
+3C
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+DIVIDEX2
+ 70
+0
+ 3
+Divide (2x) ________ . . ________ . . _
+ 72
+65
+ 73
+6
+ 40
+63.5
+ 49
+25.3999999999999986
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 0
+LTYPE
+ 5
+3D
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+CENTER
+ 70
+0
+ 3
+Center ____ _ ____ _ ____ _ ____ _ ____ _ ____
+ 72
+65
+ 73
+4
+ 40
+50.7999999999999972
+ 49
+31.75
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 49
+6.3499999999999996
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 0
+LTYPE
+ 5
+3E
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+CENTER2
+ 70
+0
+ 3
+Center (.5x) ___ _ ___ _ ___ _ ___ _ ___ _ ___
+ 72
+65
+ 73
+4
+ 40
+28.5749999999999993
+ 49
+19.0500000000000007
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 49
+3.1749999999999998
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 0
+LTYPE
+ 5
+3F
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+CENTERX2
+ 70
+0
+ 3
+Center (2x) ________ __ ________ __ _____
+ 72
+65
+ 73
+4
+ 40
+101.5999999999999943
+ 49
+63.5
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 49
+12.6999999999999993
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 0
+LTYPE
+ 5
+40
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+BORDER
+ 70
+0
+ 3
+Border __ __ . __ __ . __ __ . __ __ . __ __ .
+ 72
+65
+ 73
+6
+ 40
+44.4500000000000028
+ 49
+12.6999999999999993
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 49
+12.6999999999999993
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-6.3499999999999996
+ 74
+0
+ 0
+LTYPE
+ 5
+41
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+BORDER2
+ 70
+0
+ 3
+Border (.5x) __.__.__.__.__.__.__.__.__.__.__.
+ 72
+65
+ 73
+6
+ 40
+22.2250000000000014
+ 49
+6.3499999999999996
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 49
+6.3499999999999996
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-3.1749999999999998
+ 74
+0
+ 0
+LTYPE
+ 5
+42
+100
+AcDbSymbolTableRecord
+100
+AcDbLinetypeTableRecord
+ 2
+BORDERX2
+ 70
+0
+ 3
+Border (2x) ____ ____ . ____ ____ . ___
+ 72
+65
+ 73
+6
+ 40
+88.9000000000000057
+ 49
+25.3999999999999986
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 49
+25.3999999999999986
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 49
+0.0
+ 74
+0
+ 49
+-12.6999999999999993
+ 74
+0
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+LAYER
+ 5
+2
+100
+AcDbSymbolTable
+ 70
+1
+ 0
+LAYER
+ 5
+10
+100
+AcDbSymbolTableRecord
+100
+AcDbLayerTableRecord
+ 2
+0
+ 70
+0
+ 62
+7
+420
+0
+ 6
+CONTINUOUS
+370
+25
+390
+F
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+STYLE
+ 5
+3
+100
+AcDbSymbolTable
+ 70
+1
+ 0
+STYLE
+ 5
+11
+100
+AcDbSymbolTableRecord
+100
+AcDbTextStyleTableRecord
+ 2
+Standard
+ 70
+0
+ 40
+0.0
+ 41
+0.75
+ 50
+0.0
+ 71
+0
+ 42
+2.5
+ 3
+txt
+ 4
+
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+VIEW
+ 5
+6
+100
+AcDbSymbolTable
+ 70
+0
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+UCS
+ 5
+7
+100
+AcDbSymbolTable
+ 70
+0
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+APPID
+ 5
+9
+100
+AcDbSymbolTable
+ 70
+1
+ 0
+APPID
+ 5
+12
+100
+AcDbSymbolTableRecord
+100
+AcDbRegAppTableRecord
+ 2
+ACAD
+ 70
+0
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+DIMSTYLE
+ 5
+A
+100
+AcDbSymbolTable
+ 70
+1
+100
+AcDbDimStyleTable
+ 71
+0
+ 0
+DIMSTYLE
+105
+27
+100
+AcDbSymbolTableRecord
+100
+AcDbDimStyleTableRecord
+ 2
+Standard
+ 41
+2.5
+ 42
+0.625
+ 43
+3.75
+ 44
+1.25
+ 70
+0
+ 73
+0
+ 74
+0
+ 77
+1
+ 78
+8
+140
+2.5
+141
+2.5
+143
+0.03937007874016
+147
+0.625
+171
+3
+172
+1
+271
+2
+272
+2
+274
+3
+278
+44
+283
+0
+284
+8
+340
+11
+ 0
+ENDTAB
+ 0
+TABLE
+ 2
+BLOCK_RECORD
+ 5
+1
+100
+AcDbSymbolTable
+ 70
+1
+ 0
+BLOCK_RECORD
+ 5
+1F
+100
+AcDbSymbolTableRecord
+100
+AcDbBlockTableRecord
+ 2
+*Model_Space
+340
+22
+ 0
+BLOCK_RECORD
+ 5
+1B
+100
+AcDbSymbolTableRecord
+100
+AcDbBlockTableRecord
+ 2
+*Paper_Space
+340
+1E
+ 0
+BLOCK_RECORD
+ 5
+23
+100
+AcDbSymbolTableRecord
+100
+AcDbBlockTableRecord
+ 2
+*Paper_Space0
+340
+26
+ 0
+ENDTAB
+ 0
+ENDSEC
+ 0
+SECTION
+ 2
+BLOCKS
+ 0
+BLOCK
+ 5
+20
+100
+AcDbEntity
+ 8
+0
+100
+AcDbBlockBegin
+ 2
+*Model_Space
+ 70
+0
+ 10
+0.0
+ 20
+0.0
+ 30
+0.0
+ 3
+*Model_Space
+ 1
+
+ 0
+ENDBLK
+ 5
+21
+100
+AcDbEntity
+ 8
+0
+100
+AcDbBlockEnd
+ 0
+BLOCK
+ 5
+1C
+100
+AcDbEntity
+ 67
+1
+ 8
+0
+100
+AcDbBlockBegin
+ 2
+*Paper_Space
+ 70
+0
+ 10
+0.0
+ 20
+0.0
+ 30
+0.0
+ 3
+*Paper_Space
+ 1
+
+ 0
+ENDBLK
+ 5
+1D
+100
+AcDbEntity
+ 67
+1
+ 8
+0
+100
+AcDbBlockEnd
+ 0
+BLOCK
+ 5
+24
+100
+AcDbEntity
+ 8
+0
+100
+AcDbBlockBegin
+ 2
+*Paper_Space0
+ 70
+0
+ 10
+0.0
+ 20
+0.0
+ 30
+0.0
+ 3
+*Paper_Space0
+ 1
+
+ 0
+ENDBLK
+ 5
+25
+100
+AcDbEntity
+ 8
+0
+100
+AcDbBlockEnd
+ 0
+ENDSEC
+ 0
+SECTION
+ 2
+ENTITIES
+ 0
+LINE
+ 5
+43
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+0
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+10.0
+ 20
+100.0
+ 30
+0.0
+ 11
+210.0
+ 21
+100.0
+ 31
+0.0
+ 0
+LINE
+ 5
+44
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+0
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+210.0
+ 20
+100.0
+ 30
+0.0
+ 11
+210.0
+ 21
+-100.0
+ 31
+0.0
+ 0
+LINE
+ 5
+45
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+0
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+210.0
+ 20
+-100.0
+ 30
+0.0
+ 11
+10.0
+ 21
+-100.0
+ 31
+0.0
+ 0
+LINE
+ 5
+46
+100
+AcDbEntity
+100
+AcDbLine
+ 8
+0
+ 62
+256
+370
+-1
+ 6
+ByLayer
+ 10
+10.0
+ 20
+-100.0
+ 30
+0.0
+ 11
+10.0
+ 21
+100.0
+ 31
+0.0
+ 0
+DIMENSION
+ 5
+47
+100
+AcDbEntity
+ 8
+0
+ 62
+256
+370
+-1
+ 6
+ByLayer
+100
+AcDbDimension
+ 10
+10.0000000000000018
+ 20
+150.0
+ 30
+0.0
+ 11
+110.0
+ 21
+151.875
+ 31
+0.0
+ 70
+1
+ 71
+5
+ 72
+1
+ 41
+1.0
+ 42
+0.0
+ 1
+localfile
+ 3
+Standard
+100
+AcDbAlignedDimension
+ 13
+10.0
+ 23
+130.0
+ 33
+0.0
+ 14
+210.0
+ 24
+130.0
+ 34
+0.0
+ 0
+ENDSEC
+ 0
+SECTION
+ 2
+OBJECTS
+ 0
+DICTIONARY
+ 5
+C
+100
+AcDbDictionary
+280
+0
+281
+1
+ 3
+ACAD_GROUP
+350
+D
+ 3
+ACAD_LAYOUT
+350
+1A
+ 3
+ACAD_MLINESTYLE
+350
+17
+ 3
+ACAD_PLOTSETTINGS
+350
+19
+ 3
+ACAD_PLOTSTYLENAME
+350
+E
+ 3
+AcDbVariableDictionary
+350
+48
+ 0
+DICTIONARY
+ 5
+D
+100
+AcDbDictionary
+280
+0
+281
+1
+ 0
+ACDBDICTIONARYWDFLT
+ 5
+E
+100
+AcDbDictionary
+281
+1
+ 3
+Normal
+350
+F
+100
+AcDbDictionaryWithDefault
+340
+F
+ 0
+ACDBPLACEHOLDER
+ 5
+F
+ 0
+DICTIONARY
+ 5
+17
+100
+AcDbDictionary
+280
+0
+281
+1
+ 3
+Standard
+350
+18
+ 0
+MLINESTYLE
+ 5
+18
+100
+AcDbMlineStyle
+ 2
+STANDARD
+ 70
+0
+ 3
+
+ 62
+256
+ 51
+90.0
+ 52
+90.0
+ 71
+2
+ 49
+0.5
+ 62
+256
+ 6
+BYLAYER
+ 49
+-0.5
+ 62
+256
+ 6
+BYLAYER
+ 0
+DICTIONARY
+ 5
+19
+100
+AcDbDictionary
+280
+0
+281
+1
+ 0
+DICTIONARY
+ 5
+1A
+100
+AcDbDictionary
+281
+1
+ 3
+Layout1
+350
+1E
+ 3
+Layout2
+350
+26
+ 3
+Model
+350
+22
+ 0
+LAYOUT
+ 5
+1E
+100
+AcDbPlotSettings
+ 1
+
+ 2
+C:\Program Files\AutoCAD 2002\plotters\DWF ePlot (optimized for plotting).pc3
+ 4
+
+ 6
+
+ 40
+0.0
+ 41
+0.0
+ 42
+0.0
+ 43
+0.0
+ 44
+0.0
+ 45
+0.0
+ 46
+0.0
+ 47
+0.0
+ 48
+0.0
+ 49
+0.0
+140
+0.0
+141
+0.0
+142
+1.0
+143
+1.0
+ 70
+688
+ 72
+0
+ 73
+0
+ 74
+5
+ 7
+
+ 75
+16
+147
+1.0
+148
+0.0
+149
+0.0
+100
+AcDbLayout
+ 1
+Layout1
+ 70
+1
+ 71
+1
+ 10
+0.0
+ 20
+0.0
+ 11
+420.0
+ 21
+297.0
+ 12
+0.0
+ 22
+0.0
+ 32
+0.0
+ 14
+100000000000000000000.0
+ 24
+100000000000000000000.0
+ 34
+100000000000000000000.0
+ 15
+-100000000000000000000.0
+ 25
+-100000000000000000000.0
+ 35
+-100000000000000000000.0
+146
+0.0
+ 13
+0.0
+ 23
+0.0
+ 33
+0.0
+ 16
+1.0
+ 26
+0.0
+ 36
+0.0
+ 17
+0.0
+ 27
+1.0
+ 37
+0.0
+ 76
+0
+330
+1B
+ 0
+LAYOUT
+ 5
+22
+100
+AcDbPlotSettings
+ 1
+
+ 2
+C:\Program Files\AutoCAD 2002\plotters\DWF ePlot (optimized for plotting).pc3
+ 4
+
+ 6
+
+ 40
+0.0
+ 41
+0.0
+ 42
+0.0
+ 43
+0.0
+ 44
+0.0
+ 45
+0.0
+ 46
+0.0
+ 47
+0.0
+ 48
+0.0
+ 49
+0.0
+140
+0.0
+141
+0.0
+142
+1.0
+143
+1.0
+ 70
+1712
+ 72
+0
+ 73
+0
+ 74
+0
+ 7
+
+ 75
+0
+147
+1.0
+148
+0.0
+149
+0.0
+100
+AcDbLayout
+ 1
+Model
+ 70
+1
+ 71
+0
+ 10
+0.0
+ 20
+0.0
+ 11
+12.0
+ 21
+9.0
+ 12
+0.0
+ 22
+0.0
+ 32
+0.0
+ 14
+0.0
+ 24
+0.0
+ 34
+0.0
+ 15
+0.0
+ 25
+0.0
+ 35
+0.0
+146
+0.0
+ 13
+0.0
+ 23
+0.0
+ 33
+0.0
+ 16
+1.0
+ 26
+0.0
+ 36
+0.0
+ 17
+0.0
+ 27
+1.0
+ 37
+0.0
+ 76
+0
+330
+1F
+ 0
+LAYOUT
+ 5
+26
+100
+AcDbPlotSettings
+ 1
+
+ 2
+C:\Program Files\AutoCAD 2002\plotters\DWF ePlot (optimized for plotting).pc3
+ 4
+
+ 6
+
+ 40
+0.0
+ 41
+0.0
+ 42
+0.0
+ 43
+0.0
+ 44
+0.0
+ 45
+0.0
+ 46
+0.0
+ 47
+0.0
+ 48
+0.0
+ 49
+0.0
+140
+0.0
+141
+0.0
+142
+1.0
+143
+1.0
+ 70
+688
+ 72
+0
+ 73
+0
+ 74
+5
+ 7
+
+ 75
+16
+147
+1.0
+148
+0.0
+149
+0.0
+100
+AcDbLayout
+ 1
+Layout2
+ 70
+1
+ 71
+2
+ 10
+0.0
+ 20
+0.0
+ 11
+12.0
+ 21
+9.0
+ 12
+0.0
+ 22
+0.0
+ 32
+0.0
+ 14
+0.0
+ 24
+0.0
+ 34
+0.0
+ 15
+0.0
+ 25
+0.0
+ 35
+0.0
+146
+0.0
+ 13
+0.0
+ 23
+0.0
+ 33
+0.0
+ 16
+1.0
+ 26
+0.0
+ 36
+0.0
+ 17
+0.0
+ 27
+1.0
+ 37
+0.0
+ 76
+0
+330
+23
+ 0
+DICTIONARY
+ 5
+48
+100
+AcDbDictionary
+281
+1
+ 3
+DIMASSOC
+350
+4A
+ 3
+HIDETEXT
+350
+49
+ 0
+DICTIONARYVAR
+ 5
+49
+100
+DictionaryVariables
+280
+0
+ 1
+2
+ 0
+DICTIONARYVAR
+ 5
+4A
+100
+DictionaryVariables
+280
+0
+ 1
+1
+ 0
+ENDSEC
+ 0
+EOF
diff --git a/testdata/scad/misc/localfiles_dir/localfiles_module.scad b/testdata/scad/misc/localfiles_dir/localfiles_module.scad
new file mode 100644
index 0000000..b98a49b
--- /dev/null
+++ b/testdata/scad/misc/localfiles_dir/localfiles_module.scad
@@ -0,0 +1,10 @@
+module localfiles_module()
+{
+ linear_extrude(h=100) import("localfile.dxf");
+ translate([-250,0,0]) linear_extrude(file="localfile.dxf");
+ 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"));
+}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index d178f0f..de4bab7 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -406,6 +406,8 @@ set(CORE_SOURCES
../src/ModuleCache.cc
../src/node.cc
../src/context.cc
+ ../src/modcontext.cc
+ ../src/evalcontext.cc
../src/csgterm.cc
../src/csgtermnormalizer.cc
../src/polyset.cc
@@ -754,12 +756,15 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES}
list(APPEND DUMPTEST_FILES ${MINIMAL_FILES} ${FEATURES_FILES} ${EXAMPLE_FILES})
list(APPEND DUMPTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/include-tests.scad
- ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/use-tests.scad)
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/use-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/localfiles-test.scad)
list(APPEND CGALPNGTEST_FILES ${FEATURES_FILES} ${SCAD_DXF_FILES} ${EXAMPLE_FILES})
list(APPEND CGALPNGTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/include-tests.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/use-tests.scad
- ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/transform-nan-inf-tests.scad)
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/transform-nan-inf-tests.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/localfiles-test.scad)
+
list(APPEND OPENCSGTEST_FILES ${CGALPNGTEST_FILES})
list(APPEND OPENCSGTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/bbox-transform-bug.scad)
list(APPEND OPENCSGTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/bugs/intersection-prune-test.scad)
diff --git a/tests/cgalcachetest.cc b/tests/cgalcachetest.cc
index b7e51b5..b65a2c8 100644
--- a/tests/cgalcachetest.cc
+++ b/tests/cgalcachetest.cc
@@ -30,7 +30,7 @@
#include "parsersettings.h"
#include "node.h"
#include "module.h"
-#include "context.h"
+#include "modcontext.h"
#include "value.h"
#include "export.h"
#include "builtin.h"
@@ -129,11 +129,11 @@ 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"));
- Context root_ctx;
- register_builtin(root_ctx);
+ ModuleContext root_ctx;
+ root_ctx.registerBuiltin();
AbstractModule *root_module;
- ModuleInstantiation root_inst;
+ ModuleInstantiation root_inst("group");
root_module = parsefile(filename);
if (!root_module) {
diff --git a/tests/cgalpngtest.cc b/tests/cgalpngtest.cc
index 947a231..afc3128 100644
--- a/tests/cgalpngtest.cc
+++ b/tests/cgalpngtest.cc
@@ -30,7 +30,7 @@
#include "node.h"
#include "module.h"
#include "polyset.h"
-#include "context.h"
+#include "modcontext.h"
#include "value.h"
#include "export.h"
#include "builtin.h"
@@ -102,11 +102,11 @@ 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"));
- Context root_ctx;
- register_builtin(root_ctx);
+ ModuleContext root_ctx;
+ root_ctx.registerBuiltin();
AbstractModule *root_module;
- ModuleInstantiation root_inst;
+ ModuleInstantiation root_inst("group");
root_module = parsefile(filename);
if (!root_module) {
diff --git a/tests/cgalstlsanitytest.cc b/tests/cgalstlsanitytest.cc
index 228bfde..49a3f8e 100644
--- a/tests/cgalstlsanitytest.cc
+++ b/tests/cgalstlsanitytest.cc
@@ -29,7 +29,7 @@
#include "parsersettings.h"
#include "node.h"
#include "module.h"
-#include "context.h"
+#include "modcontext.h"
#include "value.h"
#include "export.h"
#include "builtin.h"
@@ -84,11 +84,11 @@ 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"));
- Context root_ctx;
- register_builtin(root_ctx);
+ ModuleContext root_ctx;
+ root_ctx.registerBuiltin();
AbstractModule *root_module;
- ModuleInstantiation root_inst;
+ ModuleInstantiation root_inst("group");
root_module = parsefile(filename);
if (!root_module) {
diff --git a/tests/cgaltest.cc b/tests/cgaltest.cc
index 9c8c090..b7ae669 100644
--- a/tests/cgaltest.cc
+++ b/tests/cgaltest.cc
@@ -29,7 +29,7 @@
#include "parsersettings.h"
#include "node.h"
#include "module.h"
-#include "context.h"
+#include "modcontext.h"
#include "value.h"
#include "export.h"
#include "builtin.h"
@@ -81,11 +81,11 @@ 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"));
- Context root_ctx;
- register_builtin(root_ctx);
+ ModuleContext root_ctx;
+ root_ctx.registerBuiltin();
AbstractModule *root_module;
- ModuleInstantiation root_inst;
+ ModuleInstantiation root_inst("group");
root_module = parsefile(filename);
if (!root_module) {
diff --git a/tests/csgtermtest.cc b/tests/csgtermtest.cc
index 864ba5d..f4a88e0 100644
--- a/tests/csgtermtest.cc
+++ b/tests/csgtermtest.cc
@@ -31,7 +31,7 @@
#include "parsersettings.h"
#include "node.h"
#include "module.h"
-#include "context.h"
+#include "modcontext.h"
#include "value.h"
#include "export.h"
#include "builtin.h"
@@ -76,11 +76,11 @@ 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"));
- Context root_ctx;
- register_builtin(root_ctx);
+ ModuleContext root_ctx;
+ root_ctx.registerBuiltin();
AbstractModule *root_module;
- ModuleInstantiation root_inst;
+ ModuleInstantiation root_inst("group");
const AbstractNode *root_node;
root_module = parsefile(filename);
diff --git a/tests/csgtestcore.cc b/tests/csgtestcore.cc
index 1e518e2..6da6411 100644
--- a/tests/csgtestcore.cc
+++ b/tests/csgtestcore.cc
@@ -6,7 +6,7 @@
#include "openscad.h"
#include "parsersettings.h"
#include "builtin.h"
-#include "context.h"
+#include "modcontext.h"
#include "node.h"
#include "module.h"
#include "polyset.h"
@@ -132,11 +132,11 @@ 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"));
- Context root_ctx;
- register_builtin(root_ctx);
+ ModuleContext root_ctx;
+ root_ctx.registerBuiltin();
AbstractModule *root_module;
- ModuleInstantiation root_inst;
+ ModuleInstantiation root_inst("group");
if (sysinfo_dump)
root_module = parse("sphere();","",false);
diff --git a/tests/csgtexttest.cc b/tests/csgtexttest.cc
index 6a72dff..3e26814 100644
--- a/tests/csgtexttest.cc
+++ b/tests/csgtexttest.cc
@@ -31,7 +31,7 @@
#include "parsersettings.h"
#include "node.h"
#include "module.h"
-#include "context.h"
+#include "modcontext.h"
#include "value.h"
#include "export.h"
#include "builtin.h"
@@ -80,11 +80,11 @@ 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"));
- Context root_ctx;
- register_builtin(root_ctx);
+ ModuleContext root_ctx;
+ root_ctx.registerBuiltin();
AbstractModule *root_module;
- ModuleInstantiation root_inst;
+ ModuleInstantiation root_inst("group");
AbstractNode *root_node;
root_module = parsefile(filename);
diff --git a/tests/dumptest.cc b/tests/dumptest.cc
index 4ddefe2..e0d2776 100644
--- a/tests/dumptest.cc
+++ b/tests/dumptest.cc
@@ -29,7 +29,7 @@
#include "parsersettings.h"
#include "node.h"
#include "module.h"
-#include "context.h"
+#include "modcontext.h"
#include "value.h"
#include "export.h"
#include "builtin.h"
@@ -86,11 +86,11 @@ 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"));
- Context root_ctx;
- register_builtin(root_ctx);
+ ModuleContext root_ctx;
+ root_ctx.registerBuiltin();
AbstractModule *root_module;
- ModuleInstantiation root_inst;
+ ModuleInstantiation root_inst("group");
AbstractNode *root_node;
root_module = parsefile(filename);
@@ -115,7 +115,6 @@ int main(int argc, char **argv)
exit(1);
}
- fs::current_path(original_path);
std::ofstream outfile;
outfile.open(outfilename);
outfile << dumpstdstr << "\n";
@@ -124,21 +123,22 @@ int main(int argc, char **argv)
delete root_node;
delete root_module;
+ fs::current_path(original_path);
root_module = parsefile(outfilename);
if (!root_module) {
fprintf(stderr, "Error: Unable to read back dumped file\n");
exit(1);
}
- if (fs::path(filename).has_parent_path()) {
- fs::current_path(fs::path(filename).parent_path());
- }
-
AbstractNode::resetIndexCounter();
root_node = root_module->evaluate(&root_ctx, &root_inst);
tree.setRoot(root_node);
+ if (fs::path(outfilename).has_parent_path()) {
+ fs::current_path(fs::path(outfilename).parent_path());
+ }
+
string readbackstr = dumptree(tree, *root_node);
if (dumpstdstr != readbackstr) {
fprintf(stderr, "Error: Readback is different from original dump:\n");
diff --git a/tests/echotest.cc b/tests/echotest.cc
index af4942b..9924d11 100644
--- a/tests/echotest.cc
+++ b/tests/echotest.cc
@@ -29,7 +29,7 @@
#include "parsersettings.h"
#include "node.h"
#include "module.h"
-#include "context.h"
+#include "modcontext.h"
#include "value.h"
#include "builtin.h"
#include "printutils.h"
@@ -88,11 +88,11 @@ 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"));
- Context root_ctx;
- register_builtin(root_ctx);
+ ModuleContext root_ctx;
+ root_ctx.registerBuiltin();
AbstractModule *root_module;
- ModuleInstantiation root_inst;
+ ModuleInstantiation root_inst("group");
AbstractNode *root_node;
root_module = parsefile(filename);
diff --git a/tests/modulecachetest.cc b/tests/modulecachetest.cc
index 1103720..62f9543 100644
--- a/tests/modulecachetest.cc
+++ b/tests/modulecachetest.cc
@@ -29,7 +29,7 @@
#include "parsersettings.h"
#include "node.h"
#include "module.h"
-#include "context.h"
+#include "modcontext.h"
#include "value.h"
#include "export.h"
#include "builtin.h"
@@ -76,11 +76,11 @@ 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"));
- Context root_ctx;
- register_builtin(root_ctx);
+ ModuleContext root_ctx;
+ root_ctx.registerBuiltin();
AbstractModule *root_module;
- ModuleInstantiation root_inst;
+ ModuleInstantiation root_inst("group");
AbstractNode *root_node;
root_module = parsefile(filename);
diff --git a/tests/regression/cgalpngtest/child-child-test-expected.png b/tests/regression/cgalpngtest/child-child-test-expected.png
new file mode 100644
index 0000000..80b70ba
--- /dev/null
+++ b/tests/regression/cgalpngtest/child-child-test-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/localfiles-test-expected.png b/tests/regression/cgalpngtest/localfiles-test-expected.png
new file mode 100644
index 0000000..3ad3d96
--- /dev/null
+++ b/tests/regression/cgalpngtest/localfiles-test-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/module-recursion-expected.png b/tests/regression/cgalpngtest/module-recursion-expected.png
new file mode 100644
index 0000000..3012a12
--- /dev/null
+++ b/tests/regression/cgalpngtest/module-recursion-expected.png
Binary files differ
diff --git a/tests/regression/cgalpngtest/modulevariables-expected.png b/tests/regression/cgalpngtest/modulevariables-expected.png
new file mode 100644
index 0000000..0dc18e8
--- /dev/null
+++ b/tests/regression/cgalpngtest/modulevariables-expected.png
Binary files differ
diff --git a/tests/regression/dumptest/child-child-test-expected.txt b/tests/regression/dumptest/child-child-test-expected.txt
new file mode 100644
index 0000000..13f098d
--- /dev/null
+++ b/tests/regression/dumptest/child-child-test-expected.txt
@@ -0,0 +1,59 @@
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]) {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 5, r2 = 5, center = false);
+ }
+ }
+ multmatrix([[1, 0, 0, 5], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]) {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 5, r2 = 5, center = false);
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 10], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]) {
+ group() {
+ color([1, 0, 0, 1]) {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 5, r2 = 5, center = false);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 15], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ group() {
+ color([1, 0, 0, 1]) {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]) {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 5, r2 = 5, center = false);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
diff --git a/tests/regression/dumptest/localfiles-test-expected.txt b/tests/regression/dumptest/localfiles-test-expected.txt
new file mode 100644
index 0000000..acdf7e7
--- /dev/null
+++ b/tests/regression/dumptest/localfiles-test-expected.txt
@@ -0,0 +1,17 @@
+ group() {
+ linear_extrude(height = 100, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 2) {
+ import(file = "localfiles_dir/localfile.dxf", layer = "", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2);
+ }
+ multmatrix([[1, 0, 0, -250], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ linear_extrude(file = "localfiles_dir/localfile.dxf", layer = "", origin = [0, 0], scale = 1, height = 100, center = false, convexity = 1, $fn = 0, $fa = 12, $fs = 2);
+ }
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 350], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ rotate_extrude(file = "localfiles_dir/localfile.dxf", layer = "", origin = [0, 0], scale = 1, convexity = 1, $fn = 0, $fa = 12, $fs = 2);
+ }
+ multmatrix([[1, 0, 0, 250], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) {
+ multmatrix([[200, 0, 0, 0], [0, 200, 0, 0], [0, 0, 50, 0], [0, 0, 0, 1]]) {
+ surface(file = "localfiles_dir/localfile.dat", center = false);
+ }
+ }
+ }
+
diff --git a/tests/regression/dumptest/module-recursion-expected.txt b/tests/regression/dumptest/module-recursion-expected.txt
new file mode 100644
index 0000000..9ad8877
--- /dev/null
+++ b/tests/regression/dumptest/module-recursion-expected.txt
@@ -0,0 +1,244 @@
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 1, r1 = 0.2, r2 = 0.2, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 1], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.7, r1 = 0.14, r2 = 0.14, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.7], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.49, r1 = 0.098, r2 = 0.098, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.49], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.343, r1 = 0.0686, r2 = 0.0686, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.343], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.343, r1 = 0.0686, r2 = 0.0686, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.343], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.49, r1 = 0.098, r2 = 0.098, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.49], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.343, r1 = 0.0686, r2 = 0.0686, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.343], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.343, r1 = 0.0686, r2 = 0.0686, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.343], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.7, r1 = 0.14, r2 = 0.14, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.7], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.49, r1 = 0.098, r2 = 0.098, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.49], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.343, r1 = 0.0686, r2 = 0.0686, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.343], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.343, r1 = 0.0686, r2 = 0.0686, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.343], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.49, r1 = 0.098, r2 = 0.098, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.49], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.343, r1 = 0.0686, r2 = 0.0686, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.343], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 0.343, r1 = 0.0686, r2 = 0.0686, center = false);
+ multmatrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0.343], [0, 0, 0, 1]]) {
+ group() {
+ multmatrix([[-1, 0, 0, 0], [0, -0.76604444311, 0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ multmatrix([[1, 0, 0, 0], [0, 0.76604444311, -0.64278760968, 0], [0, 0.64278760968, 0.76604444311, 0], [0, 0, 0, 1]]) {
+ group() {
+ group();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
diff --git a/tests/regression/dumptest/modulevariables-expected.txt b/tests/regression/dumptest/modulevariables-expected.txt
new file mode 100644
index 0000000..fed4bbc
--- /dev/null
+++ b/tests/regression/dumptest/modulevariables-expected.txt
@@ -0,0 +1,4 @@
+ group() {
+ cylinder($fn = 0, $fa = 12, $fs = 2, h = 10, r1 = 23, r2 = 10, center = false);
+ }
+
diff --git a/tests/regression/opencsgtest/child-child-test-expected.png b/tests/regression/opencsgtest/child-child-test-expected.png
new file mode 100644
index 0000000..07d61c0
--- /dev/null
+++ b/tests/regression/opencsgtest/child-child-test-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/localfiles-test-expected.png b/tests/regression/opencsgtest/localfiles-test-expected.png
new file mode 100644
index 0000000..7bc7909
--- /dev/null
+++ b/tests/regression/opencsgtest/localfiles-test-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/module-recursion-expected.png b/tests/regression/opencsgtest/module-recursion-expected.png
new file mode 100644
index 0000000..324c260
--- /dev/null
+++ b/tests/regression/opencsgtest/module-recursion-expected.png
Binary files differ
diff --git a/tests/regression/opencsgtest/modulevariables-expected.png b/tests/regression/opencsgtest/modulevariables-expected.png
new file mode 100644
index 0000000..bf23265
--- /dev/null
+++ b/tests/regression/opencsgtest/modulevariables-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/child-child-test-expected.png b/tests/regression/throwntogethertest/child-child-test-expected.png
new file mode 100644
index 0000000..07d61c0
--- /dev/null
+++ b/tests/regression/throwntogethertest/child-child-test-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/localfiles-test-expected.png b/tests/regression/throwntogethertest/localfiles-test-expected.png
new file mode 100644
index 0000000..7bc7909
--- /dev/null
+++ b/tests/regression/throwntogethertest/localfiles-test-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/module-recursion-expected.png b/tests/regression/throwntogethertest/module-recursion-expected.png
new file mode 100644
index 0000000..324c260
--- /dev/null
+++ b/tests/regression/throwntogethertest/module-recursion-expected.png
Binary files differ
diff --git a/tests/regression/throwntogethertest/modulevariables-expected.png b/tests/regression/throwntogethertest/modulevariables-expected.png
new file mode 100644
index 0000000..bf23265
--- /dev/null
+++ b/tests/regression/throwntogethertest/modulevariables-expected.png
Binary files differ
contact: Jan Huwald // Impressum