openscad/src/context.cc

205 lines
6.5 KiB
C++
Raw Normal View History

/*
2011-01-21 04:21:09 +03:00
* OpenSCAD (www.openscad.org)
* Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
* Marius Kintel <marius@kintel.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* As a special exception, you have permission to link this program
* with the CGAL library and distribute executables, as long as you
* follow the requirements of the GNU GPL in regard to all of the
* software in the executable aside from CGAL.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "context.h"
#include "expression.h"
#include "function.h"
#include "module.h"
#include "builtin.h"
#include "printutils.h"
2011-09-03 08:10:36 +04:00
#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
using namespace boost::filesystem;
2011-09-03 20:51:29 +04:00
std::vector<const Context*> Context::ctx_stack;
/*!
Initializes this context. Optionally initializes a context for an external library
*/
Context::Context(const Context *parent, const Module *library)
: parent(parent), inst_p(NULL)
{
2011-09-03 08:10:36 +04:00
ctx_stack.push_back(this);
2011-09-03 20:51:29 +04:00
if (parent) document_path = parent->document_path;
if (library) {
// FIXME: Don't access module members directly
2011-09-03 20:51:29 +04:00
this->functions_p = &library->functions;
this->modules_p = &library->modules;
this->usedlibs_p = &library->usedlibs;
for (size_t j = 0; j < library->assignments_var.size(); j++) {
this->set_variable(library->assignments_var[j],
library->assignments_expr[j]->evaluate(this));
}
}
else {
functions_p = NULL;
modules_p = NULL;
usedlibs_p = NULL;
}
}
Context::~Context()
{
ctx_stack.pop_back();
}
2011-09-03 20:51:29 +04:00
/*!
Initialize context from argument lists (function call/module instantiation)
*/
void Context::args(const std::vector<std::string> &argnames,
const std::vector<Expression*> &argexpr,
const std::vector<std::string> &call_argnames,
const std::vector<Value> &call_argvalues)
{
2011-09-03 08:10:36 +04:00
for (size_t i=0; i<argnames.size(); i++) {
2011-09-03 20:51:29 +04:00
set_variable(argnames[i], i < argexpr.size() && argexpr[i] ?
argexpr[i]->evaluate(this->parent) : Value());
}
2011-09-03 08:10:36 +04:00
size_t posarg = 0;
for (size_t i=0; i<call_argnames.size(); i++) {
if (call_argnames[i].empty()) {
if (posarg < argnames.size())
set_variable(argnames[posarg++], call_argvalues[i]);
} else {
set_variable(call_argnames[i], call_argvalues[i]);
}
}
}
2011-09-03 20:51:29 +04:00
void Context::set_variable(const std::string &name, const Value &value)
{
2011-09-03 08:10:36 +04:00
if (name[0] == '$')
2011-09-03 20:51:29 +04:00
this->config_variables[name] = value;
else
this->variables[name] = value;
}
void Context::set_constant(const std::string &name, const Value &value)
{
if (this->constants.find(name) != this->constants.end())
PRINTF("WARNING: Attempt to modify constant '%s'.",name.c_str());
else
2011-09-03 20:51:29 +04:00
this->constants[name] = value;
}
2011-09-03 08:10:36 +04:00
Value Context::lookup_variable(const std::string &name, bool silent) const
{
2011-09-03 08:10:36 +04:00
if (name[0] == '$') {
for (int i = ctx_stack.size()-1; i >= 0; i--) {
2011-09-03 08:10:36 +04:00
const ValueMap &confvars = ctx_stack[i]->config_variables;
if (confvars.find(name) != confvars.end())
return confvars.find(name)->second;
}
return Value();
}
2011-09-03 20:51:29 +04:00
if (!this->parent && this->constants.find(name) != this->constants.end())
return this->constants.find(name)->second;
if (this->variables.find(name) != this->variables.end())
return this->variables.find(name)->second;
if (this->parent)
return this->parent->lookup_variable(name, silent);
if (!silent)
2011-09-03 08:10:36 +04:00
PRINTF("WARNING: Ignoring unknown variable '%s'.", name.c_str());
return Value();
}
2011-09-03 20:51:29 +04:00
Value Context::evaluate_function(const std::string &name,
const std::vector<std::string> &argnames,
const std::vector<Value> &argvalues) const
{
2011-09-03 20:51:29 +04:00
if (this->functions_p && this->functions_p->find(name) != this->functions_p->end())
return this->functions_p->find(name)->second->evaluate(this, argnames, argvalues);
if (this->usedlibs_p) {
BOOST_FOREACH(const ModuleContainer::value_type &m, *this->usedlibs_p) {
if (m.second->functions.find(name) != m.second->functions.end()) {
Context ctx(this->parent, m.second);
2011-09-03 08:10:36 +04:00
return m.second->functions[name]->evaluate(&ctx, argnames, argvalues);
}
}
}
2011-09-03 20:51:29 +04:00
if (this->parent)
return this->parent->evaluate_function(name, argnames, argvalues);
PRINTF("WARNING: Ignoring unknown function '%s'.", name.c_str());
return Value();
}
2011-09-03 20:51:29 +04:00
AbstractNode *Context::evaluate_module(const ModuleInstantiation &inst) const
{
if (this->modules_p && this->modules_p->find(inst.name()) != this->modules_p->end()) {
AbstractModule *m = this->modules_p->find(inst.name())->second;
std::string replacement = Builtins::instance()->isDeprecated(inst.name());
if (!replacement.empty()) {
PRINTF("DEPRECATED: The %s() module will be removed in future releases. Use %s() instead.", inst.name().c_str(), replacement.c_str());
}
return m->evaluate(this, &inst);
}
2011-09-03 20:51:29 +04:00
if (this->usedlibs_p) {
BOOST_FOREACH(const ModuleContainer::value_type &m, *this->usedlibs_p) {
if (m.second->modules.find(inst.name()) != m.second->modules.end()) {
2011-09-03 20:51:29 +04:00
Context ctx(this->parent, m.second);
return m.second->modules[inst.name()]->evaluate(&ctx, &inst);
}
}
}
2011-09-03 20:51:29 +04:00
if (this->parent) return this->parent->evaluate_module(inst);
PRINTF("WARNING: Ignoring unknown module '%s'.", inst.name().c_str());
return NULL;
}
/*!
Returns the absolute path to the given filename, unless it's empty.
*/
2011-09-03 20:51:29 +04:00
std::string Context::getAbsolutePath(const std::string &filename) const
{
2011-09-03 08:10:36 +04:00
if (!filename.empty()) {
return absolute(path(this->document_path) / filename).string();
}
else {
return filename;
}
}
void register_builtin(Context &ctx)
{
ctx.functions_p = &Builtins::instance()->functions();
ctx.modules_p = &Builtins::instance()->modules();
ctx.set_variable("$fn", Value(0.0));
ctx.set_variable("$fs", Value(2.0));
ctx.set_variable("$fa", Value(12.0));
ctx.set_variable("$t", Value(0.0));
Value zero3;
zero3.type = Value::VECTOR;
zero3.append(new Value(0.0));
zero3.append(new Value(0.0));
zero3.append(new Value(0.0));
ctx.set_variable("$vpt", zero3);
ctx.set_variable("$vpr", zero3);
ctx.set_constant("PI",Value(M_PI));
}