Add list-comprehensions

master
Oskar Linde 2014-04-25 13:29:00 +02:00
parent ed3041c551
commit b611ed5eac
4 changed files with 185 additions and 24 deletions

View File

@ -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");

View File

@ -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;

View File

@ -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;

View File

@ -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 */