mirror of https://github.com/vitalif/openscad
Add list-comprehensions
parent
ed3041c551
commit
b611ed5eac
152
src/expr.cc
152
src/expr.cc
|
@ -74,6 +74,89 @@ private:
|
|||
const Expression &expr;
|
||||
};
|
||||
|
||||
// unnamed namespace
|
||||
namespace {
|
||||
Value::VectorType flatten(Value::VectorType const& vec) {
|
||||
int n = 0;
|
||||
for (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++) {
|
||||
std::copy(vec[i].toVector().begin(),vec[i].toVector().end(),std::back_inserter(ret));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void evaluate_sequential_assignment(const AssignmentList & assignment_list, Context *context) {
|
||||
EvalContext let_context(context, assignment_list);
|
||||
|
||||
for (int i = 0; i < let_context.numArgs(); i++) {
|
||||
// NOTE: iteratively evaluated list of arguments
|
||||
context->set_variable(let_context.getArgName(i), let_context.getArgValue(i, context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value Expression::evaluate_list_comprehension(const Context *context) const
|
||||
{
|
||||
Value::VectorType vec;
|
||||
|
||||
if (this->call_funcname == "if") {
|
||||
if (this->children[0]->evaluate(context)) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Value Expression::evaluate(const Context *context) const
|
||||
{
|
||||
if (this->type == "!")
|
||||
|
@ -171,18 +254,34 @@ Value Expression::evaluate(const Context *context) const
|
|||
return context->evaluate_function(this->call_funcname, &c);
|
||||
}
|
||||
if (this->type == "l") { // let expression
|
||||
EvalContext let_context(context, this->call_arguments);
|
||||
|
||||
Context c(context);
|
||||
for (int i = 0; i < let_context.numArgs(); i++) {
|
||||
// NOTE: iteratively evaluated list of arguments
|
||||
c.set_variable(let_context.getArgName(i), let_context.getArgValue(i, &c));
|
||||
}
|
||||
evaluate_sequential_assignment(this->call_arguments, &c);
|
||||
|
||||
return this->children[0]->evaluate(&c);
|
||||
}
|
||||
if (this->type == "i") { // list comprehension expression
|
||||
return this->children[0]->evaluate(context);
|
||||
}
|
||||
if (this->type == "c") {
|
||||
return evaluate_list_comprehension(context);
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
namespace /* anonymous*/ {
|
||||
|
||||
std::ostream &operator << (std::ostream &o, AssignmentList const& l) {
|
||||
for (size_t i=0; i < l.size(); i++) {
|
||||
const Assignment &arg = l[i];
|
||||
if (i > 0) o << ", ";
|
||||
if (!arg.first.empty()) o << arg.first << " = ";
|
||||
o << *arg.second;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string Expression::toString() const
|
||||
{
|
||||
std::stringstream stream;
|
||||
|
@ -230,25 +329,32 @@ std::string Expression::toString() const
|
|||
stream << *this->children[0] << "." << this->var_name;
|
||||
}
|
||||
else if (this->type == "F") {
|
||||
stream << this->call_funcname << "(";
|
||||
for (size_t i=0; i < this->call_arguments.size(); i++) {
|
||||
const Assignment &arg = this->call_arguments[i];
|
||||
if (i > 0) stream << ", ";
|
||||
if (!arg.first.empty()) stream << arg.first << " = ";
|
||||
stream << *arg.second;
|
||||
}
|
||||
stream << ")";
|
||||
stream << this->call_funcname << "(" << this->call_arguments << ")";
|
||||
}
|
||||
else if (this->type == "l") {
|
||||
stream << "let(";
|
||||
for (size_t i=0; i < this->call_arguments.size(); i++) {
|
||||
const Assignment &arg = this->call_arguments[i];
|
||||
if (i > 0) stream << ", ";
|
||||
if (!arg.first.empty()) stream << arg.first << " = ";
|
||||
stream << *arg.second;
|
||||
}
|
||||
stream << ") ";
|
||||
stream << *this->children[0];
|
||||
stream << "let(" << this->call_arguments << ") " << *this->children[0];
|
||||
}
|
||||
else if (this->type == "i") { // list comprehension expression
|
||||
Expression const* c = this->children[0];
|
||||
|
||||
stream << "[";
|
||||
|
||||
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];
|
||||
} else {
|
||||
assert(false && "Illegal list comprehension element");
|
||||
}
|
||||
} while (c->type == "c");
|
||||
|
||||
stream << *c << "]";
|
||||
}
|
||||
else {
|
||||
assert(false && "Illegal expression type");
|
||||
|
|
|
@ -38,6 +38,8 @@ public:
|
|||
~Expression();
|
||||
|
||||
Value evaluate(const class Context *context) const;
|
||||
Value evaluate_list_comprehension(const Context *context) const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
mutable int recursioncount;
|
||||
|
|
|
@ -143,6 +143,7 @@ use[ \t\r\n>]*"<" { BEGIN(cond_use); }
|
|||
"if" return TOK_IF;
|
||||
"else" return TOK_ELSE;
|
||||
"let" return TOK_LET;
|
||||
"for" return TOK_FOR;
|
||||
|
||||
"true" return TOK_TRUE;
|
||||
"false" return TOK_FALSE;
|
||||
|
|
54
src/parser.y
54
src/parser.y
|
@ -84,6 +84,7 @@ std::string parser_source_path;
|
|||
%token TOK_FUNCTION
|
||||
%token TOK_IF
|
||||
%token TOK_ELSE
|
||||
%token TOK_FOR
|
||||
%token TOK_LET
|
||||
|
||||
%token <text> TOK_ID
|
||||
|
@ -114,6 +115,8 @@ std::string parser_source_path;
|
|||
|
||||
%type <expr> expr
|
||||
%type <expr> vector_expr
|
||||
%type <expr> list_comprehension_elements
|
||||
%type <expr> list_comprehension_elements_or_expr
|
||||
|
||||
%type <inst> module_instantiation
|
||||
%type <ifelse> if_statement
|
||||
|
@ -125,6 +128,7 @@ std::string parser_source_path;
|
|||
|
||||
%type <arg> argument_call
|
||||
%type <arg> argument_decl
|
||||
%type <text> module_id
|
||||
|
||||
%debug
|
||||
|
||||
|
@ -281,8 +285,15 @@ child_statement:
|
|||
assignment ;
|
||||
*/
|
||||
|
||||
|
||||
// "for" is a valid module identifier
|
||||
module_id:
|
||||
TOK_ID { $$ = $1; }
|
||||
| TOK_FOR { $$ = strdup("for"); }
|
||||
;
|
||||
|
||||
single_module_instantiation:
|
||||
TOK_ID '(' arguments_call ')'
|
||||
module_id '(' arguments_call ')'
|
||||
{
|
||||
$$ = new ModuleInstantiation($1);
|
||||
$$->arguments = *$3;
|
||||
|
@ -350,6 +361,12 @@ expr:
|
|||
$$->children.push_back($4);
|
||||
$$->children.push_back($6);
|
||||
}
|
||||
| '[' list_comprehension_elements ']'
|
||||
{
|
||||
$$ = new Expression();
|
||||
$$->type = "i";
|
||||
$$->children.push_back($2);
|
||||
}
|
||||
| '[' optional_commas ']'
|
||||
{
|
||||
$$ = new Expression(Value(Value::VectorType()));
|
||||
|
@ -449,6 +466,41 @@ expr:
|
|||
}
|
||||
;
|
||||
|
||||
list_comprehension_elements:
|
||||
/* The last set element may not be a "let" (as that would instead
|
||||
be parsed as an expression) */
|
||||
TOK_LET '(' arguments_call ')' list_comprehension_elements
|
||||
{
|
||||
$$ = new Expression("c", $5);
|
||||
$$->call_funcname = "let";
|
||||
$$->call_arguments = *$3;
|
||||
delete $3;
|
||||
}
|
||||
| TOK_FOR '(' arguments_call ')' list_comprehension_elements_or_expr
|
||||
{
|
||||
$$ = $5;
|
||||
|
||||
/* 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]);
|
||||
$$ = e;
|
||||
}
|
||||
delete $3;
|
||||
}
|
||||
| TOK_IF '(' expr ')' list_comprehension_elements_or_expr
|
||||
{
|
||||
$$ = new Expression("c", $3, $5);
|
||||
$$->call_funcname = "if";
|
||||
}
|
||||
;
|
||||
|
||||
list_comprehension_elements_or_expr:
|
||||
list_comprehension_elements
|
||||
| expr
|
||||
;
|
||||
|
||||
optional_commas:
|
||||
',' optional_commas
|
||||
| /* empty */
|
||||
|
|
Loading…
Reference in New Issue