mirror of https://github.com/vitalif/openscad
Add list-comprehensions
parent
ed3041c551
commit
b611ed5eac
150
src/expr.cc
150
src/expr.cc
|
@ -74,6 +74,89 @@ private:
|
||||||
const Expression &expr;
|
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
|
Value Expression::evaluate(const Context *context) const
|
||||||
{
|
{
|
||||||
if (this->type == "!")
|
if (this->type == "!")
|
||||||
|
@ -171,18 +254,34 @@ Value Expression::evaluate(const Context *context) const
|
||||||
return context->evaluate_function(this->call_funcname, &c);
|
return context->evaluate_function(this->call_funcname, &c);
|
||||||
}
|
}
|
||||||
if (this->type == "l") { // let expression
|
if (this->type == "l") { // let expression
|
||||||
EvalContext let_context(context, this->call_arguments);
|
|
||||||
|
|
||||||
Context c(context);
|
Context c(context);
|
||||||
for (int i = 0; i < let_context.numArgs(); i++) {
|
evaluate_sequential_assignment(this->call_arguments, &c);
|
||||||
// NOTE: iteratively evaluated list of arguments
|
|
||||||
c.set_variable(let_context.getArgName(i), let_context.getArgValue(i, &c));
|
|
||||||
}
|
|
||||||
return this->children[0]->evaluate(&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();
|
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::string Expression::toString() const
|
||||||
{
|
{
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
|
@ -230,25 +329,32 @@ std::string Expression::toString() const
|
||||||
stream << *this->children[0] << "." << this->var_name;
|
stream << *this->children[0] << "." << this->var_name;
|
||||||
}
|
}
|
||||||
else if (this->type == "F") {
|
else if (this->type == "F") {
|
||||||
stream << this->call_funcname << "(";
|
stream << this->call_funcname << "(" << this->call_arguments << ")";
|
||||||
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 << ")";
|
|
||||||
}
|
}
|
||||||
else if (this->type == "l") {
|
else if (this->type == "l") {
|
||||||
stream << "let(";
|
stream << "let(" << this->call_arguments << ") " << *this->children[0];
|
||||||
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 << ") ";
|
else if (this->type == "i") { // list comprehension expression
|
||||||
stream << *this->children[0];
|
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 {
|
else {
|
||||||
assert(false && "Illegal expression type");
|
assert(false && "Illegal expression type");
|
||||||
|
|
|
@ -38,6 +38,8 @@ public:
|
||||||
~Expression();
|
~Expression();
|
||||||
|
|
||||||
Value evaluate(const class Context *context) const;
|
Value evaluate(const class Context *context) const;
|
||||||
|
Value evaluate_list_comprehension(const Context *context) const;
|
||||||
|
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
|
|
||||||
mutable int recursioncount;
|
mutable int recursioncount;
|
||||||
|
|
|
@ -143,6 +143,7 @@ use[ \t\r\n>]*"<" { BEGIN(cond_use); }
|
||||||
"if" return TOK_IF;
|
"if" return TOK_IF;
|
||||||
"else" return TOK_ELSE;
|
"else" return TOK_ELSE;
|
||||||
"let" return TOK_LET;
|
"let" return TOK_LET;
|
||||||
|
"for" return TOK_FOR;
|
||||||
|
|
||||||
"true" return TOK_TRUE;
|
"true" return TOK_TRUE;
|
||||||
"false" return TOK_FALSE;
|
"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_FUNCTION
|
||||||
%token TOK_IF
|
%token TOK_IF
|
||||||
%token TOK_ELSE
|
%token TOK_ELSE
|
||||||
|
%token TOK_FOR
|
||||||
%token TOK_LET
|
%token TOK_LET
|
||||||
|
|
||||||
%token <text> TOK_ID
|
%token <text> TOK_ID
|
||||||
|
@ -114,6 +115,8 @@ std::string parser_source_path;
|
||||||
|
|
||||||
%type <expr> expr
|
%type <expr> expr
|
||||||
%type <expr> vector_expr
|
%type <expr> vector_expr
|
||||||
|
%type <expr> list_comprehension_elements
|
||||||
|
%type <expr> list_comprehension_elements_or_expr
|
||||||
|
|
||||||
%type <inst> module_instantiation
|
%type <inst> module_instantiation
|
||||||
%type <ifelse> if_statement
|
%type <ifelse> if_statement
|
||||||
|
@ -125,6 +128,7 @@ std::string parser_source_path;
|
||||||
|
|
||||||
%type <arg> argument_call
|
%type <arg> argument_call
|
||||||
%type <arg> argument_decl
|
%type <arg> argument_decl
|
||||||
|
%type <text> module_id
|
||||||
|
|
||||||
%debug
|
%debug
|
||||||
|
|
||||||
|
@ -281,8 +285,15 @@ child_statement:
|
||||||
assignment ;
|
assignment ;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// "for" is a valid module identifier
|
||||||
|
module_id:
|
||||||
|
TOK_ID { $$ = $1; }
|
||||||
|
| TOK_FOR { $$ = strdup("for"); }
|
||||||
|
;
|
||||||
|
|
||||||
single_module_instantiation:
|
single_module_instantiation:
|
||||||
TOK_ID '(' arguments_call ')'
|
module_id '(' arguments_call ')'
|
||||||
{
|
{
|
||||||
$$ = new ModuleInstantiation($1);
|
$$ = new ModuleInstantiation($1);
|
||||||
$$->arguments = *$3;
|
$$->arguments = *$3;
|
||||||
|
@ -350,6 +361,12 @@ expr:
|
||||||
$$->children.push_back($4);
|
$$->children.push_back($4);
|
||||||
$$->children.push_back($6);
|
$$->children.push_back($6);
|
||||||
}
|
}
|
||||||
|
| '[' list_comprehension_elements ']'
|
||||||
|
{
|
||||||
|
$$ = new Expression();
|
||||||
|
$$->type = "i";
|
||||||
|
$$->children.push_back($2);
|
||||||
|
}
|
||||||
| '[' optional_commas ']'
|
| '[' optional_commas ']'
|
||||||
{
|
{
|
||||||
$$ = new Expression(Value(Value::VectorType()));
|
$$ = 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:
|
||||||
',' optional_commas
|
',' optional_commas
|
||||||
| /* empty */
|
| /* empty */
|
||||||
|
|
Loading…
Reference in New Issue