diff --git a/src/expr.cc b/src/expr.cc index 8076ccc9..501d8820 100644 --- a/src/expr.cc +++ b/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"); diff --git a/src/expression.h b/src/expression.h index bf601d3e..788e893d 100644 --- a/src/expression.h +++ b/src/expression.h @@ -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; diff --git a/src/lexer.l b/src/lexer.l index 2e501cd5..b8cfbfb1 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -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; diff --git a/src/parser.y b/src/parser.y index c95d7253..347045ac 100644 --- a/src/parser.y +++ b/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 TOK_ID @@ -114,6 +115,8 @@ std::string parser_source_path; %type expr %type vector_expr +%type list_comprehension_elements +%type list_comprehension_elements_or_expr %type module_instantiation %type if_statement @@ -125,6 +128,7 @@ std::string parser_source_path; %type argument_call %type argument_decl +%type 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 */