diff --git a/openscad.pro b/openscad.pro index ebedbac2..58e0921f 100644 --- a/openscad.pro +++ b/openscad.pro @@ -259,7 +259,9 @@ HEADERS += src/typedefs.h \ src/dxfdim.h \ src/export.h \ src/expression.h \ + src/stackcheck.h \ src/function.h \ + src/exceptions.h \ src/grid.h \ src/highlighter.h \ src/localscope.h \ @@ -335,6 +337,7 @@ SOURCES += src/version_check.cc \ src/handle_dep.cc \ src/value.cc \ src/expr.cc \ + src/stackcheck.cc \ src/func.cc \ src/localscope.cc \ src/module.cc \ diff --git a/src/NULLGL.cc b/src/NULLGL.cc index 496810c2..65d6e5e7 100644 --- a/src/NULLGL.cc +++ b/src/NULLGL.cc @@ -4,17 +4,13 @@ GLView::GLView() {} void GLView::setRenderer(Renderer* r) {} void GLView::initializeGL() {} void GLView::resizeGL(int w, int h) {} -void GLView::setupGimbalCamPerspective() {} -void GLView::setupGimbalCamOrtho(double distance, bool offset) {} -void GLView::setupVectorCamPerspective() {} -void GLView::setupVectorCamOrtho(bool offset) {} -void GLView::setCamera( Camera &cam ) {} +void GLView::setCamera(const Camera &cam ) {assert(false && "not implemented");} void GLView::paintGL() {} -void GLView::vectorCamPaintGL() {} -void GLView::gimbalCamPaintGL() {} -void GLView::showSmallaxes() {} -void GLView::showAxes() {} +void GLView::showSmallaxes(const Color4f &col) {} +void GLView::showAxes(const Color4f &col) {} void GLView::showCrosshairs() {} +void GLView::setColorScheme(const ColorScheme &cs){assert(false && "not implemented");} +void GLView::setColorScheme(const std::string &cs) {assert(false && "not implemented");} #include "ThrownTogetherRenderer.h" @@ -23,12 +19,16 @@ ThrownTogetherRenderer::ThrownTogetherRenderer(CSGChain *root_chain, void ThrownTogetherRenderer::draw(bool /*showfaces*/, bool showedges) const {} void ThrownTogetherRenderer::renderCSGChain(CSGChain *chain, bool highlight, bool background, bool showedges, bool fberror) const {} +BoundingBox ThrownTogetherRenderer::getBoundingBox() const {assert(false && "not implemented");} #include "CGALRenderer.h" CGALRenderer::CGALRenderer(shared_ptr geom) {} CGALRenderer::~CGALRenderer() {} void CGALRenderer::draw(bool showfaces, bool showedges) const {} +BoundingBox CGALRenderer::getBoundingBox() const {assert(false && "not implemented");} +void CGALRenderer::setColorScheme(const ColorScheme &cs){assert(false && "not implemented");} + #include "system-gl.h" diff --git a/src/PlatformUtils-mac.mm b/src/PlatformUtils-mac.mm index c6ba6277..cb93e522 100644 --- a/src/PlatformUtils-mac.mm +++ b/src/PlatformUtils-mac.mm @@ -18,5 +18,22 @@ std::string PlatformUtils::userConfigPath() return std::string([[appSupportDir path] UTF8String]) + std::string("/") + PlatformUtils::OPENSCAD_FOLDER_NAME; } +unsigned long PlatformUtils::stackLimit() +{ + struct rlimit limit; + + int ret = getrlimit(RLIMIT_STACK, &limit); + if (ret == 0) { + if (limit.rlim_cur > STACK_BUFFER_SIZE) { + return limit.rlim_cur - STACK_BUFFER_SIZE; + } + if (limit.rlim_max > STACK_BUFFER_SIZE) { + return limit.rlim_max - STACK_BUFFER_SIZE; + } + } + + return STACK_LIMIT_DEFAULT; +} + void PlatformUtils::ensureStdIO(void) {} diff --git a/src/PlatformUtils-posix.cc b/src/PlatformUtils-posix.cc index 64cc5dbe..3854a5e7 100644 --- a/src/PlatformUtils-posix.cc +++ b/src/PlatformUtils-posix.cc @@ -1,3 +1,5 @@ +#include + #include "PlatformUtils.h" #include "boosty.h" @@ -41,5 +43,22 @@ std::string PlatformUtils::userConfigPath() return ""; } +unsigned long PlatformUtils::stackLimit() +{ + struct rlimit limit; + + int ret = getrlimit(RLIMIT_STACK, &limit); + if (ret == 0) { + if (limit.rlim_cur > STACK_BUFFER_SIZE) { + return limit.rlim_cur - STACK_BUFFER_SIZE; + } + if (limit.rlim_max > STACK_BUFFER_SIZE) { + return limit.rlim_max - STACK_BUFFER_SIZE; + } + } + + return STACK_LIMIT_DEFAULT; +} + void PlatformUtils::ensureStdIO(void) {} diff --git a/src/PlatformUtils-win.cc b/src/PlatformUtils-win.cc index a504bb1f..c94a2a67 100644 --- a/src/PlatformUtils-win.cc +++ b/src/PlatformUtils-win.cc @@ -92,6 +92,11 @@ std::string PlatformUtils::userConfigPath() return retval + std::string("/") + PlatformUtils::OPENSCAD_FOLDER_NAME; } +unsigned long PlatformUtils::stackLimit() +{ + return STACK_LIMIT_DEFAULT; +} + #include #include #include diff --git a/src/PlatformUtils.h b/src/PlatformUtils.h index 4d524332..a5c2ec91 100644 --- a/src/PlatformUtils.h +++ b/src/PlatformUtils.h @@ -4,6 +4,9 @@ #include "boosty.h" +#define STACK_BUFFER_SIZE (64 * 1024) +#define STACK_LIMIT_DEFAULT (8 * 1024 * 1024 - STACK_BUFFER_SIZE) + namespace PlatformUtils { extern const char *OPENSCAD_FOLDER_NAME; @@ -41,6 +44,14 @@ namespace PlatformUtils { */ int setenv(const char *name, const char *value, int overwrite); + /** + * Return system defined stack limit. If the system does not define + * a specific limit, the platform specific code will select a value. + * + * @return maximum stack size in bytes. + */ + unsigned long stackLimit(); + /** * Single character separating path specifications in a list * (e.g. OPENSCADPATH). On Windows that's ';' and on most other diff --git a/src/builtin.cc b/src/builtin.cc index d91d17ad..e495f4a5 100644 --- a/src/builtin.cc +++ b/src/builtin.cc @@ -95,19 +95,19 @@ std::string Builtins::isDeprecated(const std::string &name) Builtins::Builtins() { - this->globalscope.assignments.push_back(Assignment("$fn", boost::shared_ptr(new Expression(Value(0.0))))); - this->globalscope.assignments.push_back(Assignment("$fs", boost::shared_ptr(new Expression(Value(2.0))))); - this->globalscope.assignments.push_back(Assignment("$fa", boost::shared_ptr(new Expression(Value(12.0))))); - this->globalscope.assignments.push_back(Assignment("$t", boost::shared_ptr(new Expression(Value(0.0))))); + this->globalscope.assignments.push_back(Assignment("$fn", boost::shared_ptr(new ExpressionConst(ValuePtr(0.0))))); + this->globalscope.assignments.push_back(Assignment("$fs", boost::shared_ptr(new ExpressionConst(ValuePtr(2.0))))); + this->globalscope.assignments.push_back(Assignment("$fa", boost::shared_ptr(new ExpressionConst(ValuePtr(12.0))))); + this->globalscope.assignments.push_back(Assignment("$t", boost::shared_ptr(new ExpressionConst(ValuePtr(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->globalscope.assignments.push_back(Assignment("$vpt", boost::shared_ptr(new Expression(zero3val)))); - this->globalscope.assignments.push_back(Assignment("$vpr", boost::shared_ptr(new Expression(zero3val)))); - this->globalscope.assignments.push_back(Assignment("$vpd", boost::shared_ptr(new Expression(500)))); + ValuePtr zero3val(zero3); + this->globalscope.assignments.push_back(Assignment("$vpt", boost::shared_ptr(new ExpressionConst(zero3val)))); + this->globalscope.assignments.push_back(Assignment("$vpr", boost::shared_ptr(new ExpressionConst(zero3val)))); + this->globalscope.assignments.push_back(Assignment("$vpd", boost::shared_ptr(new ExpressionConst(ValuePtr(500))))); } Builtins::~Builtins() diff --git a/src/cgaladv.cc b/src/cgaladv.cc index 1822cac0..20addb78 100644 --- a/src/cgaladv.cc +++ b/src/cgaladv.cc @@ -64,8 +64,11 @@ AbstractNode *CgaladvModule::instantiate(const Context *ctx, const ModuleInstant c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - Value convexity, path, subdiv_type, level; - + ValuePtr convexity = ValuePtr::undefined; + ValuePtr path = ValuePtr::undefined; + ValuePtr subdiv_type = ValuePtr::undefined; + ValuePtr level = ValuePtr::undefined; + if (type == MINKOWSKI) { convexity = c.lookup_variable("convexity", true); } @@ -82,31 +85,31 @@ AbstractNode *CgaladvModule::instantiate(const Context *ctx, const ModuleInstant } if (type == RESIZE) { - Value ns = c.lookup_variable("newsize"); + ValuePtr ns = c.lookup_variable("newsize"); node->newsize << 0,0,0; - if ( ns.type() == Value::VECTOR ) { - Value::VectorType vs = ns.toVector(); + if ( ns->type() == Value::VECTOR ) { + const Value::VectorType &vs = ns->toVector(); if ( vs.size() >= 1 ) node->newsize[0] = vs[0].toDouble(); if ( vs.size() >= 2 ) node->newsize[1] = vs[1].toDouble(); if ( vs.size() >= 3 ) node->newsize[2] = vs[2].toDouble(); } - Value autosize = c.lookup_variable("auto"); + ValuePtr autosize = c.lookup_variable("auto"); node->autosize << false, false, false; - if ( autosize.type() == Value::VECTOR ) { - Value::VectorType va = autosize.toVector(); + if ( autosize->type() == Value::VECTOR ) { + const Value::VectorType &va = autosize->toVector(); if ( va.size() >= 1 ) node->autosize[0] = va[0].toBool(); if ( va.size() >= 2 ) node->autosize[1] = va[1].toBool(); if ( va.size() >= 3 ) node->autosize[2] = va[2].toBool(); } - else if ( autosize.type() == Value::BOOL ) { - node->autosize << autosize.toBool(),autosize.toBool(),autosize.toBool(); + else if ( autosize->type() == Value::BOOL ) { + node->autosize << autosize->toBool(),autosize->toBool(),autosize->toBool(); } } - node->convexity = (int)convexity.toDouble(); + node->convexity = (int)convexity->toDouble(); node->path = path; - node->subdiv_type = subdiv_type.toString(); - node->level = (int)level.toDouble(); + node->subdiv_type = subdiv_type->toString(); + node->level = (int)level->toDouble(); if (node->level <= 1) node->level = 1; @@ -151,7 +154,7 @@ std::string CgaladvNode::toString() const stream << "(convexity = " << this->convexity << ")"; break; case GLIDE: - stream << "(path = " << this->path << ", convexity = " << this->convexity << ")"; + stream << "(path = " << *this->path << ", convexity = " << this->convexity << ")"; break; case SUBDIV: stream << "(level = " << this->level << ", convexity = " << this->convexity << ")"; diff --git a/src/cgaladvnode.h b/src/cgaladvnode.h index 6794ae90..a2f81fe7 100644 --- a/src/cgaladvnode.h +++ b/src/cgaladvnode.h @@ -26,7 +26,7 @@ public: virtual std::string toString() const; virtual std::string name() const; - Value path; + ValuePtr path; std::string subdiv_type; int convexity, level; Vector3d newsize; diff --git a/src/cgalutils.cc b/src/cgalutils.cc index 5338a14e..5693ed60 100644 --- a/src/cgalutils.cc +++ b/src/cgalutils.cc @@ -35,7 +35,7 @@ namespace /* anonymous */ { return Result(CGAL::to_double(v[0]),CGAL::to_double(v[1]),CGAL::to_double(v[2])); } -#define GEN_SURFACE_DEBUG +#undef GEN_SURFACE_DEBUG class CGAL_Build_PolySet : public CGAL::Modifier_base { diff --git a/src/color.cc b/src/color.cc index 816b77b4..d5615aa3 100644 --- a/src/color.cc +++ b/src/color.cc @@ -221,15 +221,15 @@ AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantia c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - Value v = c.lookup_variable("c"); - if (v.type() == Value::VECTOR) { + ValuePtr v = c.lookup_variable("c"); + if (v->type() == Value::VECTOR) { for (size_t i = 0; i < 4; i++) { - node->color[i] = i < v.toVector().size() ? v.toVector()[i].toDouble() : 1.0; + node->color[i] = i < v->toVector().size() ? v->toVector()[i].toDouble() : 1.0; if (node->color[i] > 1) PRINTB_NOCACHE("WARNING: color() expects numbers between 0.0 and 1.0. Value of %.1f is too large.", node->color[i]); } - } else if (v.type() == Value::STRING) { - std::string colorname = v.toString(); + } else if (v->type() == Value::STRING) { + std::string colorname = v->toString(); boost::algorithm::to_lower(colorname); Color4f color; if (webcolors.find(colorname) != webcolors.end()) { @@ -239,9 +239,9 @@ AbstractNode *ColorModule::instantiate(const Context *ctx, const ModuleInstantia PRINT_NOCACHE("WARNING: http://en.wikipedia.org/wiki/Web_colors"); } } - Value alpha = c.lookup_variable("alpha"); - if (alpha.type() == Value::NUMBER) { - node->color[3] = alpha.toDouble(); + ValuePtr alpha = c.lookup_variable("alpha"); + if (alpha->type() == Value::NUMBER) { + node->color[3] = alpha->toDouble(); } std::vector instantiatednodes = inst->instantiateChildren(evalctx); diff --git a/src/context.cc b/src/context.cc index 6a05eb2e..c7903967 100644 --- a/src/context.cc +++ b/src/context.cc @@ -78,14 +78,14 @@ void Context::setVariables(const AssignmentList &args, const EvalContext *evalctx) { BOOST_FOREACH(const Assignment &arg, args) { - set_variable(arg.first, arg.second ? arg.second->evaluate(this->parent) : Value()); + set_variable(arg.first, arg.second ? arg.second->evaluate(this->parent) : ValuePtr::undefined); } if (evalctx) { size_t posarg = 0; for (size_t i=0; inumArgs(); i++) { const std::string &name = evalctx->getArgName(i); - const Value &val = evalctx->getArgValue(i); + ValuePtr val = evalctx->getArgValue(i); if (name.empty()) { if (posarg < args.size()) this->set_variable(args[posarg++].first, val); } else { @@ -95,13 +95,18 @@ void Context::setVariables(const AssignmentList &args, } } -void Context::set_variable(const std::string &name, const Value &value) +void Context::set_variable(const std::string &name, const ValuePtr &value) { if (is_config_variable(name)) this->config_variables[name] = value; else this->variables[name] = value; } -void Context::set_constant(const std::string &name, const Value &value) +void Context::set_variable(const std::string &name, const Value &value) +{ + set_variable(name, ValuePtr(value)); +} + +void Context::set_constant(const std::string &name, const ValuePtr &value) { if (this->constants.find(name) != this->constants.end()) { PRINTB("WARNING: Attempt to modify constant '%s'.", name); @@ -111,11 +116,23 @@ void Context::set_constant(const std::string &name, const Value &value) } } -Value Context::lookup_variable(const std::string &name, bool silent) const +void Context::set_constant(const std::string &name, const Value &value) +{ + set_constant(name, ValuePtr(value)); +} + +void Context::apply_variables(const Context &other) +{ + for (ValueMap::const_iterator it = other.variables.begin();it != other.variables.end();it++) { + set_variable((*it).first, (*it).second); + } +} + +ValuePtr Context::lookup_variable(const std::string &name, bool silent) const { if (!this->ctx_stack) { PRINT("ERROR: Context had null stack in lookup_variable()!!"); - return Value(); + return ValuePtr::undefined; } if (is_config_variable(name)) { for (int i = this->ctx_stack->size()-1; i >= 0; i--) { @@ -123,7 +140,7 @@ Value Context::lookup_variable(const std::string &name, bool silent) const if (confvars.find(name) != confvars.end()) return confvars.find(name)->second; } - return Value(); + return ValuePtr::undefined; } if (!this->parent && this->constants.find(name) != this->constants.end()) return this->constants.find(name)->second; @@ -133,7 +150,7 @@ Value Context::lookup_variable(const std::string &name, bool silent) const return this->parent->lookup_variable(name, silent); if (!silent) PRINTB("WARNING: Ignoring unknown variable '%s'.", name); - return Value(); + return ValuePtr::undefined; } bool Context::has_local_variable(const std::string &name) const @@ -145,17 +162,30 @@ bool Context::has_local_variable(const std::string &name) const return variables.find(name) != variables.end(); } -Value Context::evaluate_function(const std::string &name, const EvalContext *evalctx) const +/** + * This is separated because PRINTB uses quite a lot of stack space + * and the methods using it evaluate_function() and instantiate_module() + * are called often when recursive functions or modules are evaluated. + * + * @param what what is ignored + * @param name name of the ignored object + */ +static void print_ignore_warning(const char *what, const char *name) +{ + PRINTB("WARNING: Ignoring unknown %s '%s'.", what % name); +} + +ValuePtr Context::evaluate_function(const std::string &name, const EvalContext *evalctx) const { if (this->parent) return this->parent->evaluate_function(name, evalctx); - PRINTB("WARNING: Ignoring unknown function '%s'.", name); - return Value(); + print_ignore_warning("function", name.c_str()); + return ValuePtr::undefined; } AbstractNode *Context::instantiate_module(const ModuleInstantiation &inst, EvalContext *evalctx) const { if (this->parent) return this->parent->instantiate_module(inst, evalctx); - PRINTB("WARNING: Ignoring unknown module '%s'.", inst.name()); + print_ignore_warning("module", inst.name().c_str()); return NULL; } @@ -190,7 +220,7 @@ std::string Context::dump(const AbstractModule *mod, const ModuleInstantiation * } } } - typedef std::pair ValueMapType; + typedef std::pair ValueMapType; s << " vars:"; BOOST_FOREACH(const ValueMapType &v, constants) { s << boost::format(" %s = %s") % v.first % v.second; diff --git a/src/context.h b/src/context.h index 29c887ce..f3b1bcc0 100644 --- a/src/context.h +++ b/src/context.h @@ -5,6 +5,7 @@ #include #include "value.h" #include "typedefs.h" +#include "memory.h" class Context { @@ -14,29 +15,32 @@ public: virtual ~Context(); const Context *getParent() const { return this->parent; } - virtual Value evaluate_function(const std::string &name, const class EvalContext *evalctx) const; + virtual ValuePtr evaluate_function(const std::string &name, const class EvalContext *evalctx) const; virtual class AbstractNode *instantiate_module(const class ModuleInstantiation &inst, EvalContext *evalctx) const; void setVariables(const AssignmentList &args, const class EvalContext *evalctx = NULL); + void set_variable(const std::string &name, const ValuePtr &value); void set_variable(const std::string &name, const Value &value); + void set_constant(const std::string &name, const ValuePtr &value); void set_constant(const std::string &name, const Value &value); - Value lookup_variable(const std::string &name, bool silent = false) const; + void apply_variables(const Context &other); + ValuePtr lookup_variable(const std::string &name, bool silent = false) const; bool has_local_variable(const std::string &name) 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: protected: const Context *parent; Stack *ctx_stack; - typedef boost::unordered_map ValueMap; + typedef boost::unordered_map ValueMap; ValueMap constants; ValueMap variables; ValueMap config_variables; diff --git a/src/control.cc b/src/control.cc index b2518670..bcbae4a5 100644 --- a/src/control.cc +++ b/src/control.cc @@ -63,7 +63,7 @@ public: // methods static const EvalContext* getLastModuleCtx(const EvalContext *evalctx); - static AbstractNode* getChild(const Value& value, const EvalContext* modulectx); + static AbstractNode* getChild(const Value &value, const EvalContext* modulectx); private: // data Type type; @@ -75,27 +75,27 @@ void ControlModule::for_eval(AbstractNode &node, const ModuleInstantiation &inst { if (evalctx->numArgs() > l) { const std::string &it_name = evalctx->getArgName(l); - const Value &it_values = evalctx->getArgValue(l, ctx); + ValuePtr it_values = evalctx->getArgValue(l, ctx); Context c(ctx); - if (it_values.type() == Value::RANGE) { - Value::RangeType range = it_values.toRange(); + if (it_values->type() == Value::RANGE) { + Value::RangeType range = it_values->toRange(); boost::uint32_t steps = range.nbsteps(); if (steps >= 10000) { PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps); } else { for (Value::RangeType::iterator it = range.begin();it != range.end();it++) { - c.set_variable(it_name, Value(*it)); + c.set_variable(it_name, ValuePtr(*it)); 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]); + 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, &c, evalctx); } } - else if (it_values.type() != Value::UNDEFINED) { + else if (it_values->type() != Value::UNDEFINED) { c.set_variable(it_name, it_values); for_eval(node, inst, l+1, &c, evalctx); } @@ -173,7 +173,7 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns int n = 0; if (evalctx->numArgs() > 0) { double v; - if (evalctx->getArgValue(0).getDouble(v)) { + if (evalctx->getArgValue(0)->getDouble(v)) { n = trunc(v); if (n < 0) { PRINTB("WARNING: Negative child index (%d) not allowed", n); @@ -221,13 +221,13 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns } else if (evalctx->numArgs()>0) { // one (or more ignored) parameter - const Value& value = evalctx->getArgValue(0); - if (value.type() == Value::NUMBER) { - return getChild(value,modulectx); + ValuePtr value = evalctx->getArgValue(0); + if (value->type() == Value::NUMBER) { + return getChild(*value, modulectx); } - else if (value.type() == Value::VECTOR) { + else if (value->type() == Value::VECTOR) { AbstractNode* node = new AbstractNode(inst); - const Value::VectorType& vect = value.toVector(); + const Value::VectorType& vect = value->toVector(); foreach (const Value::VectorType::value_type& vectvalue, vect) { AbstractNode* childnode = getChild(vectvalue,modulectx); if (childnode==NULL) continue; // error @@ -235,9 +235,9 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns } return node; } - else if (value.type() == Value::RANGE) { + else if (value->type() == Value::RANGE) { AbstractNode* node = new AbstractNode(inst); - Value::RangeType range = value.toRange(); + Value::RangeType range = value->toRange(); boost::uint32_t steps = range.nbsteps(); if (steps >= 10000) { PRINTB("WARNING: Bad range parameter for children: too many elements (%lu).", steps); @@ -253,7 +253,7 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns else { // Invalid parameter // (e.g. first child of difference is invalid) - PRINTB("WARNING: Bad parameter type (%s) for children, only accept: empty, number, vector, range.", value.toString()); + PRINTB("WARNING: Bad parameter type (%s) for children, only accept: empty, number, vector, range.", value->toString()); return NULL; } } @@ -268,11 +268,11 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns for (size_t i = 0; i < inst->arguments.size(); i++) { if (i > 0) msg << ", "; if (!evalctx->getArgName(i).empty()) msg << evalctx->getArgName(i) << " = "; - Value val = evalctx->getArgValue(i); - if (val.type() == Value::STRING) { - msg << '"' << val.toString() << '"'; + ValuePtr val = evalctx->getArgValue(i); + if (val->type() == Value::STRING) { + msg << '"' << val->toString() << '"'; } else { - msg << val.toString(); + msg << val->toString(); } } PRINTB("%s", msg.str()); @@ -308,7 +308,7 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns case IF: { node = new AbstractNode(inst); const IfElseModuleInstantiation *ifelse = dynamic_cast(inst); - if (evalctx->numArgs() > 0 && evalctx->getArgValue(0).toBool()) { + if (evalctx->numArgs() > 0 && evalctx->getArgValue(0)->toBool()) { inst->scope.apply(*evalctx); std::vector instantiatednodes = ifelse->instantiateChildren(evalctx); node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); diff --git a/src/dxfdim.cc b/src/dxfdim.cc index 8f55a2fa..8bcd1bc5 100644 --- a/src/dxfdim.cc +++ b/src/dxfdim.cc @@ -39,11 +39,11 @@ #include #include -boost::unordered_map dxf_dim_cache; -boost::unordered_map dxf_cross_cache; +boost::unordered_map dxf_dim_cache; +boost::unordered_map dxf_cross_cache; namespace fs = boost::filesystem; -Value builtin_dxf_dim(const Context *ctx, const EvalContext *evalctx) +ValuePtr builtin_dxf_dim(const Context *ctx, const EvalContext *evalctx) { std::string filename; std::string layername; @@ -56,17 +56,16 @@ Value builtin_dxf_dim(const Context *ctx, const EvalContext *evalctx) // since the path is only available for ModuleInstantiations, not function expressions. // See issue #217 for (size_t i = 0; i < evalctx->numArgs(); i++) { - if (evalctx->getArgName(i) == "file") - filename = lookup_file(evalctx->getArgValue(i).toString(), + ValuePtr n = evalctx->getArgName(i); + ValuePtr v = evalctx->getArgValue(i); + if (evalctx->getArgName(i) == "file") { + filename = lookup_file(v->toString(), evalctx->documentPath(), ctx->documentPath()); - if (evalctx->getArgName(i) == "layer") - layername = evalctx->getArgValue(i).toString(); - if (evalctx->getArgName(i) == "origin") - evalctx->getArgValue(i).getVec2(xorigin, yorigin); - if (evalctx->getArgName(i) == "scale") - evalctx->getArgValue(i).getDouble(scale); - if (evalctx->getArgName(i) == "name") - name = evalctx->getArgValue(i).toString(); + } + if (n == "layer") layername = v->toString(); + if (n == "origin") v->getVec2(xorigin, yorigin); + if (n == "scale") v->getDouble(scale); + if (n == "name") name = v->toString(); } std::stringstream keystream; @@ -100,46 +99,46 @@ Value builtin_dxf_dim(const Context *ctx, const EvalContext *evalctx) double y = d->coords[4][1] - d->coords[3][1]; double angle = d->angle; double distance_projected_on_line = fabs(x * cos(angle*M_PI/180) + y * sin(angle*M_PI/180)); - return dxf_dim_cache[key] = Value(distance_projected_on_line); + return dxf_dim_cache[key] = ValuePtr(distance_projected_on_line); } else if (type == 1) { // Aligned double x = d->coords[4][0] - d->coords[3][0]; double y = d->coords[4][1] - d->coords[3][1]; - return dxf_dim_cache[key] = Value(sqrt(x*x + y*y)); + return dxf_dim_cache[key] = ValuePtr(sqrt(x*x + y*y)); } else if (type == 2) { // Angular double a1 = atan2(d->coords[0][0] - d->coords[5][0], d->coords[0][1] - d->coords[5][1]); double a2 = atan2(d->coords[4][0] - d->coords[3][0], d->coords[4][1] - d->coords[3][1]); - return dxf_dim_cache[key] = Value(fabs(a1 - a2) * 180 / M_PI); + return dxf_dim_cache[key] = ValuePtr(fabs(a1 - a2) * 180 / M_PI); } else if (type == 3 || type == 4) { // Diameter or Radius double x = d->coords[5][0] - d->coords[0][0]; double y = d->coords[5][1] - d->coords[0][1]; - return dxf_dim_cache[key] = Value(sqrt(x*x + y*y)); + return dxf_dim_cache[key] = ValuePtr(sqrt(x*x + y*y)); } else if (type == 5) { // Angular 3 Point } else if (type == 6) { // Ordinate - return dxf_dim_cache[key] = Value((d->type & 64) ? d->coords[3][0] : d->coords[3][1]); + return dxf_dim_cache[key] = ValuePtr((d->type & 64) ? d->coords[3][0] : d->coords[3][1]); } PRINTB("WARNING: Dimension '%s' in '%s', layer '%s' has unsupported type!", name % filename % layername); - return Value(); + return ValuePtr::undefined; } PRINTB("WARNING: Can't find dimension '%s' in '%s', layer '%s'!", name % filename % layername); - return Value(); + return ValuePtr::undefined; } -Value builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx) +ValuePtr builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx) { std::string filename; std::string layername; @@ -151,14 +150,12 @@ Value builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx) // since the path is only available for ModuleInstantiations, not function expressions. // See isse #217 for (size_t i = 0; i < evalctx->numArgs(); i++) { - if (evalctx->getArgName(i) == "file") - filename = ctx->getAbsolutePath(evalctx->getArgValue(i).toString()); - if (evalctx->getArgName(i) == "layer") - layername = evalctx->getArgValue(i).toString(); - if (evalctx->getArgName(i) == "origin") - evalctx->getArgValue(i).getVec2(xorigin, yorigin); - if (evalctx->getArgName(i) == "scale") - evalctx->getArgValue(i).getDouble(scale); + ValuePtr n = evalctx->getArgName(i); + ValuePtr v = evalctx->getArgValue(i); + if (n == "file") filename = ctx->getAbsolutePath(v->toString()); + if (n == "layer") layername = v->toString(); + if (n == "origin") v->getVec2(xorigin, yorigin); + if (n == "scale") v->getDouble(scale); } std::stringstream keystream; @@ -174,8 +171,9 @@ Value builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx) << "|" << filesize; std::string key = keystream.str(); - if (dxf_cross_cache.find(key) != dxf_cross_cache.end()) + if (dxf_cross_cache.find(key) != dxf_cross_cache.end()) { return dxf_cross_cache.find(key)->second; + } DxfData dxf(36, 0, 0, filename, layername, xorigin, yorigin, scale); @@ -204,13 +202,13 @@ Value builtin_dxf_cross(const Context *ctx, const EvalContext *evalctx) Value::VectorType ret; ret.push_back(Value(x)); ret.push_back(Value(y)); - return dxf_cross_cache[key] = Value(ret); + return dxf_cross_cache[key] = ValuePtr(ret); } } PRINTB("WARNING: Can't find cross in '%s', layer '%s'!", filename % layername); - return Value(); + return ValuePtr::undefined; } void initialize_builtin_dxf_dim() diff --git a/src/dxfdim.h b/src/dxfdim.h index d84d07ce..bdadcee3 100644 --- a/src/dxfdim.h +++ b/src/dxfdim.h @@ -3,5 +3,5 @@ #include #include "value.h" -extern boost::unordered_map dxf_dim_cache; -extern boost::unordered_map dxf_cross_cache; +extern boost::unordered_map dxf_dim_cache; +extern boost::unordered_map dxf_cross_cache; diff --git a/src/evalcontext.cc b/src/evalcontext.cc index 35c6f092..79f078b7 100644 --- a/src/evalcontext.cc +++ b/src/evalcontext.cc @@ -5,6 +5,7 @@ #include "printutils.h" #include "builtin.h" #include "localscope.h" +#include "exceptions.h" #include @@ -20,11 +21,15 @@ const std::string &EvalContext::getArgName(size_t i) const return this->eval_arguments[i].first; } -Value EvalContext::getArgValue(size_t i, const Context *ctx) const +ValuePtr EvalContext::getArgValue(size_t i, const Context *ctx) const { assert(i < this->eval_arguments.size()); const Assignment &arg = this->eval_arguments[i]; - return arg.second ? arg.second->evaluate(ctx ? ctx : this) : Value(); + ValuePtr v; + if (arg.second) { + v = arg.second->evaluate(ctx ? ctx : this); + } + return v; } size_t EvalContext::numChildren() const @@ -62,7 +67,7 @@ std::string EvalContext::dump(const AbstractModule *mod, const ModuleInstantiati if (m) { s << boost::format(" module args:"); BOOST_FOREACH(const Assignment &arg, m->definition_arguments) { - s << boost::format(" %s = %s") % arg.first % variables[arg.first]; + s << boost::format(" %s = %s") % arg.first % *(variables[arg.first]); } } } diff --git a/src/evalcontext.h b/src/evalcontext.h index d591c39f..f5054264 100644 --- a/src/evalcontext.h +++ b/src/evalcontext.h @@ -17,7 +17,7 @@ public: size_t numArgs() const { return this->eval_arguments.size(); } const std::string &getArgName(size_t i) const; - Value getArgValue(size_t i, const Context *ctx = NULL) const; + ValuePtr getArgValue(size_t i, const Context *ctx = NULL) const; size_t numChildren() const; ModuleInstantiation *getChild(size_t i) const; diff --git a/src/exceptions.h b/src/exceptions.h new file mode 100644 index 00000000..21c4a2ed --- /dev/null +++ b/src/exceptions.h @@ -0,0 +1,16 @@ +#include + +class RecursionException: public std::exception { +public: + RecursionException(const char *recursiontype, const std::string &name) + : rectype(recursiontype), name(name) {} + virtual ~RecursionException() throw() {} + virtual const char *what() const throw() { + std::stringstream out; + out << "ERROR: Recursion detected calling " << this->rectype << " '" << this->name << "'"; + return out.str().c_str(); + } +private: + const char *rectype; + const std::string name; +}; diff --git a/src/expr.cc b/src/expr.cc index a1bf5d2a..77c5ac77 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -23,7 +23,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ - #include "expression.h" #include "value.h" #include "evalcontext.h" @@ -32,123 +31,21 @@ #include #include "stl-utils.h" #include "printutils.h" +#include "stackcheck.h" +#include "exceptions.h" #include #include -Expression::Expression() : recursioncount(0) -{ -} - -Expression::Expression(const std::string &type, Expression *left, Expression *right) - : type(type), recursioncount(0) -{ - this->children.push_back(left); - this->children.push_back(right); -} - -Expression::Expression(const std::string &type, Expression *expr) - : type(type), recursioncount(0) -{ - this->children.push_back(expr); -} - -Expression::Expression(const Value &val) : const_value(val), type("C"), recursioncount(0) -{ -} - -Expression::~Expression() -{ - std::for_each(this->children.begin(), this->children.end(), del_fun()); -} - -Value Expression::sub_evaluate_range(const Context *context) const -{ - Value v1 = this->children[0]->evaluate(context); - if (v1.type() == Value::NUMBER) { - Value v2 = this->children[1]->evaluate(context); - if (v2.type() == Value::NUMBER) { - if (this->children.size() == 2) { - Value::RangeType range(v1.toDouble(), v2.toDouble()); - return Value(range); - } else { - Value v3 = this->children[2]->evaluate(context); - if (v3.type() == Value::NUMBER) { - Value::RangeType range(v1.toDouble(), v2.toDouble(), v3.toDouble()); - return Value(range); - } - } - } - } - return Value(); -} - -Value Expression::sub_evaluate_member(const Context *context) const -{ - Value v = this->children[0]->evaluate(context); - - if (v.type() == Value::VECTOR) { - if (this->var_name == "x") return v[0]; - if (this->var_name == "y") return v[1]; - if (this->var_name == "z") return v[2]; - } else if (v.type() == Value::RANGE) { - if (this->var_name == "begin") return Value(v[0]); - if (this->var_name == "step") return Value(v[1]); - if (this->var_name == "end") return Value(v[2]); - } - return Value(); -} - -Value Expression::sub_evaluate_vector(const Context *context) const -{ - Value::VectorType vec; - BOOST_FOREACH(const Expression *e, this->children) { - vec.push_back(e->evaluate(context)); - } - return Value(vec); -} - -Value Expression::sub_evaluate_function(const Context *context) const -{ - if (this->recursioncount >= 1000) { - PRINTB("ERROR: Recursion detected calling function '%s'", this->call_funcname); - // TO DO: throw function_recursion_detected(); - return Value(); - } - this->recursioncount += 1; - EvalContext c(context, this->call_arguments); - const Value &result = context->evaluate_function(this->call_funcname, &c); - this->recursioncount -= 1; - return result; -} - -#define TYPE2INT(c,c1) ((int)(c) | ((int)(c1)<<8)) - -static inline int type2int(register const char *typestr) -{ - // the following asserts basic ASCII only so sign extension does not matter - // utilize the fact that type strings must have one or two non-null characters -#if 0 // defined(DEBUG) // may need this code as template for future development - register int c1, result = *typestr; - if (result && 0 != (c1 = typestr[1])) { - // take the third character for error checking only - result |= (c1 << 8) | ((int)typestr[2] << 16); - } - return result; -#else - return TYPE2INT(typestr[0], typestr[1]); -#endif -} - // unnamed namespace namespace { Value::VectorType flatten(Value::VectorType const& vec) { int n = 0; - for (int i = 0; i < vec.size(); i++) { + for (unsigned int i = 0; i < vec.size(); i++) { assert(vec[i].type() == Value::VECTOR); n += vec[i].toVector().size(); } Value::VectorType ret; ret.reserve(n); - for (int i = 0; i < vec.size(); i++) { + for (unsigned int i = 0; i < vec.size(); i++) { std::copy(vec[i].toVector().begin(),vec[i].toVector().end(),std::back_inserter(ret)); } return ret; @@ -159,9 +56,9 @@ namespace { const bool allow_reassignment = false; - for (int i = 0; i < let_context.numArgs(); i++) { + for (unsigned int i = 0; i < let_context.numArgs(); i++) { if (!allow_reassignment && context->has_local_variable(let_context.getArgName(i))) { - PRINTB("WARNING: Ignoring duplicate variable assignment %s = %s", let_context.getArgName(i) % let_context.getArgValue(i, context).toString()); + PRINTB("WARNING: Ignoring duplicate variable assignment %s = %s", let_context.getArgName(i) % let_context.getArgValue(i, context)->toString()); } else { // NOTE: iteratively evaluated list of arguments context->set_variable(let_context.getArgName(i), let_context.getArgValue(i, context)); @@ -170,133 +67,32 @@ namespace { } } -Value Expression::sub_evaluate_let_expression(const Context *context) const +Expression::Expression() : first(NULL), second(NULL), third(NULL) { - Context c(context); - evaluate_sequential_assignment(this->call_arguments, &c); - - return this->children[0]->evaluate(&c); } -Value Expression::sub_evaluate_list_comprehension(const Context *context) const +Expression::Expression(Expression *expr) : first(expr), second(NULL), third(NULL) { - Value::VectorType vec; - - if (this->call_funcname == "if") { - if (this->children[0]->evaluate(context)) { - if (this->children[1]->type == "c") { - return this->children[1]->evaluate(context); - } else { - vec.push_back(this->children[1]->evaluate(context)); - } - } - return vec; - } else if (this->call_funcname == "for") { - EvalContext for_context(context, this->call_arguments); - - Context assign_context(context); - - // comprehension for statements are by the parser reduced to only contain one single element - const std::string &it_name = for_context.getArgName(0); - const Value &it_values = for_context.getArgValue(0, &assign_context); - - Context c(context); - - if (it_values.type() == Value::RANGE) { - Value::RangeType range = it_values.toRange(); - boost::uint32_t steps = range.nbsteps(); - if (steps >= 1000000) { - PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps); - } else { - for (Value::RangeType::iterator it = range.begin();it != range.end();it++) { - c.set_variable(it_name, Value(*it)); - vec.push_back(this->children[0]->evaluate(&c)); - } - } - } - 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]); - vec.push_back(this->children[0]->evaluate(&c)); - } - } - else if (it_values.type() != Value::UNDEFINED) { - c.set_variable(it_name, it_values); - vec.push_back(this->children[0]->evaluate(&c)); - } - if (this->children[0]->type == "c") { - return flatten(vec); - } else { - return vec; - } - } else if (this->call_funcname == "let") { - Context c(context); - evaluate_sequential_assignment(this->call_arguments, &c); - - return this->children[0]->evaluate(&c); - } else { - abort(); - } + children.push_back(expr); } - -Value Expression::evaluate(const Context *context) const +Expression::Expression(Expression *left, Expression *right) : first(left), second(right), third(NULL) { - switch (type2int(this->type.c_str())) { - case '!': - return ! this->children[0]->evaluate(context); - case TYPE2INT('&','&'): - return this->children[0]->evaluate(context) && this->children[1]->evaluate(context); - case TYPE2INT('|','|'): - return this->children[0]->evaluate(context) || this->children[1]->evaluate(context); - case '*': - return this->children[0]->evaluate(context) * this->children[1]->evaluate(context); - case '/': - return this->children[0]->evaluate(context) / this->children[1]->evaluate(context); - case '%': - return this->children[0]->evaluate(context) % this->children[1]->evaluate(context); - case '+': - return this->children[0]->evaluate(context) + this->children[1]->evaluate(context); - case '-': - return this->children[0]->evaluate(context) - this->children[1]->evaluate(context); - case '<': - return this->children[0]->evaluate(context) < this->children[1]->evaluate(context); - case TYPE2INT('<','='): - return this->children[0]->evaluate(context) <= this->children[1]->evaluate(context); - case TYPE2INT('=','='): - return this->children[0]->evaluate(context) == this->children[1]->evaluate(context); - case TYPE2INT('!','='): - return this->children[0]->evaluate(context) != this->children[1]->evaluate(context); - case TYPE2INT('>','='): - return this->children[0]->evaluate(context) >= this->children[1]->evaluate(context); - case '>': - return this->children[0]->evaluate(context) > this->children[1]->evaluate(context); - case TYPE2INT('?',':'): - return this->children[this->children[0]->evaluate(context) ? 1 : 2]->evaluate(context); - case TYPE2INT('[',']'): - return this->children[0]->evaluate(context)[this->children[1]->evaluate(context)]; - case 'I': - return -this->children[0]->evaluate(context); - case 'C': - return this->const_value; - case 'R': - return sub_evaluate_range(context); - case 'V': - return sub_evaluate_vector(context); - case 'L': - return context->lookup_variable(this->var_name); - case 'N': - return sub_evaluate_member(context); - case 'F': - return sub_evaluate_function(context); - case 'l': - return sub_evaluate_let_expression(context); - case 'i': // list comprehension expression - return this->children[0]->evaluate(context); - case 'c': - return sub_evaluate_list_comprehension(context); - } - abort(); + children.push_back(left); + children.push_back(right); +} + +Expression::Expression(Expression *expr1, Expression *expr2, Expression *expr3) + : first(expr1), second(expr2), third(expr3) +{ + children.push_back(expr1); + children.push_back(expr2); + children.push_back(expr3); +} + +Expression::~Expression() +{ + std::for_each(this->children.begin(), this->children.end(), del_fun()); } namespace /* anonymous*/ { @@ -313,89 +109,509 @@ namespace /* anonymous*/ { } -std::string Expression::toString() const +bool Expression::isListComprehension() const { - std::stringstream stream; + return false; +} - if (this->type == "*" || this->type == "/" || this->type == "%" || this->type == "+" || - this->type == "-" || this->type == "<" || this->type == "<=" || this->type == "==" || - this->type == "!=" || this->type == ">=" || this->type == ">" || - this->type == "&&" || this->type == "||") { - stream << "(" << *this->children[0] << " " << this->type << " " << *this->children[1] << ")"; - } - else if (this->type == "?:") { - stream << "(" << *this->children[0] << " ? " << *this->children[1] << " : " << *this->children[2] << ")"; - } - else if (this->type == "[]") { - stream << *this->children[0] << "[" << *this->children[1] << "]"; - } - else if (this->type == "I") { - stream << "-" << *this->children[0]; - } - else if (this->type == "!") { - stream << "!" << *this->children[0]; - } - else if (this->type == "C") { - stream << this->const_value; - } - else if (this->type == "R") { - stream << "[" << *this->children[0] << " : " << *this->children[1]; - if (this->children.size() > 2) { - stream << " : " << *this->children[2]; - } - stream << "]"; - } - else if (this->type == "V") { - stream << "["; - for (size_t i=0; i < this->children.size(); i++) { - if (i > 0) stream << ", "; - stream << *this->children[i]; - } - stream << "]"; - } - else if (this->type == "L") { - stream << this->var_name; - } - else if (this->type == "N") { - stream << *this->children[0] << "." << this->var_name; - } - else if (this->type == "F") { - stream << this->call_funcname << "(" << this->call_arguments << ")"; - } - else if (this->type == "l") { - stream << "let(" << this->call_arguments << ") " << *this->children[0]; - } - else if (this->type == "i") { // list comprehension expression - Expression const* c = this->children[0]; +ExpressionNot::ExpressionNot(Expression *expr) : Expression(expr) +{ +} - stream << "["; +ValuePtr ExpressionNot::evaluate(const Context *context) const +{ + return !first->evaluate(context); +} - do { - if (c->call_funcname == "for") { - stream << "for(" << c->call_arguments << ") "; - c = c->children[0]; - } else if (c->call_funcname == "if") { - stream << "if(" << *c->children[0] << ") "; - c = c->children[1]; - } else if (c->call_funcname == "let") { - stream << "let(" << c->call_arguments << ") "; - c = c->children[0]; +void ExpressionNot::print(std::ostream &stream) const +{ + stream << "!" << *first; +} + +ExpressionLogicalAnd::ExpressionLogicalAnd(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionLogicalAnd::evaluate(const Context *context) const +{ + return this->first->evaluate(context) && this->second->evaluate(context); +} + +void ExpressionLogicalAnd::print(std::ostream &stream) const +{ + stream << "(" << *first << " && " << *second << ")"; +} + +ExpressionLogicalOr::ExpressionLogicalOr(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionLogicalOr::evaluate(const Context *context) const +{ + return this->first->evaluate(context) || this->second->evaluate(context); +} + +void ExpressionLogicalOr::print(std::ostream &stream) const +{ + stream << "(" << *first << " || " << *second << ")"; +} + +ExpressionMultiply::ExpressionMultiply(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionMultiply::evaluate(const Context *context) const +{ + return this->first->evaluate(context) * this->second->evaluate(context); +} + +void ExpressionMultiply::print(std::ostream &stream) const +{ + stream << "(" << *first << " * " << *second << ")"; +} + +ExpressionDivision::ExpressionDivision(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionDivision::evaluate(const Context *context) const +{ + return this->first->evaluate(context) / this->second->evaluate(context); +} + +void ExpressionDivision::print(std::ostream &stream) const +{ + stream << "(" << *first << " / " << *second << ")"; +} + +ExpressionModulo::ExpressionModulo(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionModulo::evaluate(const Context *context) const +{ + return this->first->evaluate(context) % this->second->evaluate(context); +} + +void ExpressionModulo::print(std::ostream &stream) const +{ + stream << "(" << *first << " % " << *second << ")"; +} + +ExpressionPlus::ExpressionPlus(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionPlus::evaluate(const Context *context) const +{ + return this->first->evaluate(context) + this->second->evaluate(context); +} + +void ExpressionPlus::print(std::ostream &stream) const +{ + stream << "(" << *first << " + " << *second << ")"; +} + +ExpressionMinus::ExpressionMinus(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionMinus::evaluate(const Context *context) const +{ + return this->first->evaluate(context) - this->second->evaluate(context); +} + +void ExpressionMinus::print(std::ostream &stream) const +{ + stream << "(" << *first << " - " << *second << ")"; +} + +ExpressionLess::ExpressionLess(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionLess::evaluate(const Context *context) const +{ + return this->first->evaluate(context) < this->second->evaluate(context); +} + +void ExpressionLess::print(std::ostream &stream) const +{ + stream << "(" << *first << " < " << *second << ")"; +} + +ExpressionLessOrEqual::ExpressionLessOrEqual(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionLessOrEqual::evaluate(const Context *context) const +{ + return this->first->evaluate(context) <= this->second->evaluate(context); +} + +void ExpressionLessOrEqual::print(std::ostream &stream) const +{ + stream << "(" << *first << " <= " << *second << ")"; +} + +ExpressionEqual::ExpressionEqual(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionEqual::evaluate(const Context *context) const +{ + return this->first->evaluate(context) == this->second->evaluate(context); +} + +void ExpressionEqual::print(std::ostream &stream) const +{ + stream << "(" << *first << " == " << *second << ")"; +} + +ExpressionNotEqual::ExpressionNotEqual(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionNotEqual::evaluate(const Context *context) const +{ + return this->first->evaluate(context) != this->second->evaluate(context); +} + +void ExpressionNotEqual::print(std::ostream &stream) const +{ + stream << "(" << *first << " != " << *second << ")"; +} + +ExpressionGreaterOrEqual::ExpressionGreaterOrEqual(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionGreaterOrEqual::evaluate(const Context *context) const +{ + return this->first->evaluate(context) >= this->second->evaluate(context); +} + +void ExpressionGreaterOrEqual::print(std::ostream &stream) const +{ + stream << "(" << *first << " >= " << *second << ")"; +} + +ExpressionGreater::ExpressionGreater(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionGreater::evaluate(const Context *context) const +{ + return this->first->evaluate(context) > this->second->evaluate(context); +} + +void ExpressionGreater::print(std::ostream &stream) const +{ + stream << "(" << *first << " > " << *second << ")"; +} + +ExpressionTernary::ExpressionTernary(Expression *expr1, Expression *expr2, Expression *expr3) : Expression(expr1, expr2, expr3) +{ +} + +ValuePtr ExpressionTernary::evaluate(const Context *context) const +{ + return (this->first->evaluate(context) ? this->second : this->third)->evaluate(context); +} + +void ExpressionTernary::print(std::ostream &stream) const +{ + stream << "(" << *first << " ? " << *second << " : " << *third << ")"; +} + +ExpressionArrayLookup::ExpressionArrayLookup(Expression *left, Expression *right) : Expression(left, right) +{ +} + +ValuePtr ExpressionArrayLookup::evaluate(const Context *context) const { + return this->first->evaluate(context)[this->second->evaluate(context)]; +} + +void ExpressionArrayLookup::print(std::ostream &stream) const +{ + stream << *first << "[" << *second << "]"; +} + +ExpressionInvert::ExpressionInvert(Expression *expr) : Expression(expr) +{ +} + +ValuePtr ExpressionInvert::evaluate(const Context *context) const +{ + return -this->first->evaluate(context); +} + +void ExpressionInvert::print(std::ostream &stream) const +{ + stream << "-" << *first; +} + +ExpressionConst::ExpressionConst(const ValuePtr &val) : const_value(val) +{ +} + +ValuePtr ExpressionConst::evaluate(const class Context *) const +{ + return ValuePtr(this->const_value); +} + +void ExpressionConst::print(std::ostream &stream) const +{ + stream << *this->const_value; +} + +ExpressionRange::ExpressionRange(Expression *expr1, Expression *expr2) : Expression(expr1, expr2) +{ +} + +ExpressionRange::ExpressionRange(Expression *expr1, Expression *expr2, Expression *expr3) : Expression(expr1, expr2, expr3) +{ +} + +ValuePtr ExpressionRange::evaluate(const Context *context) const +{ + ValuePtr v1 = this->first->evaluate(context); + if (v1->type() == Value::NUMBER) { + ValuePtr v2 = this->second->evaluate(context); + if (v2->type() == Value::NUMBER) { + if (this->children.size() == 2) { + Value::RangeType range(v1->toDouble(), v2->toDouble()); + return ValuePtr(range); } else { - assert(false && "Illegal list comprehension element"); + ValuePtr v3 = this->third->evaluate(context); + if (v3->type() == Value::NUMBER) { + Value::RangeType range(v1->toDouble(), v2->toDouble(), v3->toDouble()); + return ValuePtr(range); + } } - } while (c->type == "c"); - - stream << *c << "]"; - } - else { - assert(false && "Illegal expression type"); + } } + return ValuePtr::undefined; +} - return stream.str(); +void ExpressionRange::print(std::ostream &stream) const +{ + stream << "[" << *first << " : " << *second; + if (this->children.size() > 2) stream << " : " << *third; + stream << "]"; +} + +ExpressionVector::ExpressionVector(Expression *expr) : Expression(expr) +{ +} + +ValuePtr ExpressionVector::evaluate(const Context *context) const +{ + Value::VectorType vec; + BOOST_FOREACH(const Expression *e, this->children) { + vec.push_back(*(e->evaluate(context))); + } + return ValuePtr(vec); +} + +void ExpressionVector::print(std::ostream &stream) const +{ + stream << "["; + for (size_t i=0; i < this->children.size(); i++) { + if (i > 0) stream << ", "; + stream << *this->children[i]; + } + stream << "]"; +} + +ExpressionLookup::ExpressionLookup(const std::string &var_name) : var_name(var_name) +{ +} + +ValuePtr ExpressionLookup::evaluate(const Context *context) const +{ + return context->lookup_variable(this->var_name); +} + +void ExpressionLookup::print(std::ostream &stream) const +{ + stream << this->var_name; +} + +ExpressionMember::ExpressionMember(Expression *expr, const std::string &member) + : Expression(expr), member(member) +{ +} + +ValuePtr ExpressionMember::evaluate(const Context *context) const +{ + ValuePtr v = this->first->evaluate(context); + + if (v->type() == Value::VECTOR) { + if (this->member == "x") return v[0]; + if (this->member == "y") return v[1]; + if (this->member == "z") return v[2]; + } else if (v->type() == Value::RANGE) { + if (this->member == "begin") return v[0]; + if (this->member == "step") return v[1]; + if (this->member == "end") return v[2]; + } + return ValuePtr::undefined; +} + +void ExpressionMember::print(std::ostream &stream) const +{ + stream << *first << "." << this->member; +} + +ExpressionFunctionCall::ExpressionFunctionCall(const std::string &funcname, + const AssignmentList &arglist) + : funcname(funcname), call_arguments(arglist) +{ +} + +ValuePtr ExpressionFunctionCall::evaluate(const Context *context) const +{ + if (StackCheck::inst()->check()) { + throw RecursionException("function", funcname); + } + + EvalContext c(context, this->call_arguments); + ValuePtr result = context->evaluate_function(this->funcname, &c); + + return result; +} + +void ExpressionFunctionCall::print(std::ostream &stream) const +{ + stream << this->funcname << "(" << this->call_arguments << ")"; +} + +ExpressionLet::ExpressionLet(const AssignmentList &arglist, Expression *expr) + : Expression(expr), call_arguments(arglist) +{ +} + +ValuePtr ExpressionLet::evaluate(const Context *context) const +{ + Context c(context); + evaluate_sequential_assignment(this->call_arguments, &c); + + return this->first->evaluate(&c); +} + +void ExpressionLet::print(std::ostream &stream) const +{ + stream << "let(" << this->call_arguments << ") " << *first; +} + +ExpressionLcExpression::ExpressionLcExpression(Expression *expr) : Expression(expr) +{ +} + +ValuePtr ExpressionLcExpression::evaluate(const Context *context) const +{ + return this->first->evaluate(context); +} + +void ExpressionLcExpression::print(std::ostream &stream) const +{ + stream << "[" << *this->first << "]"; +} + +ExpressionLc::ExpressionLc(const std::string &name, + const AssignmentList &arglist, Expression *expr) + : Expression(expr), name(name), call_arguments(arglist) +{ +} + +ExpressionLc::ExpressionLc(const std::string &name, + Expression *expr1, Expression *expr2) + : Expression(expr1, expr2), name(name) +{ +} + +bool ExpressionLc::isListComprehension() const +{ + return true; +} + +ValuePtr ExpressionLc::evaluate(const Context *context) const +{ + Value::VectorType vec; + + if (this->name == "if") { + if (this->first->evaluate(context)) { + if (this->second->isListComprehension()) { + return this->second->evaluate(context); + } else { + vec.push_back((*this->second->evaluate(context))); + } + } + return ValuePtr(vec); + } else if (this->name == "for") { + EvalContext for_context(context, this->call_arguments); + + Context assign_context(context); + + // comprehension for statements are by the parser reduced to only contain one single element + const std::string &it_name = for_context.getArgName(0); + ValuePtr it_values = for_context.getArgValue(0, &assign_context); + + Context c(context); + + if (it_values->type() == Value::RANGE) { + Value::RangeType range = it_values->toRange(); + boost::uint32_t steps = range.nbsteps(); + if (steps >= 1000000) { + PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps); + } else { + for (Value::RangeType::iterator it = range.begin();it != range.end();it++) { + c.set_variable(it_name, ValuePtr(*it)); + vec.push_back((*this->first->evaluate(&c))); + } + } + } + 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]); + vec.push_back((*this->first->evaluate(&c))); + } + } + else if (it_values->type() != Value::UNDEFINED) { + c.set_variable(it_name, it_values); + vec.push_back((*this->first->evaluate(&c))); + } + if (this->first->isListComprehension()) { + return ValuePtr(flatten(vec)); + } else { + return ValuePtr(vec); + } + } else if (this->name == "let") { + Context c(context); + evaluate_sequential_assignment(this->call_arguments, &c); + + return this->first->evaluate(&c); + } else { + abort(); + } +} + +void ExpressionLc::print(std::ostream &stream) const +{ + stream << this->name; + if (this->name == "if") { + stream << "(" << *this->first << ") " << *this->second; + } + else if (this->name == "for" || this->name == "let") { + stream << "(" << this->call_arguments << ") " << *this->first; + } else { + assert(false && "Illegal list comprehension element"); + } } std::ostream &operator<<(std::ostream &stream, const Expression &expr) { - stream << expr.toString(); + expr.print(stream); return stream; } diff --git a/src/expression.h b/src/expression.h index b17496e5..2cd1bded 100644 --- a/src/expression.h +++ b/src/expression.h @@ -9,51 +9,247 @@ class Expression { public: std::vector children; - - const Value const_value; - std::string var_name; - - std::string call_funcname; - AssignmentList call_arguments; - - // Boolean: ! && || - // Operators: * / % + - - // Relations: < <= == != >= > - // Vector element: [] - // Condition operator: ?: - // Invert (prefix '-'): I - // Constant value: C - // Create Range: R - // Create Vector: V - // Create Matrix: M - // Lookup Variable: L - // Lookup member per name: N - // Function call: F - // Let expression: l - // List comprehension expression: i - // List comprehension: c - std::string type; + Expression *first; + Expression *second; + Expression *third; Expression(); - Expression(const Value &val); - Expression(const std::string &type, Expression *left, Expression *right); - Expression(const std::string &type, Expression *expr); - ~Expression(); + Expression(Expression *expr); + Expression(Expression *left, Expression *right); + Expression(Expression *expr1, Expression *expr2, Expression *expr3); + virtual ~Expression(); - Value evaluate(const class Context *context) const; - - std::string toString() const; - -private: - mutable int recursioncount; - - // The following sub_* methods are needed to minimize stack usage only. - Value sub_evaluate_function(const class Context *context) const; - Value sub_evaluate_member(const class Context *context) const; - Value sub_evaluate_range(const class Context *context) const; - Value sub_evaluate_vector(const class Context *context) const; - Value sub_evaluate_let_expression(const class Context *context) const; - Value sub_evaluate_list_comprehension(const class Context *context) const; + virtual bool isListComprehension() const; + virtual ValuePtr evaluate(const class Context *context) const = 0; + virtual void print(std::ostream &stream) const = 0; }; std::ostream &operator<<(std::ostream &stream, const Expression &expr); + +class ExpressionNot : public Expression +{ +public: + ExpressionNot(Expression *expr); + virtual ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionLogicalAnd : public Expression +{ +public: + ExpressionLogicalAnd(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionLogicalOr : public Expression +{ +public: + ExpressionLogicalOr(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionMultiply : public Expression +{ +public: + ExpressionMultiply(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionDivision : public Expression +{ +public: + ExpressionDivision(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionModulo : public Expression +{ +public: + ExpressionModulo(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionPlus : public Expression +{ +public: + ExpressionPlus(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionMinus : public Expression +{ +public: + ExpressionMinus(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionLess : public Expression +{ +public: + ExpressionLess(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionLessOrEqual : public Expression +{ +public: + ExpressionLessOrEqual(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionEqual : public Expression +{ +public: + ExpressionEqual(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionNotEqual : public Expression +{ +public: + ExpressionNotEqual(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionGreaterOrEqual : public Expression +{ +public: + ExpressionGreaterOrEqual(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionGreater : public Expression +{ +public: + ExpressionGreater(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionTernary : public Expression +{ +public: + ExpressionTernary(Expression *expr1, Expression *expr2, Expression *expr3); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionArrayLookup : public Expression +{ +public: + ExpressionArrayLookup(Expression *left, Expression *right); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +private: +}; + +class ExpressionInvert : public Expression +{ +public: + ExpressionInvert(Expression *expr); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionConst : public Expression +{ +public: + ExpressionConst(const ValuePtr &val); + ValuePtr evaluate(const class Context *) const; + virtual void print(std::ostream &stream) const; +private: + ValuePtr const_value; +}; + +class ExpressionRange : public Expression +{ +public: + ExpressionRange(Expression *expr1, Expression *expr2); + ExpressionRange(Expression *expr1, Expression *expr2, Expression *expr3); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionVector : public Expression +{ +public: + ExpressionVector(Expression *expr); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionLookup : public Expression +{ +public: + ExpressionLookup(const std::string &var_name); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +private: + std::string var_name; +}; + +class ExpressionMember : public Expression +{ +public: + ExpressionMember(Expression *expr, const std::string &member); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +private: + std::string member; +}; + +class ExpressionFunctionCall : public Expression +{ +public: + ExpressionFunctionCall(const std::string &funcname, const AssignmentList &arglist); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +public: + std::string funcname; + AssignmentList call_arguments; +}; + +class ExpressionLet : public Expression +{ +public: + ExpressionLet(const AssignmentList &arglist, Expression *expr); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +private: + AssignmentList call_arguments; +}; + +class ExpressionLcExpression : public Expression +{ +public: + ExpressionLcExpression(Expression *expr); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +}; + +class ExpressionLc : public Expression +{ + virtual bool isListComprehension() const; +public: + ExpressionLc(const std::string &name, + const AssignmentList &arglist, Expression *expr); + ExpressionLc(const std::string &name, + Expression *expr1, Expression *expr2); + ValuePtr evaluate(const class Context *context) const; + virtual void print(std::ostream &stream) const; +private: + std::string name; + AssignmentList call_arguments; +}; diff --git a/src/func.cc b/src/func.cc index f26f44c5..c7e26193 100644 --- a/src/func.cc +++ b/src/func.cc @@ -35,6 +35,7 @@ #include #include "stl-utils.h" #include "printutils.h" +#include "stackcheck.h" #include #include @@ -69,10 +70,11 @@ AbstractFunction::~AbstractFunction() { } -Value AbstractFunction::evaluate(const Context*, const EvalContext *evalctx) const +// FIXME: Is this needed? +ValuePtr AbstractFunction::evaluate(const Context*, const EvalContext *evalctx) const { (void)evalctx; // unusued parameter - return Value(); + return ValuePtr::undefined; } std::string AbstractFunction::dump(const std::string &indent, const std::string &name) const @@ -82,17 +84,24 @@ std::string AbstractFunction::dump(const std::string &indent, const std::string return dump.str(); } +Function::Function(const char *name, AssignmentList &definition_arguments, Expression *expr) + : name(name), definition_arguments(definition_arguments), expr(expr) +{ +} + Function::~Function() { delete expr; } -Value Function::evaluate(const Context *ctx, const EvalContext *evalctx) const +ValuePtr Function::evaluate(const Context *ctx, const EvalContext *evalctx) const { - if (!expr) return Value(); + if (!expr) return ValuePtr::undefined; Context c(ctx); c.setVariables(definition_arguments, evalctx); - return expr->evaluate(&c); + ValuePtr result = expr->evaluate(&c); + + return result; } std::string Function::dump(const std::string &indent, const std::string &name) const @@ -109,11 +118,71 @@ std::string Function::dump(const std::string &indent, const std::string &name) c return dump.str(); } +class FunctionTailRecursion : public Function +{ +private: + bool invert; + ExpressionFunctionCall *call; // memory owned by the main expression + Expression *endexpr; // memory owned by the main expression + +public: + FunctionTailRecursion(const char *name, AssignmentList &definition_arguments, Expression *expr, ExpressionFunctionCall *call, Expression *endexpr, bool invert); + virtual ~FunctionTailRecursion(); + + virtual ValuePtr evaluate(const Context *ctx, const EvalContext *evalctx) const; +}; + +FunctionTailRecursion::FunctionTailRecursion(const char *name, AssignmentList &definition_arguments, Expression *expr, ExpressionFunctionCall *call, Expression *endexpr, bool invert) + : Function(name, definition_arguments, expr), invert(invert), call(call), endexpr(endexpr) +{ +} + +FunctionTailRecursion::~FunctionTailRecursion() +{ +} + +ValuePtr FunctionTailRecursion::evaluate(const Context *ctx, const EvalContext *evalctx) const +{ + if (!expr) return ValuePtr::undefined; + + Context c(ctx); + c.setVariables(definition_arguments, evalctx); + + EvalContext ec(&c, call->call_arguments); + Context tmp(&c); + while (invert ^ expr->first->evaluate(&c)) { + tmp.setVariables(definition_arguments, &ec); + c.apply_variables(tmp); + } + + ValuePtr result = endexpr->evaluate(&c); + + return result; +} + +Function * Function::create(const char *name, AssignmentList &definition_arguments, Expression *expr) +{ + if (dynamic_cast(expr)) { + ExpressionFunctionCall *f1 = dynamic_cast(expr->second); + ExpressionFunctionCall *f2 = dynamic_cast(expr->third); + if (f1 && !f2) { + if (name == f1->funcname) { + return new FunctionTailRecursion(name, definition_arguments, expr, f1, expr->third, false); + } + } else if (f2 && !f1) { + if (name == f2->funcname) { + return new FunctionTailRecursion(name, definition_arguments, expr, f2, expr->second, true); + } + } + } + return new Function(name, definition_arguments, expr); +} + BuiltinFunction::~BuiltinFunction() { } -Value BuiltinFunction::evaluate(const Context *ctx, const EvalContext *evalctx) const +ValuePtr BuiltinFunction::evaluate(const Context *ctx, const EvalContext *evalctx) const { return eval_func(ctx, evalctx); } @@ -135,51 +204,51 @@ static inline double rad2deg(double x) return x * 180.0 / M_PI; } -Value builtin_abs(const Context *, const EvalContext *evalctx) +ValuePtr builtin_abs(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(fabs(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(fabs(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_sign(const Context *, const EvalContext *evalctx) +ValuePtr builtin_sign(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) { - register double x = v.toDouble(); - return Value((x<0) ? -1.0 : ((x>0) ? 1.0 : 0.0)); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) { + register double x = v->toDouble(); + return ValuePtr((x<0) ? -1.0 : ((x>0) ? 1.0 : 0.0)); } } - return Value(); + return ValuePtr::undefined; } -Value builtin_rands(const Context *, const EvalContext *evalctx) +ValuePtr builtin_rands(const Context *, const EvalContext *evalctx) { size_t n = evalctx->numArgs(); if (n == 3 || n == 4) { - const Value &v0 = evalctx->getArgValue(0); - if (v0.type() != Value::NUMBER) goto quit; - double min = v0.toDouble(); + ValuePtr v0 = evalctx->getArgValue(0); + if (v0->type() != Value::NUMBER) goto quit; + double min = v0->toDouble(); - const Value &v1 = evalctx->getArgValue(1); - if (v1.type() != Value::NUMBER) goto quit; - double max = v1.toDouble(); + ValuePtr v1 = evalctx->getArgValue(1); + if (v1->type() != Value::NUMBER) goto quit; + double max = v1->toDouble(); if (max < min) { register double tmp = min; min = max; max = tmp; } - const Value &v2 = evalctx->getArgValue(2); - if (v2.type() != Value::NUMBER) goto quit; - size_t numresults = std::max( 0, static_cast( v2.toDouble() ) ); + ValuePtr v2 = evalctx->getArgValue(2); + if (v2->type() != Value::NUMBER) goto quit; + size_t numresults = std::max(0, static_cast(v2->toDouble())); bool deterministic = false; if (n > 3) { - const Value &v3 = evalctx->getArgValue(3); - if (v3.type() != Value::NUMBER) goto quit; - deterministic_rng.seed( (unsigned int) v3.toDouble() ); + ValuePtr v3 = evalctx->getArgValue(3); + if (v3->type() != Value::NUMBER) goto quit; + deterministic_rng.seed((unsigned int) v3->toDouble()); deterministic = true; } boost::uniform_real<> distributor( min, max ); @@ -190,83 +259,81 @@ Value builtin_rands(const Context *, const EvalContext *evalctx) } else { for (size_t i=0; i < numresults; i++) { if ( deterministic ) { - vec.push_back( Value( distributor( deterministic_rng ) ) ); + vec.push_back(Value(distributor(deterministic_rng))); } else { - vec.push_back( Value( distributor( lessdeterministic_rng ) ) ); + vec.push_back(Value(distributor(lessdeterministic_rng))); } } } - return Value(vec); + return ValuePtr(vec); } quit: - return Value(); + return ValuePtr::undefined; } -Value builtin_min(const Context *, const EvalContext *evalctx) +ValuePtr builtin_min(const Context *, const EvalContext *evalctx) { // preserve special handling of the first argument // as a template for vector processing size_t n = evalctx->numArgs(); if (n >= 1) { - const Value &v0 = evalctx->getArgValue(0); + ValuePtr v0 = evalctx->getArgValue(0); - if (n == 1 && v0.type() == Value::VECTOR && !v0.toVector().empty()) { - Value min = v0.toVector()[0]; - for (size_t i = 1; i < v0.toVector().size(); i++) { - if (v0.toVector()[i] < min) - min = v0.toVector()[i]; + if (n == 1 && v0->type() == Value::VECTOR && !v0->toVector().empty()) { + Value min = v0->toVector()[0]; + for (size_t i = 1; i < v0->toVector().size(); i++) { + if (v0->toVector()[i] < min) min = v0->toVector()[i]; } - return min; + return ValuePtr(min); } - if (v0.type() == Value::NUMBER) { - double val = v0.toDouble(); + if (v0->type() == Value::NUMBER) { + double val = v0->toDouble(); for (size_t i = 1; i < n; ++i) { - const Value &v = evalctx->getArgValue(i); + ValuePtr v = evalctx->getArgValue(i); // 4/20/14 semantic change per discussion: // break on any non-number - if (v.type() != Value::NUMBER) goto quit; - register double x = v.toDouble(); + if (v->type() != Value::NUMBER) goto quit; + register double x = v->toDouble(); if (x < val) val = x; } - return Value(val); + return ValuePtr(val); } } quit: - return Value(); + return ValuePtr::undefined; } -Value builtin_max(const Context *, const EvalContext *evalctx) +ValuePtr builtin_max(const Context *, const EvalContext *evalctx) { // preserve special handling of the first argument // as a template for vector processing size_t n = evalctx->numArgs(); if (n >= 1) { - const Value &v0 = evalctx->getArgValue(0); + ValuePtr v0 = evalctx->getArgValue(0); - if (n == 1 && v0.type() == Value::VECTOR && !v0.toVector().empty()) { - Value max = v0.toVector()[0]; - for (size_t i = 1; i < v0.toVector().size(); i++) { - if (v0.toVector()[i] > max) - max = v0.toVector()[i]; + if (n == 1 && v0->type() == Value::VECTOR && !v0->toVector().empty()) { + Value max = v0->toVector()[0]; + for (size_t i = 1; i < v0->toVector().size(); i++) { + if (v0->toVector()[i] > max) max = v0->toVector()[i]; } - return max; + return ValuePtr(max); } - if (v0.type() == Value::NUMBER) { - double val = v0.toDouble(); + if (v0->type() == Value::NUMBER) { + double val = v0->toDouble(); for (size_t i = 1; i < n; ++i) { - const Value &v = evalctx->getArgValue(i); + ValuePtr v = evalctx->getArgValue(i); // 4/20/14 semantic change per discussion: // break on any non-number - if (v.type() != Value::NUMBER) goto quit; - register double x = v.toDouble(); + if (v->type() != Value::NUMBER) goto quit; + register double x = v->toDouble(); if (x > val) val = x; } - return Value(val); + return ValuePtr(val); } } quit: - return Value(); + return ValuePtr::undefined; } // this limit assumes 26+26=52 bits mantissa @@ -307,14 +374,14 @@ double sin_degrees(register double x) return oppose ? -x : x; } -Value builtin_sin(const Context *, const EvalContext *evalctx) +ValuePtr builtin_sin(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(sin_degrees(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(sin_degrees(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } double cos_degrees(register double x) @@ -354,220 +421,221 @@ double cos_degrees(register double x) return oppose ? -x : x; } -Value builtin_cos(const Context *, const EvalContext *evalctx) +ValuePtr builtin_cos(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(cos_degrees(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(cos_degrees(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_asin(const Context *, const EvalContext *evalctx) +ValuePtr builtin_asin(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(rad2deg(asin(v.toDouble()))); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(rad2deg(asin(v->toDouble()))); } - return Value(); + return ValuePtr::undefined; } -Value builtin_acos(const Context *, const EvalContext *evalctx) +ValuePtr builtin_acos(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(rad2deg(acos(v.toDouble()))); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(rad2deg(acos(v->toDouble()))); } - return Value(); + return ValuePtr::undefined; } -Value builtin_tan(const Context *, const EvalContext *evalctx) +ValuePtr builtin_tan(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(tan(deg2rad(v.toDouble()))); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(tan(deg2rad(v->toDouble()))); } - return Value(); + return ValuePtr::undefined; } -Value builtin_atan(const Context *, const EvalContext *evalctx) +ValuePtr builtin_atan(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(rad2deg(atan(v.toDouble()))); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(rad2deg(atan(v->toDouble()))); } - return Value(); + return ValuePtr::undefined; } -Value builtin_atan2(const Context *, const EvalContext *evalctx) +ValuePtr builtin_atan2(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 2) { - Value v0 = evalctx->getArgValue(0), v1 = evalctx->getArgValue(1); - if (v0.type() == Value::NUMBER && v1.type() == Value::NUMBER) - return Value(rad2deg(atan2(v0.toDouble(), v1.toDouble()))); + ValuePtr v0 = evalctx->getArgValue(0), v1 = evalctx->getArgValue(1); + if (v0->type() == Value::NUMBER && v1->type() == Value::NUMBER) + return ValuePtr(rad2deg(atan2(v0->toDouble(), v1->toDouble()))); } - return Value(); + return ValuePtr::undefined; } -Value builtin_pow(const Context *, const EvalContext *evalctx) +ValuePtr builtin_pow(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 2) { - Value v0 = evalctx->getArgValue(0), v1 = evalctx->getArgValue(1); - if (v0.type() == Value::NUMBER && v1.type() == Value::NUMBER) - return Value(pow(v0.toDouble(), v1.toDouble())); + ValuePtr v0 = evalctx->getArgValue(0), v1 = evalctx->getArgValue(1); + if (v0->type() == Value::NUMBER && v1->type() == Value::NUMBER) + return ValuePtr(pow(v0->toDouble(), v1->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_round(const Context *, const EvalContext *evalctx) +ValuePtr builtin_round(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(round(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(round(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_ceil(const Context *, const EvalContext *evalctx) +ValuePtr builtin_ceil(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(ceil(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(ceil(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_floor(const Context *, const EvalContext *evalctx) +ValuePtr builtin_floor(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(floor(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(floor(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_sqrt(const Context *, const EvalContext *evalctx) +ValuePtr builtin_sqrt(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(sqrt(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(sqrt(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_exp(const Context *, const EvalContext *evalctx) +ValuePtr builtin_exp(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(exp(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(exp(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_length(const Context *, const EvalContext *evalctx) +ValuePtr builtin_length(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::VECTOR) return Value(int(v.toVector().size())); - if (v.type() == Value::STRING) { + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::VECTOR) return ValuePtr(int(v->toVector().size())); + if (v->type() == Value::STRING) { //Unicode glyph count for the length -- rather than the string (num. of bytes) length. - std::string text = v.toString(); - return Value(int( g_utf8_strlen( text.c_str(), text.size() ) )); + std::string text = v->toString(); + return ValuePtr(int( g_utf8_strlen( text.c_str(), text.size() ) )); } } - return Value(); + return ValuePtr::undefined; } -Value builtin_log(const Context *, const EvalContext *evalctx) +ValuePtr builtin_log(const Context *, const EvalContext *evalctx) { size_t n = evalctx->numArgs(); if (n == 1 || n == 2) { - const Value &v0 = evalctx->getArgValue(0); - if (v0.type() == Value::NUMBER) { - double x = 10.0, y = v0.toDouble(); + ValuePtr v0 = evalctx->getArgValue(0); + if (v0->type() == Value::NUMBER) { + double x = 10.0, y = v0->toDouble(); if (n > 1) { - const Value &v1 = evalctx->getArgValue(1); - if (v1.type() != Value::NUMBER) goto quit; - x = y; y = v1.toDouble(); + ValuePtr v1 = evalctx->getArgValue(1); + if (v1->type() != Value::NUMBER) goto quit; + x = y; y = v1->toDouble(); } - return Value(log(y) / log(x)); + return ValuePtr(log(y) / log(x)); } } quit: - return Value(); + return ValuePtr::undefined; } -Value builtin_ln(const Context *, const EvalContext *evalctx) +ValuePtr builtin_ln(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() == Value::NUMBER) - return Value(log(v.toDouble())); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() == Value::NUMBER) + return ValuePtr(log(v->toDouble())); } - return Value(); + return ValuePtr::undefined; } -Value builtin_str(const Context *, const EvalContext *evalctx) +ValuePtr builtin_str(const Context *, const EvalContext *evalctx) { std::stringstream stream; for (size_t i = 0; i < evalctx->numArgs(); i++) { - stream << evalctx->getArgValue(i).toString(); + stream << evalctx->getArgValue(i)->toString(); } - return Value(stream.str()); + return ValuePtr(stream.str()); } -Value builtin_chr(const Context *, const EvalContext *evalctx) +ValuePtr builtin_chr(const Context *, const EvalContext *evalctx) { std::stringstream stream; for (size_t i = 0; i < evalctx->numArgs(); i++) { - const Value v = evalctx->getArgValue(i); - stream << v.chrString(); + ValuePtr v = evalctx->getArgValue(i); + stream << v->chrString(); } - return Value(stream.str()); + return ValuePtr(stream.str()); } -Value builtin_concat(const Context *, const EvalContext *evalctx) +ValuePtr builtin_concat(const Context *, const EvalContext *evalctx) { Value::VectorType result; for (size_t i = 0; i < evalctx->numArgs(); i++) { - const Value v = evalctx->getArgValue(i); - if (v.type() == Value::VECTOR) { - Value::VectorType vec = v.toVector(); + ValuePtr v = evalctx->getArgValue(i); + if (v->type() == Value::VECTOR) { + Value::VectorType vec = v->toVector(); for (Value::VectorType::const_iterator it = vec.begin(); it != vec.end(); it++) { result.push_back(*it); } } else { - result.push_back(v); + result.push_back(*v); } } - return Value(result); + return ValuePtr(result); } -Value builtin_lookup(const Context *, const EvalContext *evalctx) +ValuePtr builtin_lookup(const Context *, const EvalContext *evalctx) { double p, low_p, low_v, high_p, high_v; if (evalctx->numArgs() < 2 || // Needs two args - !evalctx->getArgValue(0).getDouble(p)) // First must be a number - return Value(); + !evalctx->getArgValue(0)->getDouble(p)) // First must be a number + return ValuePtr::undefined; - const Value::VectorType vec = evalctx->getArgValue(1).toVector(); + ValuePtr v1 = evalctx->getArgValue(1); + const Value::VectorType &vec = v1->toVector(); if (vec[0].toVector().size() < 2) // Second must be a vector of vectors - return Value(); + return ValuePtr::undefined; if (!vec[0].getVec2(low_p, low_v) || !vec[0].getVec2(high_p, high_v)) - return Value(); + return ValuePtr::undefined; for (size_t i = 1; i < vec.size(); i++) { double this_p, this_v; if (vec[i].getVec2(this_p, this_v)) { @@ -582,11 +650,11 @@ Value builtin_lookup(const Context *, const EvalContext *evalctx) } } if (p <= low_p) - return Value(high_v); + return ValuePtr(high_v); if (p >= high_p) - return Value(low_v); + return ValuePtr(low_v); double f = (p-low_p) / (high_p-low_p); - return Value(high_v * f + low_v * (1-f)); + return ValuePtr(high_v * f + low_v * (1-f)); } /* @@ -715,48 +783,48 @@ static Value::VectorType search(const std::string &find, const Value::VectorType return returnvec; } -Value builtin_search(const Context *, const EvalContext *evalctx) +ValuePtr builtin_search(const Context *, const EvalContext *evalctx) { - if (evalctx->numArgs() < 2) return Value(); + if (evalctx->numArgs() < 2) return ValuePtr::undefined; - const Value &findThis = evalctx->getArgValue(0); - const Value &searchTable = evalctx->getArgValue(1); - unsigned int num_returns_per_match = (evalctx->numArgs() > 2) ? evalctx->getArgValue(2).toDouble() : 1; - unsigned int index_col_num = (evalctx->numArgs() > 3) ? evalctx->getArgValue(3).toDouble() : 0; + ValuePtr findThis = evalctx->getArgValue(0); + ValuePtr searchTable = evalctx->getArgValue(1); + unsigned int num_returns_per_match = (evalctx->numArgs() > 2) ? evalctx->getArgValue(2)->toDouble() : 1; + unsigned int index_col_num = (evalctx->numArgs() > 3) ? evalctx->getArgValue(3)->toDouble() : 0; Value::VectorType returnvec; - if (findThis.type() == Value::NUMBER) { + if (findThis->type() == Value::NUMBER) { unsigned int matchCount = 0; - for (size_t j = 0; j < searchTable.toVector().size(); j++) { - const Value& search_element = searchTable.toVector()[j]; + for (size_t j = 0; j < searchTable->toVector().size(); j++) { + const Value &search_element = searchTable->toVector()[j]; - if ((index_col_num == 0 && findThis == search_element) || + if ((index_col_num == 0 && *findThis == search_element) || (index_col_num < search_element.toVector().size() && - findThis == search_element.toVector()[index_col_num])) { + *findThis == search_element.toVector()[index_col_num])) { returnvec.push_back(Value(double(j))); matchCount++; if (num_returns_per_match != 0 && matchCount >= num_returns_per_match) break; } } - } else if (findThis.type() == Value::STRING) { - if (searchTable.type() == Value::STRING) { - returnvec = search(findThis.toString(), searchTable.toString(), num_returns_per_match, index_col_num); + } else if (findThis->type() == Value::STRING) { + if (searchTable->type() == Value::STRING) { + returnvec = search(findThis->toString(), searchTable->toString(), num_returns_per_match, index_col_num); } else { - returnvec = search(findThis.toString(), searchTable.toVector(), num_returns_per_match, index_col_num); + returnvec = search(findThis->toString(), searchTable->toVector(), num_returns_per_match, index_col_num); } - } else if (findThis.type() == Value::VECTOR) { - for (size_t i = 0; i < findThis.toVector().size(); i++) { + } else if (findThis->type() == Value::VECTOR) { + for (size_t i = 0; i < findThis->toVector().size(); i++) { unsigned int matchCount = 0; Value::VectorType resultvec; - Value const& find_value = findThis.toVector()[i]; + Value const& find_value = findThis->toVector()[i]; - for (size_t j = 0; j < searchTable.toVector().size(); j++) { + for (size_t j = 0; j < searchTable->toVector().size(); j++) { - Value const& search_element = searchTable.toVector()[j]; + Value const& search_element = searchTable->toVector()[j]; if ((index_col_num == 0 && find_value == search_element) || (index_col_num < search_element.toVector().size() && @@ -773,53 +841,53 @@ Value builtin_search(const Context *, const EvalContext *evalctx) } } if (num_returns_per_match == 1 && matchCount == 0) { - if (findThis.toVector()[i].type() == Value::NUMBER) { - PRINTB(" WARNING: search term not found: %s",findThis.toVector()[i].toDouble()); + if (findThis->toVector()[i].type() == Value::NUMBER) { + PRINTB(" WARNING: search term not found: %s",findThis->toVector()[i].toDouble()); } - else if (findThis.toVector()[i].type() == Value::STRING) { - PRINTB(" WARNING: search term not found: \"%s\"",findThis.toVector()[i].toString()); + else if (findThis->toVector()[i].type() == Value::STRING) { + PRINTB(" WARNING: search term not found: \"%s\"",findThis->toVector()[i].toString()); } - returnvec.push_back(Value(resultvec)); + returnvec.push_back(resultvec); } if (num_returns_per_match == 0 || num_returns_per_match > 1) { - returnvec.push_back(Value(resultvec)); + returnvec.push_back(resultvec); } } } else { PRINTB(" WARNING: search: none performed on input %s", findThis); - return Value(); + return ValuePtr::undefined; } - return Value(returnvec); + return ValuePtr(returnvec); } #define QUOTE(x__) # x__ #define QUOTED(x__) QUOTE(x__) -Value builtin_version(const Context *, const EvalContext *evalctx) +ValuePtr builtin_version(const Context *, const EvalContext *evalctx) { (void)evalctx; // unusued parameter Value::VectorType val; - val.push_back(Value(double(OPENSCAD_YEAR))); - val.push_back(Value(double(OPENSCAD_MONTH))); + val.push_back(double(OPENSCAD_YEAR)); + val.push_back(double(OPENSCAD_MONTH)); #ifdef OPENSCAD_DAY - val.push_back(Value(double(OPENSCAD_DAY))); + val.push_back(double(OPENSCAD_DAY)); #endif - return Value(val); + return ValuePtr(val); } -Value builtin_version_num(const Context *ctx, const EvalContext *evalctx) +ValuePtr builtin_version_num(const Context *ctx, const EvalContext *evalctx) { - Value val = (evalctx->numArgs() == 0) ? builtin_version(ctx, evalctx) : evalctx->getArgValue(0); + ValuePtr val = (evalctx->numArgs() == 0) ? builtin_version(ctx, evalctx) : evalctx->getArgValue(0); double y, m, d = 0; - if (!val.getVec3(y, m, d)) { - if (!val.getVec2(y, m)) { - return Value(); + if (!val->getVec3(y, m, d)) { + if (!val->getVec2(y, m)) { + return ValuePtr::undefined; } } - return Value(y * 10000 + m * 100 + d); + return ValuePtr(y * 10000 + m * 100 + d); } -Value builtin_parent_module(const Context *, const EvalContext *evalctx) +ValuePtr builtin_parent_module(const Context *, const EvalContext *evalctx) { int n; double d; @@ -827,30 +895,30 @@ Value builtin_parent_module(const Context *, const EvalContext *evalctx) if (evalctx->numArgs() == 0) d=1; // parent module else if (evalctx->numArgs() == 1) { - const Value &v = evalctx->getArgValue(0); - if (v.type() != Value::NUMBER) return Value(); - v.getDouble(d); + ValuePtr v = evalctx->getArgValue(0); + if (v->type() != Value::NUMBER) return ValuePtr::undefined; + v->getDouble(d); } else - return Value(); + return ValuePtr::undefined; n=trunc(d); if (n < 0) { PRINTB("WARNING: Negative parent module index (%d) not allowed", n); - return Value(); + return ValuePtr::undefined; } if (n >= s) { PRINTB("WARNING: Parent module index (%d) greater than the number of modules on the stack", n); - return Value(); + return ValuePtr::undefined; } - return Value(Module::stack_element(s - 1 - n)); + return ValuePtr(Module::stack_element(s - 1 - n)); } -Value builtin_norm(const Context *, const EvalContext *evalctx) +ValuePtr builtin_norm(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() == 1) { - const Value &val = evalctx->getArgValue(0); - if (val.type() == Value::VECTOR) { + ValuePtr val = evalctx->getArgValue(0); + if (val->type() == Value::VECTOR) { double sum = 0; - Value::VectorType v = val.toVector(); + Value::VectorType v = val->toVector(); size_t n = v.size(); for (size_t i = 0; i < n; i++) if (v[i].type() == Value::NUMBER) { @@ -859,48 +927,48 @@ Value builtin_norm(const Context *, const EvalContext *evalctx) sum += x*x; } else { PRINT(" WARNING: Incorrect arguments to norm()"); - return Value(); + return ValuePtr::undefined; } - return Value(sqrt(sum)); + return ValuePtr(sqrt(sum)); } } - return Value(); + return ValuePtr::undefined; } -Value builtin_cross(const Context *, const EvalContext *evalctx) +ValuePtr builtin_cross(const Context *, const EvalContext *evalctx) { if (evalctx->numArgs() != 2) { PRINT("WARNING: Invalid number of parameters for cross()"); - return Value(); + return ValuePtr::undefined; } - Value arg0 = evalctx->getArgValue(0); - Value arg1 = evalctx->getArgValue(1); - if ((arg0.type() != Value::VECTOR) || (arg1.type() != Value::VECTOR)) { + ValuePtr arg0 = evalctx->getArgValue(0); + ValuePtr arg1 = evalctx->getArgValue(1); + if ((arg0->type() != Value::VECTOR) || (arg1->type() != Value::VECTOR)) { PRINT("WARNING: Invalid type of parameters for cross()"); - return Value(); + return ValuePtr::undefined; } - Value::VectorType v0 = arg0.toVector(); - Value::VectorType v1 = arg1.toVector(); + Value::VectorType v0 = arg0->toVector(); + Value::VectorType v1 = arg1->toVector(); if ((v0.size() != 3) || (v1.size() != 3)) { PRINT("WARNING: Invalid vector size of parameter for cross()"); - return Value(); + return ValuePtr::undefined; } for (unsigned int a = 0;a < 3;a++) { if ((v0[a].type() != Value::NUMBER) || (v1[a].type() != Value::NUMBER)) { PRINT("WARNING: Invalid value in parameter vector for cross()"); - return Value(); + return ValuePtr::undefined; } double d0 = v0[a].toDouble(); double d1 = v1[a].toDouble(); if (boost::math::isnan(d0) || boost::math::isnan(d1)) { PRINT("WARNING: Invalid value (NaN) in parameter vector for cross()"); - return Value(); + return ValuePtr::undefined; } if (boost::math::isinf(d0) || boost::math::isinf(d1)) { PRINT("WARNING: Invalid value (INF) in parameter vector for cross()"); - return Value(); + return ValuePtr::undefined; } } @@ -912,7 +980,7 @@ Value builtin_cross(const Context *, const EvalContext *evalctx) result.push_back(Value(x)); result.push_back(Value(y)); result.push_back(Value(z)); - return Value(result); + return ValuePtr(result); } void register_builtin_functions() diff --git a/src/function.h b/src/function.h index d8e3ad77..c9449c74 100644 --- a/src/function.h +++ b/src/function.h @@ -7,45 +7,47 @@ #include #include - class AbstractFunction { private: - const Feature *feature; + const Feature *feature; public: - AbstractFunction() : feature(NULL) {} - AbstractFunction(const Feature& feature) : feature(&feature) {} + AbstractFunction() : feature(NULL) {} + AbstractFunction(const Feature& feature) : feature(&feature) {} virtual ~AbstractFunction(); - virtual bool is_experimental() const { return feature != NULL; } - virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); } - virtual Value evaluate(const class Context *ctx, const class EvalContext *evalctx) const; + virtual bool is_experimental() const { return feature != NULL; } + virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); } + virtual ValuePtr 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 EvalContext *evalctx); + typedef ValuePtr (*eval_func_t)(const Context *ctx, const EvalContext *evalctx); eval_func_t eval_func; BuiltinFunction(eval_func_t f) : eval_func(f) { } BuiltinFunction(eval_func_t f, const Feature& feature) : AbstractFunction(feature), eval_func(f) { } virtual ~BuiltinFunction(); - virtual Value evaluate(const Context *ctx, const EvalContext *evalctx) const; + virtual ValuePtr 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::string name; AssignmentList definition_arguments; Expression *expr; - Function() { } + Function(const char *name, AssignmentList &definition_arguments, Expression *expr); virtual ~Function(); - virtual Value evaluate(const Context *ctx, const EvalContext *evalctx) const; + virtual ValuePtr evaluate(const Context *ctx, const EvalContext *evalctx) const; virtual std::string dump(const std::string &indent, const std::string &name) const; + + static Function * create(const char *name, AssignmentList &definition_arguments, Expression *expr); }; diff --git a/src/import.cc b/src/import.cc index ad667592..ddc4a950 100644 --- a/src/import.cc +++ b/src/import.cc @@ -91,18 +91,18 @@ AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstanti c.dump(this, inst); #endif - Value v = c.lookup_variable("file"); - if (v.isUndefined()) { + ValuePtr v = c.lookup_variable("file"); + if (v->isUndefined()) { v = c.lookup_variable("filename"); - if (!v.isUndefined()) { + if (!v->isUndefined()) { printDeprecation("DEPRECATED: filename= is deprecated. Please use file="); } } - std::string filename = lookup_file(v.isUndefined() ? "" : v.toString(), inst->path(), ctx->documentPath()); + std::string filename = lookup_file(v->isUndefined() ? "" : v->toString(), inst->path(), ctx->documentPath()); import_type_e actualtype = this->type; if (actualtype == TYPE_UNKNOWN) { - std::string extraw = boosty::extension_str( fs::path(filename) ); - std::string ext = boost::algorithm::to_lower_copy( extraw ); + std::string extraw = boosty::extension_str(fs::path(filename)); + std::string ext = boost::algorithm::to_lower_copy(extraw); if (ext == ".stl") actualtype = TYPE_STL; else if (ext == ".off") actualtype = TYPE_OFF; else if (ext == ".dxf") actualtype = TYPE_DXF; @@ -110,31 +110,30 @@ AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstanti ImportNode *node = new ImportNode(inst, actualtype); - node->fn = c.lookup_variable("$fn").toDouble(); - node->fs = c.lookup_variable("$fs").toDouble(); - node->fa = c.lookup_variable("$fa").toDouble(); + node->fn = c.lookup_variable("$fn")->toDouble(); + node->fs = c.lookup_variable("$fs")->toDouble(); + node->fa = c.lookup_variable("$fa")->toDouble(); node->filename = filename; - Value layerval = c.lookup_variable("layer", true); + Value layerval = *c.lookup_variable("layer", true); if (layerval.isUndefined()) { - layerval = c.lookup_variable("layername"); + layerval = *c.lookup_variable("layername"); if (!layerval.isUndefined()) { printDeprecation("DEPRECATED: layername= is deprecated. Please use layer="); } } node->layername = layerval.isUndefined() ? "" : layerval.toString(); - node->convexity = c.lookup_variable("convexity", true).toDouble(); + node->convexity = c.lookup_variable("convexity", true)->toDouble(); if (node->convexity <= 0) node->convexity = 1; - Value origin = c.lookup_variable("origin", true); + ValuePtr origin = c.lookup_variable("origin", true); node->origin_x = node->origin_y = 0; - origin.getVec2(node->origin_x, node->origin_y); + origin->getVec2(node->origin_x, node->origin_y); - node->scale = c.lookup_variable("scale", true).toDouble(); + node->scale = c.lookup_variable("scale", true)->toDouble(); - if (node->scale <= 0) - node->scale = 1; + if (node->scale <= 0) node->scale = 1; return node; } diff --git a/src/linearextrude.cc b/src/linearextrude.cc index 1e191f6b..e535c28d 100644 --- a/src/linearextrude.cc +++ b/src/linearextrude.cc @@ -60,46 +60,46 @@ AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleI c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - node->fn = c.lookup_variable("$fn").toDouble(); - node->fs = c.lookup_variable("$fs").toDouble(); - node->fa = c.lookup_variable("$fa").toDouble(); + node->fn = c.lookup_variable("$fn")->toDouble(); + node->fs = c.lookup_variable("$fs")->toDouble(); + node->fa = c.lookup_variable("$fa")->toDouble(); - Value file = c.lookup_variable("file"); - Value layer = c.lookup_variable("layer", true); - Value height = c.lookup_variable("height", true); - Value convexity = c.lookup_variable("convexity", true); - Value origin = c.lookup_variable("origin", true); - Value scale = c.lookup_variable("scale", true); - Value center = c.lookup_variable("center", true); - Value twist = c.lookup_variable("twist", true); - Value slices = c.lookup_variable("slices", true); + ValuePtr file = c.lookup_variable("file"); + ValuePtr layer = c.lookup_variable("layer", true); + ValuePtr height = c.lookup_variable("height", true); + ValuePtr convexity = c.lookup_variable("convexity", true); + ValuePtr origin = c.lookup_variable("origin", true); + ValuePtr scale = c.lookup_variable("scale", true); + ValuePtr center = c.lookup_variable("center", true); + ValuePtr twist = c.lookup_variable("twist", true); + ValuePtr slices = c.lookup_variable("slices", true); - if (!file.isUndefined() && file.type() == Value::STRING) { + if (!file->isUndefined() && file->type() == Value::STRING) { printDeprecation("DEPRECATED: Support for reading files in linear_extrude will be removed in future releases. Use a child import() instead."); - node->filename = lookup_file(file.toString(), inst->path(), c.documentPath()); + node->filename = lookup_file(file->toString(), inst->path(), c.documentPath()); } // if height not given, and first argument is a number, // then assume it should be the height. - if (c.lookup_variable("height").isUndefined() && + if (c.lookup_variable("height")->isUndefined() && evalctx->numArgs() > 0 && evalctx->getArgName(0) == "") { - const Value &val = evalctx->getArgValue(0); - if (val.type() == Value::NUMBER) height = val; + ValuePtr val = evalctx->getArgValue(0); + if (val->type() == Value::NUMBER) height = val; } - node->layername = layer.isUndefined() ? "" : layer.toString(); + node->layername = layer->isUndefined() ? "" : layer->toString(); node->height = 100; - height.getDouble(node->height); - node->convexity = (int)convexity.toDouble(); - origin.getVec2(node->origin_x, node->origin_y); + height->getDouble(node->height); + node->convexity = (int)convexity->toDouble(); + origin->getVec2(node->origin_x, node->origin_y); node->scale_x = node->scale_y = 1; - scale.getDouble(node->scale_x); - scale.getDouble(node->scale_y); - scale.getVec2(node->scale_x, node->scale_y); + scale->getDouble(node->scale_x); + scale->getDouble(node->scale_y); + scale->getVec2(node->scale_x, node->scale_y); - if (center.type() == Value::BOOL) - node->center = center.toBool(); + if (center->type() == Value::BOOL) + node->center = center->toBool(); if (node->height <= 0) node->height = 0; @@ -109,10 +109,10 @@ AbstractNode *LinearExtrudeModule::instantiate(const Context *ctx, const ModuleI if (node->scale_x < 0) node->scale_x = 0; if (node->scale_y < 0) node->scale_y = 0; - if (slices.type() == Value::NUMBER) node->slices = (int)slices.toDouble(); + if (slices->type() == Value::NUMBER) node->slices = (int)slices->toDouble(); - if (twist.type() == Value::NUMBER) { - node->twist = twist.toDouble(); + if (twist->type() == Value::NUMBER) { + node->twist = twist->toDouble(); if (node->twist != 0.0) { if (node->slices == 0) { node->slices = (int)fmax(2, fabs(Calc::get_fragments_from_r(node->height, diff --git a/src/mainwin.cc b/src/mainwin.cc index 483f431c..92d64a16 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -1486,7 +1486,7 @@ bool MainWindow::eventFilter(QObject* obj, QEvent *event) void MainWindow::updateTemporalVariables() { - this->top_ctx.set_variable("$t", Value(this->e_tval->text().toDouble())); + this->top_ctx.set_variable("$t", ValuePtr(this->e_tval->text().toDouble())); Value::VectorType vpt; vpt.push_back(Value(-qglview->cam.object_trans.x())); @@ -1498,9 +1498,9 @@ void MainWindow::updateTemporalVariables() vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.x() + 90, 360))); vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.y(), 360))); vpr.push_back(Value(fmodf(360 - qglview->cam.object_rot.z(), 360))); - top_ctx.set_variable("$vpr", Value(vpr)); + top_ctx.set_variable("$vpr", ValuePtr(vpr)); - top_ctx.set_variable("$vpd", Value(qglview->cam.viewer_distance)); + top_ctx.set_variable("$vpd", ValuePtr(qglview->cam.viewer_distance)); } @@ -1527,25 +1527,25 @@ void MainWindow::updateCamera() double d = cam.viewer_distance; double x, y, z; - const Value vpr = root_module->lookup_variable("$vpr"); - if (vpr.getVec3(x, y, z)) { + const ValuePtr vpr = root_module->lookup_variable("$vpr"); + if (vpr->getVec3(x, y, z)) { rx = x; ry = y; rz = z; camera_set = true; } - const Value vpt = root_module->lookup_variable("$vpt"); - if (vpt.getVec3(x, y, z)) { + const ValuePtr vpt = root_module->lookup_variable("$vpt"); + if (vpt->getVec3(x, y, z)) { tx = x; ty = y; tz = z; camera_set = true; } - const Value vpd = root_module->lookup_variable("$vpd"); - if (vpd.type() == Value::NUMBER) { - d = vpd.toDouble(); + const ValuePtr vpd = root_module->lookup_variable("$vpd"); + if (vpd->type() == Value::NUMBER) { + d = vpd->toDouble(); camera_set = true; } diff --git a/src/memory.h b/src/memory.h index 1f54e4b3..86cd6aba 100644 --- a/src/memory.h +++ b/src/memory.h @@ -1,6 +1,8 @@ #pragma once #include +#include using boost::shared_ptr; +using boost::make_shared; using boost::dynamic_pointer_cast; using boost::static_pointer_cast; diff --git a/src/modcontext.cc b/src/modcontext.cc index 3dc711a6..04d46e60 100644 --- a/src/modcontext.cc +++ b/src/modcontext.cc @@ -24,8 +24,8 @@ void ModuleContext::evaluateAssignments(const AssignmentList &assignments) // First, assign all simple variables std::list undefined_vars; BOOST_FOREACH(const Assignment &ass, assignments) { - Value tmpval = ass.second->evaluate(this); - if (tmpval.isUndefined()) undefined_vars.push_back(ass.first); + ValuePtr tmpval = ass.second->evaluate(this); + if (tmpval->isUndefined()) undefined_vars.push_back(ass.first); else this->set_variable(ass.first, tmpval); } @@ -46,12 +46,12 @@ void ModuleContext::evaluateAssignments(const AssignmentList &assignments) boost::unordered_map::iterator found = tmpass.find(*curr); if (found != tmpass.end()) { const Expression *expr = found->second; - Value tmpval = expr->evaluate(this); + ValuePtr tmpval = expr->evaluate(this); // FIXME: it's not enough to check for undefined; // we need to check for any undefined variable in the subexpression // For now, ignore this and revisit the validity and order of variable // assignments later - if (!tmpval.isUndefined()) { + if (!tmpval->isUndefined()) { changed = true; this->set_variable(*curr, tmpval); undefined_vars.erase(curr); @@ -90,7 +90,7 @@ void ModuleContext::registerBuiltin() this->set_variable(ass.first, ass.second->evaluate(this)); } - this->set_constant("PI",Value(M_PI)); + this->set_constant("PI", ValuePtr(M_PI)); } const AbstractFunction *ModuleContext::findLocalFunction(const std::string &name) const @@ -123,7 +123,8 @@ const AbstractModule *ModuleContext::findLocalModule(const std::string &name) co return NULL; } -Value ModuleContext::evaluate_function(const std::string &name, const EvalContext *evalctx) const +ValuePtr ModuleContext::evaluate_function(const std::string &name, + const EvalContext *evalctx) const { const AbstractFunction *foundf = findLocalFunction(name); if (foundf) return foundf->evaluate(this, evalctx); @@ -157,7 +158,7 @@ std::string ModuleContext::dump(const AbstractModule *mod, const ModuleInstantia } } } - typedef std::pair ValueMapType; + typedef std::pair ValueMapType; s << " vars:"; BOOST_FOREACH(const ValueMapType &v, constants) { s << boost::format(" %s = %s") % v.first % v.second; @@ -178,9 +179,9 @@ FileContext::FileContext(const class FileModule &module, const Context *parent) if (!module.modulePath().empty()) this->document_path = module.modulePath(); } -Value FileContext::sub_evaluate_function(const std::string &name, const EvalContext *evalctx, - - FileModule *usedmod) const +ValuePtr FileContext::sub_evaluate_function(const std::string &name, + const EvalContext *evalctx, + FileModule *usedmod) const { FileContext ctx(*usedmod, this->parent); @@ -193,7 +194,8 @@ Value FileContext::sub_evaluate_function(const std::string &name, const EvalCont return usedmod->scope.functions[name]->evaluate(&ctx, evalctx); } -Value FileContext::evaluate_function(const std::string &name, const EvalContext *evalctx) const +ValuePtr FileContext::evaluate_function(const std::string &name, + const EvalContext *evalctx) const { const AbstractFunction *foundf = findLocalFunction(name); if (foundf) return foundf->evaluate(this, evalctx); diff --git a/src/modcontext.h b/src/modcontext.h index 1a850314..400c4142 100644 --- a/src/modcontext.h +++ b/src/modcontext.h @@ -18,8 +18,8 @@ public: void initializeModule(const Module &m); void registerBuiltin(); - virtual Value evaluate_function(const std::string &name, - const EvalContext *evalctx) const; + virtual ValuePtr evaluate_function(const std::string &name, + const EvalContext *evalctx) const; virtual AbstractNode *instantiate_module(const ModuleInstantiation &inst, EvalContext *evalctx) const; @@ -45,7 +45,8 @@ class FileContext : public ModuleContext public: FileContext(const class FileModule &module, const Context *parent); virtual ~FileContext() {} - virtual Value evaluate_function(const std::string &name, const EvalContext *evalctx) const; + virtual ValuePtr evaluate_function(const std::string &name, + const EvalContext *evalctx) const; virtual AbstractNode *instantiate_module(const ModuleInstantiation &inst, EvalContext *evalctx) const; @@ -53,5 +54,7 @@ private: const FileModule::ModuleContainer &usedlibs; // This sub_* method is needed to minimize stack usage only. - Value sub_evaluate_function(const std::string &name, const EvalContext *evalctx, FileModule *usedmod) const; + ValuePtr sub_evaluate_function(const std::string &name, + const EvalContext *evalctx, + FileModule *usedmod) const; }; diff --git a/src/module.cc b/src/module.cc index 2feafab5..8cf6498e 100644 --- a/src/module.cc +++ b/src/module.cc @@ -33,6 +33,8 @@ #include "function.h" #include "printutils.h" #include "parsersettings.h" +#include "exceptions.h" +#include "stackcheck.h" #include namespace fs = boost::filesystem; @@ -59,14 +61,14 @@ AbstractNode *AbstractModule::instantiate(const Context *ctx, const ModuleInstan double AbstractModule::lookup_double_variable_with_default(Context &c, std::string variable, double def) const { - const Value v = c.lookup_variable(variable, true); - return (v.type() == Value::NUMBER) ? v.toDouble() : def; + ValuePtr v = c.lookup_variable(variable, true); + return (v->type() == Value::NUMBER) ? v->toDouble() : def; } std::string AbstractModule::lookup_string_variable_with_default(Context &c, std::string variable, std::string def) const { - const Value v = c.lookup_variable(variable, true); - return (v.type() == Value::STRING) ? v.toString() : def; + ValuePtr v = c.lookup_variable(variable, true); + return (v->type() == Value::STRING) ? v->toString() : def; } std::string AbstractModule::dump(const std::string &indent, const std::string &name) const @@ -151,8 +153,13 @@ AbstractNode *ModuleInstantiation::evaluate(const Context *ctx) const PRINT("New eval ctx:"); c.dump(NULL, this); #endif - AbstractNode *node = ctx->instantiate_module(*this, &c); // Passes c as evalctx - return node; + try { + AbstractNode *node = ctx->instantiate_module(*this, &c); // Passes c as evalctx + return node; + } catch (RecursionException &e) { + PRINT(e.what()); + return NULL; + } } std::vector ModuleInstantiation::instantiateChildren(const Context *evalctx) const @@ -171,25 +178,10 @@ Module::~Module() { } -class ModRecursionGuard -{ -public: - ModRecursionGuard(const ModuleInstantiation &inst) : inst(inst) { - inst.recursioncount++; - } - ~ModRecursionGuard() { - inst.recursioncount--; - } - bool recursion_detected() const { return (inst.recursioncount > 1000); } -private: - const ModuleInstantiation &inst; -}; - AbstractNode *Module::instantiate(const Context *ctx, const ModuleInstantiation *inst, EvalContext *evalctx) const { - ModRecursionGuard g(*inst); - if (g.recursion_detected()) { - PRINTB("ERROR: Recursion detected calling module '%s'", inst->name()); + if (StackCheck::inst()->check()) { + throw RecursionException("module", inst->name()); return NULL; } @@ -199,9 +191,9 @@ AbstractNode *Module::instantiate(const Context *ctx, const ModuleInstantiation ModuleContext c(ctx, evalctx); // set $children first since we might have variables depending on it - c.set_variable("$children", Value(double(inst->scope.children.size()))); + c.set_variable("$children", ValuePtr(double(inst->scope.children.size()))); module_stack.push_back(inst->name()); - c.set_variable("$parent_modules", Value(double(module_stack.size()))); + c.set_variable("$parent_modules", ValuePtr(double(module_stack.size()))); c.initializeModule(*this); // FIXME: Set document path to the path of the module #if 0 && DEBUG @@ -365,17 +357,22 @@ AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiat #endif AbstractNode *node = new AbstractNode(inst); - std::vector instantiatednodes = this->scope.instantiateChildren(context); - node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); + try { + std::vector instantiatednodes = this->scope.instantiateChildren(context); + node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); + } + catch (RecursionException &e) { + PRINT(e.what()); + } return node; } -Value FileModule::lookup_variable(const std::string &name) const +ValuePtr FileModule::lookup_variable(const std::string &name) const { if (!context) { - return Value::undefined; + return ValuePtr::undefined; } return context->lookup_variable(name, true); -} \ No newline at end of file +} diff --git a/src/module.h b/src/module.h index 15a00628..504eb2a8 100644 --- a/src/module.h +++ b/src/module.h @@ -18,7 +18,7 @@ class ModuleInstantiation { public: ModuleInstantiation(const std::string &name = "") - : tag_root(false), tag_highlight(false), tag_background(false), recursioncount(0), modname(name) { } + : tag_root(false), tag_highlight(false), tag_background(false), modname(name) { } virtual ~ModuleInstantiation(); virtual std::string dump(const std::string &indent) const; @@ -40,7 +40,6 @@ public: bool tag_root; bool tag_highlight; bool tag_background; - mutable int recursioncount; protected: std::string modname; std::string modpath; @@ -112,7 +111,7 @@ public: bool hasIncludes() const { return !this->includes.empty(); } bool usesLibraries() const { return !this->usedlibs.empty(); } bool isHandlingDependencies() const { return this->is_handling_dependencies; } - Value lookup_variable(const std::string &name) const; + ValuePtr lookup_variable(const std::string &name) const; typedef boost::unordered_set ModuleContainer; ModuleContainer usedlibs; diff --git a/src/offset.cc b/src/offset.cc index faf3a38c..08705907 100644 --- a/src/offset.cc +++ b/src/offset.cc @@ -60,21 +60,21 @@ AbstractNode *OffsetModule::instantiate(const Context *ctx, const ModuleInstanti c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - node->fn = c.lookup_variable("$fn").toDouble(); - node->fs = c.lookup_variable("$fs").toDouble(); - node->fa = c.lookup_variable("$fa").toDouble(); + node->fn = c.lookup_variable("$fn")->toDouble(); + node->fs = c.lookup_variable("$fs")->toDouble(); + node->fa = c.lookup_variable("$fa")->toDouble(); - Value delta = c.lookup_variable("delta"); + ValuePtr delta = c.lookup_variable("delta"); node->delta = 1; - delta.getDouble(node->delta); + delta->getDouble(node->delta); - Value miter_limit = c.lookup_variable("miter_limit", true); + ValuePtr miter_limit = c.lookup_variable("miter_limit", true); node->miter_limit = 2; - miter_limit.getDouble(node->miter_limit); + miter_limit->getDouble(node->miter_limit); - Value join_type = c.lookup_variable("join_type", true); - if (join_type.type() == Value::STRING) { - std::string jt = join_type.toString(); + ValuePtr join_type = c.lookup_variable("join_type", true); + if (join_type->type() == Value::STRING) { + std::string jt = join_type->toString(); if (std::string("bevel") == jt) { node->join_type = ClipperLib::jtSquare; } else if (std::string("round") == jt) { @@ -85,7 +85,7 @@ AbstractNode *OffsetModule::instantiate(const Context *ctx, const ModuleInstanti PRINTB("WARNING: Unknown join_type for offset(): '%s'", jt); } - if ((node->join_type != ClipperLib::jtMiter) && !miter_limit.isUndefined()) { + if ((node->join_type != ClipperLib::jtMiter) && !miter_limit->isUndefined()) { PRINTB("WARNING: miter_limit is ignored in offset() for join_type: '%s'", jt); } } diff --git a/src/openscad.cc b/src/openscad.cc index 5431ed56..5080b814 100644 --- a/src/openscad.cc +++ b/src/openscad.cc @@ -39,6 +39,7 @@ #include "PlatformUtils.h" #include "LibraryInfo.h" #include "nodedumper.h" +#include "stackcheck.h" #include "CocoaUtils.h" #include @@ -684,6 +685,8 @@ int main(int argc, char **argv) { int rc = 0; bool isGuiLaunched = getenv("GUI_LAUNCHED") != 0; + StackCheck::inst()->init(); + #ifdef Q_OS_MAC if (isGuiLaunched) set_output_handler(CocoaUtils::nslog, NULL); #else diff --git a/src/parser.y b/src/parser.y index a7a1008b..73c07504 100644 --- a/src/parser.y +++ b/src/parser.y @@ -166,9 +166,7 @@ statement: } | TOK_FUNCTION TOK_ID '(' arguments_decl optional_commas ')' '=' expr { - Function *func = new Function(); - func->definition_arguments = *$4; - func->expr = $8; + Function *func = Function::create($2, *$4, $8); scope_stack.top()->functions[$2] = func; free($2); delete $4; @@ -302,70 +300,55 @@ single_module_instantiation: expr: TOK_TRUE { - $$ = new Expression(Value(true)); + $$ = new ExpressionConst(ValuePtr(true)); } | TOK_FALSE { - $$ = new Expression(Value(false)); + $$ = new ExpressionConst(ValuePtr(false)); } | TOK_UNDEF { - $$ = new Expression(Value::undefined); + $$ = new ExpressionConst(ValuePtr::undefined); } | TOK_ID { - $$ = new Expression(); - $$->type = "L"; - $$->var_name = $1; + $$ = new ExpressionLookup($1); free($1); } | expr '.' TOK_ID { - $$ = new Expression("N", $1); - $$->var_name = $3; + $$ = new ExpressionMember($1, $3); free($3); } | TOK_STRING { - $$ = new Expression(Value(std::string($1))); + $$ = new ExpressionConst(ValuePtr(std::string($1))); free($1); } | TOK_NUMBER { - $$ = new Expression(Value($1)); + $$ = new ExpressionConst(ValuePtr($1)); } | TOK_LET '(' arguments_call ')' expr %prec LET { - $$ = new Expression(); - $$->type = "l"; - $$->call_arguments = *$3; + $$ = new ExpressionLet(*$3, $5); delete $3; - $$->children.push_back($5); } | '[' expr ':' expr ']' { - $$ = new Expression(); - $$->type = "R"; - $$->children.push_back($2); - $$->children.push_back($4); + $$ = new ExpressionRange($2, $4); } | '[' expr ':' expr ':' expr ']' { - $$ = new Expression(); - $$->type = "R"; - $$->children.push_back($2); - $$->children.push_back($4); - $$->children.push_back($6); + $$ = new ExpressionRange($2, $4, $6); } | '[' list_comprehension_elements ']' { - $$ = new Expression(); - $$->type = "i"; - $$->children.push_back($2); + $$ = new ExpressionLcExpression($2); } | '[' optional_commas ']' { - $$ = new Expression(Value(Value::VectorType())); + $$ = new ExpressionConst(ValuePtr(Value::VectorType())); } | '[' vector_expr optional_commas ']' { @@ -373,55 +356,55 @@ expr: } | expr '*' expr { - $$ = new Expression("*", $1, $3); + $$ = new ExpressionMultiply($1, $3); } | expr '/' expr { - $$ = new Expression("/", $1, $3); + $$ = new ExpressionDivision($1, $3); } | expr '%' expr { - $$ = new Expression("%", $1, $3); + $$ = new ExpressionModulo($1, $3); } | expr '+' expr { - $$ = new Expression("+", $1, $3); + $$ = new ExpressionPlus($1, $3); } | expr '-' expr { - $$ = new Expression("-", $1, $3); + $$ = new ExpressionMinus($1, $3); } | expr '<' expr { - $$ = new Expression("<", $1, $3); + $$ = new ExpressionLess($1, $3); } | expr LE expr { - $$ = new Expression("<=", $1, $3); + $$ = new ExpressionLessOrEqual($1, $3); } | expr EQ expr { - $$ = new Expression("==", $1, $3); + $$ = new ExpressionEqual($1, $3); } | expr NE expr { - $$ = new Expression("!=", $1, $3); + $$ = new ExpressionNotEqual($1, $3); } | expr GE expr { - $$ = new Expression(">=", $1, $3); + $$ = new ExpressionGreaterOrEqual($1, $3); } | expr '>' expr { - $$ = new Expression(">", $1, $3); + $$ = new ExpressionGreater($1, $3); } | expr AND expr { - $$ = new Expression("&&", $1, $3); + $$ = new ExpressionLogicalAnd($1, $3); } | expr OR expr { - $$ = new Expression("||", $1, $3); + $$ = new ExpressionLogicalOr($1, $3); } | '+' expr { @@ -429,11 +412,11 @@ expr: } | '-' expr { - $$ = new Expression("I", $2); + $$ = new ExpressionInvert($2); } | '!' expr { - $$ = new Expression("!", $2); + $$ = new ExpressionNot($2); } | '(' expr ')' { @@ -441,22 +424,15 @@ expr: } | expr '?' expr ':' expr { - $$ = new Expression(); - $$->type = "?:"; - $$->children.push_back($1); - $$->children.push_back($3); - $$->children.push_back($5); + $$ = new ExpressionTernary($1, $3, $5); } | expr '[' expr ']' { - $$ = new Expression("[]", $1, $3); + $$ = new ExpressionArrayLookup($1, $3); } | TOK_ID '(' arguments_call ')' { - $$ = new Expression(); - $$->type = "F"; - $$->call_funcname = $1; - $$->call_arguments = *$3; + $$ = new ExpressionFunctionCall($1, *$3); free($1); delete $3; } @@ -467,9 +443,7 @@ list_comprehension_elements: be parsed as an expression) */ TOK_LET '(' arguments_call ')' list_comprehension_elements { - $$ = new Expression("c", $5); - $$->call_funcname = "let"; - $$->call_arguments = *$3; + $$ = new ExpressionLc("let", *$3, $5); delete $3; } | TOK_FOR '(' arguments_call ')' list_comprehension_elements_or_expr @@ -478,17 +452,16 @@ list_comprehension_elements: /* transform for(i=...,j=...) -> for(i=...) for(j=...) */ for (int i = $3->size()-1; i >= 0; i--) { - Expression *e = new Expression("c", $$); - e->call_funcname = "for"; - e->call_arguments.push_back((*$3)[i]); + AssignmentList arglist; + arglist.push_back((*$3)[i]); + Expression *e = new ExpressionLc("for", arglist, $$); $$ = e; } delete $3; } | TOK_IF '(' expr ')' list_comprehension_elements_or_expr { - $$ = new Expression("c", $3, $5); - $$->call_funcname = "if"; + $$ = new ExpressionLc("if", $3, $5); } ; @@ -505,7 +478,7 @@ optional_commas: vector_expr: expr { - $$ = new Expression("V", $1); + $$ = new ExpressionVector($1); } | vector_expr ',' optional_commas expr { diff --git a/src/primitives.cc b/src/primitives.cc index 936825fc..5381db8c 100644 --- a/src/primitives.cc +++ b/src/primitives.cc @@ -108,7 +108,7 @@ public: double fn, fs, fa; primitive_type_e type; int convexity; - Value points, paths, faces; + ValuePtr points, paths, faces; virtual Geometry *createGeometry() const; }; @@ -125,19 +125,19 @@ public: */ Value PrimitiveModule::lookup_radius(const Context &ctx, const std::string &diameter_var, const std::string &radius_var) const { - const Value d = ctx.lookup_variable(diameter_var, true); - const Value r = ctx.lookup_variable(radius_var, true); - const bool r_defined = (r.type() == Value::NUMBER); + ValuePtr d = ctx.lookup_variable(diameter_var, true); + ValuePtr r = ctx.lookup_variable(radius_var, true); + const bool r_defined = (r->type() == Value::NUMBER); - if (d.type() == Value::NUMBER) { + if (d->type() == Value::NUMBER) { if (r_defined) { PRINTB("WARNING: Ignoring radius variable '%s' as diameter '%s' is defined too.", radius_var % diameter_var); } - return Value(d.toDouble() / 2.0); + return Value(d->toDouble() / 2.0); } else if (r_defined) { - return r; + return *r; } else { - return Value(); + return Value::undefined; } } @@ -179,9 +179,9 @@ AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInsta Context c(ctx); c.setVariables(args, evalctx); - node->fn = c.lookup_variable("$fn").toDouble(); - node->fs = c.lookup_variable("$fs").toDouble(); - node->fa = c.lookup_variable("$fa").toDouble(); + node->fn = c.lookup_variable("$fn")->toDouble(); + node->fs = c.lookup_variable("$fs")->toDouble(); + node->fa = c.lookup_variable("$fa")->toDouble(); if (node->fs < F_MINIMUM) { PRINTB("WARNING: $fs too small - clamping to %f", F_MINIMUM); @@ -194,14 +194,14 @@ AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInsta switch (this->type) { case CUBE: { - Value size = c.lookup_variable("size"); - Value center = c.lookup_variable("center"); - size.getDouble(node->x); - size.getDouble(node->y); - size.getDouble(node->z); - size.getVec3(node->x, node->y, node->z); - if (center.type() == Value::BOOL) { - node->center = center.toBool(); + ValuePtr size = c.lookup_variable("size"); + ValuePtr center = c.lookup_variable("center"); + size->getDouble(node->x); + size->getDouble(node->y); + size->getDouble(node->z); + size->getVec3(node->x, node->y, node->z); + if (center->type() == Value::BOOL) { + node->center = center->toBool(); } break; } @@ -213,9 +213,9 @@ AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInsta break; } case CYLINDER: { - const Value h = c.lookup_variable("h"); - if (h.type() == Value::NUMBER) { - node->h = h.toDouble(); + ValuePtr h = c.lookup_variable("h"); + if (h->type() == Value::NUMBER) { + node->h = h->toDouble(); } const Value r = lookup_radius(c, "d", "r"); @@ -232,32 +232,32 @@ AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInsta node->r2 = r2.toDouble(); } - const Value center = c.lookup_variable("center"); - if (center.type() == Value::BOOL) { - node->center = center.toBool(); + ValuePtr center = c.lookup_variable("center"); + if (center->type() == Value::BOOL) { + node->center = center->toBool(); } break; } case POLYHEDRON: { node->points = c.lookup_variable("points"); node->faces = c.lookup_variable("faces"); - if (node->faces.type() == Value::UNDEFINED) { + if (node->faces->type() == Value::UNDEFINED) { // backwards compatible node->faces = c.lookup_variable("triangles", true); - if (node->faces.type() != Value::UNDEFINED) { + if (node->faces->type() != Value::UNDEFINED) { printDeprecation("DEPRECATED: polyhedron(triangles=[]) will be removed in future releases. Use polyhedron(faces=[]) instead."); } } break; } case SQUARE: { - Value size = c.lookup_variable("size"); - Value center = c.lookup_variable("center"); - size.getDouble(node->x); - size.getDouble(node->y); - size.getVec2(node->x, node->y); - if (center.type() == Value::BOOL) { - node->center = center.toBool(); + ValuePtr size = c.lookup_variable("size"); + ValuePtr center = c.lookup_variable("center"); + size->getDouble(node->x); + size->getDouble(node->y); + size->getVec2(node->x, node->y); + if (center->type() == Value::BOOL) { + node->center = center->toBool(); } break; } @@ -275,7 +275,7 @@ AbstractNode *PrimitiveModule::instantiate(const Context *ctx, const ModuleInsta } } - node->convexity = c.lookup_variable("convexity", true).toDouble(); + node->convexity = c.lookup_variable("convexity", true)->toDouble(); if (node->convexity < 1) node->convexity = 1; @@ -505,15 +505,15 @@ Geometry *PrimitiveNode::createGeometry() const PolySet *p = new PolySet(3); g = p; p->setConvexity(this->convexity); - for (size_t i=0; ifaces.toVector().size(); i++) + for (size_t i=0; ifaces->toVector().size(); i++) { p->append_poly(); - const Value::VectorType &vec = this->faces.toVector()[i].toVector(); + const Value::VectorType &vec = this->faces->toVector()[i].toVector(); for (size_t j=0; jpoints.toVector().size()) { + if (pt < this->points->toVector().size()) { double px, py, pz; - if (!this->points.toVector()[pt].getVec3(px, py, pz) || + if (!this->points->toVector()[pt].getVec3(px, py, pz) || isinf(px) || isinf(py) || isinf(pz)) { PRINTB("ERROR: Unable to convert point at index %d to a vec3 of numbers", j); return p; @@ -570,7 +570,7 @@ Geometry *PrimitiveNode::createGeometry() const Outline2d outline; double x,y; - const Value::VectorType &vec = this->points.toVector(); + const Value::VectorType &vec = this->points->toVector(); for (unsigned int i=0;ipaths.toVector().size() == 0 && outline.vertices.size() > 2) { + if (this->paths->toVector().size() == 0 && outline.vertices.size() > 2) { p->addOutline(outline); } else { - BOOST_FOREACH(const Value &polygon, this->paths.toVector()) { + BOOST_FOREACH(const Value &polygon, this->paths->toVector()) { Outline2d curroutline; BOOST_FOREACH(const Value &index, polygon.toVector()) { unsigned int idx = index.toDouble(); @@ -628,8 +628,8 @@ std::string PrimitiveNode::toString() const << ", r2 = " << this->r2 << ", center = " << (center ? "true" : "false") << ")"; break; case POLYHEDRON: - stream << "(points = " << this->points - << ", faces = " << this->faces + stream << "(points = " << *this->points + << ", faces = " << *this->faces << ", convexity = " << this->convexity << ")"; break; case SQUARE: @@ -641,7 +641,7 @@ std::string PrimitiveNode::toString() const << ", $fs = " << this->fs << ", r = " << this->r1 << ")"; break; case POLYGON: - stream << "(points = " << this->points << ", paths = " << this->paths << ", convexity = " << this->convexity << ")"; + stream << "(points = " << *this->points << ", paths = " << *this->paths << ", convexity = " << this->convexity << ")"; break; default: assert(false); diff --git a/src/projection.cc b/src/projection.cc index 57c1d1f9..8929c07a 100644 --- a/src/projection.cc +++ b/src/projection.cc @@ -55,13 +55,13 @@ AbstractNode *ProjectionModule::instantiate(const Context *ctx, const ModuleInst c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - Value convexity = c.lookup_variable("convexity", true); - Value cut = c.lookup_variable("cut", true); + ValuePtr convexity = c.lookup_variable("convexity", true); + ValuePtr cut = c.lookup_variable("cut", true); - node->convexity = (int)convexity.toDouble(); + node->convexity = (int)convexity->toDouble(); - if (cut.type() == Value::BOOL) - node->cut_mode = cut.toBool(); + if (cut->type() == Value::BOOL) + node->cut_mode = cut->toBool(); std::vector instantiatednodes = inst->instantiateChildren(evalctx); node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); diff --git a/src/render.cc b/src/render.cc index 4d109fbf..12c5a567 100644 --- a/src/render.cc +++ b/src/render.cc @@ -52,9 +52,9 @@ AbstractNode *RenderModule::instantiate(const Context *ctx, const ModuleInstanti c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - Value v = c.lookup_variable("convexity"); - if (v.type() == Value::NUMBER) - node->convexity = (int)v.toDouble(); + ValuePtr v = c.lookup_variable("convexity"); + if (v->type() == Value::NUMBER) + node->convexity = (int)v->toDouble(); std::vector instantiatednodes = inst->instantiateChildren(evalctx); node->children.insert(node->children.end(), instantiatednodes.begin(), instantiatednodes.end()); diff --git a/src/rotateextrude.cc b/src/rotateextrude.cc index b2e8d0eb..700fb7c9 100644 --- a/src/rotateextrude.cc +++ b/src/rotateextrude.cc @@ -58,25 +58,25 @@ AbstractNode *RotateExtrudeModule::instantiate(const Context *ctx, const ModuleI c.setVariables(args, evalctx); inst->scope.apply(*evalctx); - node->fn = c.lookup_variable("$fn").toDouble(); - node->fs = c.lookup_variable("$fs").toDouble(); - node->fa = c.lookup_variable("$fa").toDouble(); + node->fn = c.lookup_variable("$fn")->toDouble(); + node->fs = c.lookup_variable("$fs")->toDouble(); + node->fa = c.lookup_variable("$fa")->toDouble(); - Value file = c.lookup_variable("file"); - Value layer = c.lookup_variable("layer", true); - Value convexity = c.lookup_variable("convexity", true); - Value origin = c.lookup_variable("origin", true); - Value scale = c.lookup_variable("scale", true); + ValuePtr file = c.lookup_variable("file"); + ValuePtr layer = c.lookup_variable("layer", true); + ValuePtr convexity = c.lookup_variable("convexity", true); + ValuePtr origin = c.lookup_variable("origin", true); + ValuePtr scale = c.lookup_variable("scale", true); - if (!file.isUndefined()) { + if (!file->isUndefined()) { printDeprecation("DEPRECATED: Support for reading files in rotate_extrude will be removed in future releases. Use a child import() instead."); - node->filename = lookup_file(file.toString(), inst->path(), c.documentPath()); + node->filename = lookup_file(file->toString(), inst->path(), c.documentPath()); } - node->layername = layer.isUndefined() ? "" : layer.toString(); - node->convexity = (int)convexity.toDouble(); - origin.getVec2(node->origin_x, node->origin_y); - node->scale = scale.toDouble(); + node->layername = layer->isUndefined() ? "" : layer->toString(); + node->convexity = (int)convexity->toDouble(); + origin->getVec2(node->origin_x, node->origin_y); + node->scale = scale->toDouble(); if (node->convexity <= 0) node->convexity = 1; diff --git a/src/stackcheck.cc b/src/stackcheck.cc new file mode 100644 index 00000000..0f8ca2a8 --- /dev/null +++ b/src/stackcheck.cc @@ -0,0 +1,39 @@ +#include + +#include "stackcheck.h" +#include "PlatformUtils.h" + +StackCheck * StackCheck::self = 0; + +StackCheck::StackCheck() : ptr(0) +{ +} + +StackCheck::~StackCheck() +{ +} + +void StackCheck::init() +{ + unsigned char c; + ptr = &c; +} + +unsigned long StackCheck::size() +{ + unsigned char c; + return std::labs(ptr - &c); +} + +bool StackCheck::check() +{ + return size() >= PlatformUtils::stackLimit(); +} + +StackCheck * StackCheck::inst() +{ + if (self == 0) { + self = new StackCheck(); + } + return self; +} diff --git a/src/stackcheck.h b/src/stackcheck.h new file mode 100644 index 00000000..a2bfae22 --- /dev/null +++ b/src/stackcheck.h @@ -0,0 +1,19 @@ +#pragma once + +class StackCheck +{ +public: + StackCheck(); + virtual ~StackCheck(); + + static StackCheck * inst(); + + void init(); + bool check(); + unsigned long size(); + +private: + unsigned char * ptr; + + static StackCheck *self; +}; diff --git a/src/surface.cc b/src/surface.cc index 9f55d34a..264f9027 100644 --- a/src/surface.cc +++ b/src/surface.cc @@ -93,22 +93,22 @@ AbstractNode *SurfaceModule::instantiate(const Context *ctx, const ModuleInstant Context c(ctx); c.setVariables(args, evalctx); - Value fileval = c.lookup_variable("file"); - node->filename = lookup_file(fileval.isUndefined() ? "" : fileval.toString(), inst->path(), c.documentPath()); + ValuePtr fileval = c.lookup_variable("file"); + node->filename = lookup_file(fileval->isUndefined() ? "" : fileval->toString(), inst->path(), c.documentPath()); - Value center = c.lookup_variable("center", true); - if (center.type() == Value::BOOL) { - node->center = center.toBool(); + ValuePtr center = c.lookup_variable("center", true); + if (center->type() == Value::BOOL) { + node->center = center->toBool(); } - Value convexity = c.lookup_variable("convexity", true); - if (convexity.type() == Value::NUMBER) { - node->convexity = (int)convexity.toDouble(); + ValuePtr convexity = c.lookup_variable("convexity", true); + if (convexity->type() == Value::NUMBER) { + node->convexity = (int)convexity->toDouble(); } - Value invert = c.lookup_variable("invert", true); - if (invert.type() == Value::BOOL) { - node->invert = invert.toBool(); + ValuePtr invert = c.lookup_variable("invert", true); + if (invert->type() == Value::BOOL) { + node->invert = invert->toBool(); } return node; diff --git a/src/text.cc b/src/text.cc index 152e64bf..3b4f1a7c 100644 --- a/src/text.cc +++ b/src/text.cc @@ -54,9 +54,9 @@ AbstractNode *TextModule::instantiate(const Context *ctx, const ModuleInstantiat Context c(ctx); c.setVariables(args, evalctx); - double fn = c.lookup_variable("$fn").toDouble(); - double fa = c.lookup_variable("$fa").toDouble(); - double fs = c.lookup_variable("$fs").toDouble(); + double fn = c.lookup_variable("$fn")->toDouble(); + double fa = c.lookup_variable("$fa")->toDouble(); + double fs = c.lookup_variable("$fs")->toDouble(); node->params.set_fn(fn); node->params.set_fa(fa); diff --git a/src/transform.cc b/src/transform.cc index 7259cd67..c86f0301 100644 --- a/src/transform.cc +++ b/src/transform.cc @@ -88,45 +88,45 @@ AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInsta if (this->type == SCALE) { Vector3d scalevec(1,1,1); - Value v = c.lookup_variable("v"); - if (!v.getVec3(scalevec[0], scalevec[1], scalevec[2], 1.0)) { + ValuePtr v = c.lookup_variable("v"); + if (!v->getVec3(scalevec[0], scalevec[1], scalevec[2], 1.0)) { double num; - if (v.getDouble(num)) scalevec.setConstant(num); + if (v->getDouble(num)) scalevec.setConstant(num); } node->matrix.scale(scalevec); } else if (this->type == ROTATE) { - Value val_a = c.lookup_variable("a"); - if (val_a.type() == Value::VECTOR) + ValuePtr val_a = c.lookup_variable("a"); + if (val_a->type() == Value::VECTOR) { Eigen::AngleAxisd rotx(0, Vector3d::UnitX()); Eigen::AngleAxisd roty(0, Vector3d::UnitY()); Eigen::AngleAxisd rotz(0, Vector3d::UnitZ()); double a; - if (val_a.toVector().size() > 0) { - val_a.toVector()[0].getDouble(a); + if (val_a->toVector().size() > 0) { + val_a->toVector()[0].getDouble(a); rotx = Eigen::AngleAxisd(a*M_PI/180, Vector3d::UnitX()); } - if (val_a.toVector().size() > 1) { - val_a.toVector()[1].getDouble(a); + if (val_a->toVector().size() > 1) { + val_a->toVector()[1].getDouble(a); roty = Eigen::AngleAxisd(a*M_PI/180, Vector3d::UnitY()); } - if (val_a.toVector().size() > 2) { - val_a.toVector()[2].getDouble(a); + if (val_a->toVector().size() > 2) { + val_a->toVector()[2].getDouble(a); rotz = Eigen::AngleAxisd(a*M_PI/180, Vector3d::UnitZ()); } node->matrix.rotate(rotz * roty * rotx); } else { - Value val_v = c.lookup_variable("v"); + ValuePtr val_v = c.lookup_variable("v"); double a = 0; - val_a.getDouble(a); + val_a->getDouble(a); Vector3d axis(0,0,1); - if (val_v.getVec3(axis[0], axis[1], axis[2])) { + if (val_v->getVec3(axis[0], axis[1], axis[2])) { if (axis.squaredNorm() > 0) axis.normalize(); } @@ -137,10 +137,10 @@ AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInsta } else if (this->type == MIRROR) { - Value val_v = c.lookup_variable("v"); + ValuePtr val_v = c.lookup_variable("v"); double x = 1, y = 0, z = 0; - if (val_v.getVec3(x, y, z)) { + if (val_v->getVec3(x, y, z)) { if (x != 0.0 || y != 0.0 || z != 0.0) { double sn = 1.0 / sqrt(x*x + y*y + z*z); x *= sn, y *= sn, z *= sn; @@ -159,19 +159,20 @@ AbstractNode *TransformModule::instantiate(const Context *ctx, const ModuleInsta } else if (this->type == TRANSLATE) { - Value v = c.lookup_variable("v"); + ValuePtr v = c.lookup_variable("v"); Vector3d translatevec(0,0,0); - v.getVec3(translatevec[0], translatevec[1], translatevec[2]); + v->getVec3(translatevec[0], translatevec[1], translatevec[2]); node->matrix.translate(translatevec); } else if (this->type == MULTMATRIX) { - Value v = c.lookup_variable("m"); - if (v.type() == Value::VECTOR) { + ValuePtr v = c.lookup_variable("m"); + if (v->type() == Value::VECTOR) { for (int i = 0; i < 16; i++) { size_t x = i / 4, y = i % 4; - if (y < v.toVector().size() && v.toVector()[y].type() == Value::VECTOR && x < v.toVector()[y].toVector().size()) - v.toVector()[y].toVector()[x].getDouble(node->matrix(y, x)); + if (y < v->toVector().size() && v->toVector()[y].type() == + Value::VECTOR && x < v->toVector()[y].toVector().size()) + v->toVector()[y].toVector()[x].getDouble(node->matrix(y, x)); } } } @@ -190,7 +191,7 @@ std::string TransformNode::toString() const for (int j=0;j<4;j++) { stream << "["; for (int i=0;i<4;i++) { - Value v( this->matrix(j, i) ); + Value v(this->matrix(j, i)); stream << v; if (i != 3) stream << ", "; } diff --git a/src/value.cc b/src/value.cc index 66391a95..3180f2b0 100644 --- a/src/value.cc +++ b/src/value.cc @@ -41,6 +41,9 @@ #include +Value Value::undefined; +ValuePtr ValuePtr::undefined; + std::ostream &operator<<(std::ostream &stream, const Filename &filename) { fs::path fnpath = fs::path( (std::string)filename ); @@ -77,8 +80,6 @@ std::ostream &operator<<(std::ostream &stream, const QuotedString &s) return stream; } -Value Value::undefined; - Value::Value() : value(boost::blank()) { // std::cout << "creating undef\n"; @@ -688,7 +689,7 @@ public: } }; -Value Value::operator[](const Value &v) +Value Value::operator[](const Value &v) const { return boost::apply_visitor(bracket_visitor(), this->value, v.value); } @@ -790,3 +791,119 @@ bool Value::RangeType::iterator::operator!=(const self_type &other) const { return !(*this == other); } + +ValuePtr::ValuePtr() +{ + this->reset(new Value()); +} + +ValuePtr::ValuePtr(const Value &v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(bool v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(int v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(double v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(const std::string &v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(const char *v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(const char v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(const Value::VectorType &v) +{ + this->reset(new Value(v)); +} + +ValuePtr::ValuePtr(const Value::RangeType &v) +{ + this->reset(new Value(v)); +} + +bool ValuePtr::operator==(const ValuePtr &v) const +{ + return ValuePtr(**this == *v); +} + +bool ValuePtr::operator!=(const ValuePtr &v) const +{ + return ValuePtr(**this != *v); +} + +bool ValuePtr::operator<(const ValuePtr &v) const +{ + return ValuePtr(**this < *v); +} + +bool ValuePtr::operator<=(const ValuePtr &v) const +{ + return ValuePtr(**this <= *v); +} + +bool ValuePtr::operator>=(const ValuePtr &v) const +{ + return ValuePtr(**this >= *v); +} + +bool ValuePtr::operator>(const ValuePtr &v) const +{ + return ValuePtr(**this > *v); +} + +ValuePtr ValuePtr::operator-() const +{ + return ValuePtr(-**this); +} + +ValuePtr ValuePtr::operator[](const ValuePtr &v) const +{ + return ValuePtr((**this)[*v]); +} + +ValuePtr ValuePtr::operator+(const ValuePtr &v) const +{ + return ValuePtr(**this + *v); +} + +ValuePtr ValuePtr::operator-(const ValuePtr &v) const +{ + return ValuePtr(**this - *v); +} + +ValuePtr ValuePtr::operator*(const ValuePtr &v) const +{ + return ValuePtr(**this * *v); +} + +ValuePtr ValuePtr::operator/(const ValuePtr &v) const +{ + return ValuePtr(**this / *v); +} + +ValuePtr ValuePtr::operator%(const ValuePtr &v) const +{ + return ValuePtr(**this % *v); +} + diff --git a/src/value.h b/src/value.h index 6c59ef2c..bfd08376 100644 --- a/src/value.h +++ b/src/value.h @@ -11,6 +11,7 @@ #include #endif #include +#include "memory.h" class QuotedString : public std::string { @@ -138,7 +139,7 @@ public: bool operator>=(const Value &v) const; bool operator>(const Value &v) const; Value operator-() const; - Value operator[](const Value &v); + Value operator[](const Value &v) const; Value operator+(const Value &v) const; Value operator-(const Value &v) const; Value operator*(const Value &v) const; @@ -160,3 +161,40 @@ private: Variant value; }; + +class ValuePtr : public shared_ptr +{ +public: + static ValuePtr undefined; + + ValuePtr(); + explicit ValuePtr(const Value &v); + ValuePtr(bool v); + ValuePtr(int v); + ValuePtr(double v); + ValuePtr(const std::string &v); + ValuePtr(const char *v); + ValuePtr(const char v); + ValuePtr(const Value::VectorType &v); + ValuePtr(const Value::RangeType &v); + + operator bool() const { return **this; } + + bool operator==(const ValuePtr &v) const; + bool operator!=(const ValuePtr &v) const; + bool operator<(const ValuePtr &v) const; + bool operator<=(const ValuePtr &v) const; + bool operator>=(const ValuePtr &v) const; + bool operator>(const ValuePtr &v) const; + ValuePtr operator-() const; + ValuePtr operator[](const ValuePtr &v) const; + ValuePtr operator+(const ValuePtr &v) const; + ValuePtr operator-(const ValuePtr &v) const; + ValuePtr operator*(const ValuePtr &v) const; + ValuePtr operator/(const ValuePtr &v) const; + ValuePtr operator%(const ValuePtr &v) const; + + const Value &operator*() const { return *this->get(); } + +private: +}; diff --git a/testdata/scad/misc/recursion-tests.scad b/testdata/scad/misc/recursion-tests.scad index 2a07b915..78b31e35 100644 --- a/testdata/scad/misc/recursion-tests.scad +++ b/testdata/scad/misc/recursion-tests.scad @@ -1,2 +1,5 @@ function crash() = crash(); echo(crash()); + +module crash() crash(); +crash(); diff --git a/testdata/scad/misc/tail-recursion-tests.scad b/testdata/scad/misc/tail-recursion-tests.scad new file mode 100644 index 00000000..5795530a --- /dev/null +++ b/testdata/scad/misc/tail-recursion-tests.scad @@ -0,0 +1,31 @@ +function substring(text, start, end = -1, idx = -1, res = "") = + idx < end && idx < len(text) + ? substring(text, start, end, idx < 0 ? start + 1 : idx + 1, str(res, text[idx < 0 ? start : idx])) + : res; + +// normal recursion, no tail-recursion elimination possible +function f3a(a, ret = 0) = a <= 0 ? 0 : a + f3a(a - 1); +echo("without tail-recursion eliminiation: ", f3a(100)); + +// this allows tail-recursion eliminiation +function f3b(a, ret = 0) = a <= 0 ? ret : f3b(a - 1, ret + a); +echo("with tail-recursion eliminiation: ", f3b(100)); + +// check tail-recursion eliminiation by using a high loop count +function f3c(a, ret = 0) = a <= 0 ? ret : f3c(a - 1, ret + a); +echo("with tail-recursion eliminiation: ", f3c(2000)); + +// use nested function call +function f1(x, y = []) = x <= 0 ? y : f1(x - 1, concat(y, [[x, x]])); +echo(f1(2000)[20]); + +// recursion in the "false" part of the ternary operator +function c(a, b) = chr(a % 26 + b); +function f2a(x, y = 0, t = "") = x <= 0 ? t : f2a(x - 1, y + 2, str(t, c(y, 65))); +s1 = f2a(50000); +echo(len(s1), substring(s1, 0, 40)); + +// recursion in the "true" part of the ternary operator +function f2b(x, y = 0, t = "") = x > 0 ? f2b(x - 1, y + 1, str(t, chr((y % 26) + 97))) : t; +s2 = f2b(50000); +echo(len(s2), substring(s2, 0, 40)); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d4703592..be237330 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -78,7 +78,6 @@ endif() # Most tests will fail, but it can be used for testing/experiments if(NULLGL) - set(ENABLE_OPENCSG_FLAG "") # OpenCSG is entirely an OpenGL software set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNULLGL") set(SKIP_IMAGEMAGICK "1") # we dont generate png, so nothing to compare else() @@ -620,6 +619,7 @@ set(CORE_SOURCES ../src/calc.cc ../src/expr.cc ../src/func.cc + ../src/stackcheck.cc ../src/localscope.cc ../src/module.cc ../src/ModuleCache.cc @@ -736,16 +736,10 @@ target_link_libraries(tests-cgal tests-common ${CGAL_LIBRARY} ${CGAL_3RD_PARTY_L # # Create non-CGAL tests # +add_library(tests-nocgal STATIC ${NOCGAL_SOURCES}) +target_link_libraries(tests-nocgal tests-common) if (NOT NULLGL) - add_library(tests-nocgal STATIC ${NOCGAL_SOURCES}) - set_target_properties(tests-nocgal PROPERTIES COMPILE_FLAGS ${ENABLE_OPENCSG_FLAG}) - target_link_libraries(tests-nocgal tests-common) -else() - message(STATUS "NULLGL: cannot use GL/GLU tessellator. see dxftess.cc") - message(STATUS "NULLGL: non-CGAL tests will use CGAL's tessellator") - add_library(tests-nocgal STATIC ${NOCGAL_SOURCES}) - set_target_properties(tests-nocgal PROPERTIES COMPILE_FLAGS ${ENABLE_OPENCSG_FLAG}) - target_link_libraries(tests-nocgal tests-common) + set_target_properties(tests-nocgal PROPERTIES COMPILE_FLAGS "${ENABLE_OPENCSG_FLAG}") endif() add_library(tests-offscreen STATIC ${OFFSCREEN_SOURCES}) @@ -767,8 +761,8 @@ target_link_libraries(csgtexttest tests-nocgal) # cgalcachetest # add_executable(cgalcachetest cgalcachetest.cc) -set_target_properties(cgalcachetest PROPERTIES COMPILE_FLAGS "${ENABLE_OPENCSG_FLAG} -DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}") -target_link_libraries(cgalcachetest tests-cgal ${GLEW_LIBRARY}) +set_target_properties(cgalcachetest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}") +target_link_libraries(cgalcachetest tests-cgal) # # openscad no-qt @@ -1044,6 +1038,7 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES} ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests-unicode.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/recursion-tests.scad + ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/tail-recursion-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/value-reassignment-tests2.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/variable-scope-tests.scad diff --git a/tests/cgalcachetest.cc b/tests/cgalcachetest.cc index 663dc48c..61a9d3e4 100644 --- a/tests/cgalcachetest.cc +++ b/tests/cgalcachetest.cc @@ -38,6 +38,7 @@ #include "CGAL_Nef_polyhedron.h" #include "GeometryEvaluator.h" #include "CGALCache.h" +#include "stackcheck.h" #ifndef _MSC_VER #include @@ -88,6 +89,8 @@ int main(int argc, char **argv) { const char *filename, *outfilename = NULL; size_t cgalcachesize = 1*1024*1024; + StackCheck::inst()->init(); + po::variables_map vm; try { vm = parse_options(argc, argv); diff --git a/tests/csgtexttest.cc b/tests/csgtexttest.cc index c7a6855d..11e687d9 100644 --- a/tests/csgtexttest.cc +++ b/tests/csgtexttest.cc @@ -37,6 +37,7 @@ #include "builtin.h" #include "Tree.h" #include "PlatformUtils.h" +#include "stackcheck.h" #ifndef _MSC_VER #include @@ -73,6 +74,7 @@ int main(int argc, char **argv) int rc = 0; + StackCheck::inst()->init(); Builtins::instance()->initialize(); fs::path original_path = fs::current_path(); diff --git a/tests/modulecachetest.cc b/tests/modulecachetest.cc index c5c46fc0..78398241 100644 --- a/tests/modulecachetest.cc +++ b/tests/modulecachetest.cc @@ -34,6 +34,7 @@ #include "export.h" #include "builtin.h" #include "Tree.h" +#include "stackcheck.h" #ifndef _MSC_VER #include @@ -68,6 +69,7 @@ int main(int argc, char **argv) int rc = 0; + StackCheck::inst()->init(); Builtins::instance()->initialize(); fs::path original_path = fs::current_path(); diff --git a/tests/regression/echotest/recursion-tests-expected.echo b/tests/regression/echotest/recursion-tests-expected.echo index e5c99b1e..4bf735f7 100644 --- a/tests/regression/echotest/recursion-tests-expected.echo +++ b/tests/regression/echotest/recursion-tests-expected.echo @@ -1,2 +1,3 @@ ERROR: Recursion detected calling function 'crash' ECHO: undef +ERROR: Recursion detected calling module 'crash' diff --git a/tests/regression/echotest/tail-recursion-tests-expected.echo b/tests/regression/echotest/tail-recursion-tests-expected.echo new file mode 100644 index 00000000..753e2514 --- /dev/null +++ b/tests/regression/echotest/tail-recursion-tests-expected.echo @@ -0,0 +1,6 @@ +ECHO: "without tail-recursion eliminiation: ", 5050 +ECHO: "with tail-recursion eliminiation: ", 5050 +ECHO: "with tail-recursion eliminiation: ", 2001000 +ECHO: [1980, 1980] +ECHO: 50000, "ACEGIKMOQSUWYACEGIKMOQSUWYACEGIKMOQSUWYA" +ECHO: 50000, "abcdefghijklmnopqrstuvwxyzabcdefghijklmn"