From c47350dce9c666987f924c54507973fc11587b2e Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 21 Feb 2012 03:53:00 -0600 Subject: Corrected handling of nested definitions --- test/baseline/cmd-script.test | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 test/baseline/cmd-script.test (limited to 'test/baseline/cmd-script.test') diff --git a/test/baseline/cmd-script.test b/test/baseline/cmd-script.test new file mode 100644 index 00000000..ed665dcc --- /dev/null +++ b/test/baseline/cmd-script.test @@ -0,0 +1,3 @@ +test eval 'foo(w, u)=(z=w+u;z*2); (a=1 + 1; foo(10, 15))' +50 +end test -- cgit v1.2.3 From a0c9ab08dccf65259474b5da97e4f5b092741779 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 22:20:20 -0600 Subject: Added more baseline testing, for valexprs and Python --- test/baseline/cmd-script.test | 9 +++++++++ test/baseline/cmd-script_2.test | 3 --- test/baseline/feat-value_py.test | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) delete mode 100644 test/baseline/cmd-script_2.test create mode 100644 test/baseline/feat-value_py.test (limited to 'test/baseline/cmd-script.test') diff --git a/test/baseline/cmd-script.test b/test/baseline/cmd-script.test index ed665dcc..12e9c868 100644 --- a/test/baseline/cmd-script.test +++ b/test/baseline/cmd-script.test @@ -1,3 +1,12 @@ test eval 'foo(w, u)=(z=w+u;z*2); (a=1 + 1; foo(10, 15))' 50 end test + +test eval 'foo(x, y, z)=print(x, y, z); bar(x)=x; foo(1, 2, 3); bar(3)' +123 +3 +end test + +test eval 'total_expr=$100;amount_expr=$15;x=total_expr;x=x/5;x=amount_expr-x*5;x' +$-85 +end test diff --git a/test/baseline/cmd-script_2.test b/test/baseline/cmd-script_2.test deleted file mode 100644 index 51ea41fb..00000000 --- a/test/baseline/cmd-script_2.test +++ /dev/null @@ -1,3 +0,0 @@ -test eval 'total_expr=$100;amount_expr=$15;x=total_expr;x=x/5;x=amount_expr-x*5;x' -$-85 -end test diff --git a/test/baseline/feat-value_py.test b/test/baseline/feat-value_py.test new file mode 100644 index 00000000..5efe315d --- /dev/null +++ b/test/baseline/feat-value_py.test @@ -0,0 +1,23 @@ +python + def print_type(val): + print type(val), val + +eval print_type(true) +eval print_type([2010/08/10]) +eval print_type(10) +eval print_type($10.00) +eval print_type($10.00 + CAD 30) +eval print_type("Hello!") +eval print_type(/Hello!/) +;eval print_type((1, 2, 3)) + +test reg + True + 2010-08-10 + 10 + $10.00 + $10.00 +CAD 30 + Hello! + Hello! +end test -- cgit v1.2.3 From c8c2a17e282c2cbf3c0edb1b756e16be58328331 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 3 Mar 2012 01:17:21 -0600 Subject: Fixed invocation of lambda expressions foo = x, y, z -> print(x, y, z) foo(1, 2, 3) However, this still does not work: (x, y, z -> print(x, y, z))(1, 2, 3) --- src/op.cc | 2 +- src/scope.h | 24 +++++++++++++++++++----- test/baseline/cmd-script.test | 5 +++++ tools/proof | 4 ++-- 4 files changed, 27 insertions(+), 8 deletions(-) (limited to 'test/baseline/cmd-script.test') diff --git a/src/op.cc b/src/op.cc index 8e9df812..bd2cc32f 100644 --- a/src/op.cc +++ b/src/op.cc @@ -288,7 +288,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) } case O_LAMBDA: { - call_scope_t& call_args(downcast(scope)); + call_scope_t& call_args(find_scope(scope, true)); std::size_t args_count(call_args.size()); std::size_t args_index(0); symbol_scope_t call_scope(call_args); diff --git a/src/scope.h b/src/scope.h index 90f9c37a..785ce284 100644 --- a/src/scope.h +++ b/src/scope.h @@ -227,15 +227,17 @@ private: }; template -T * search_scope(scope_t * ptr) +T * search_scope(scope_t * ptr, bool prefer_direct_parents = false) { if (T * sought = dynamic_cast(ptr)) return sought; if (bind_scope_t * scope = dynamic_cast(ptr)) { - if (T * sought = search_scope(&scope->grandchild)) + if (T * sought = search_scope(prefer_direct_parents ? + scope->parent : &scope->grandchild)) return sought; - return search_scope(scope->parent); + return search_scope(prefer_direct_parents ? + &scope->grandchild : scope->parent); } else if (child_scope_t * child_scope = dynamic_cast(ptr)) { return search_scope(child_scope->parent); @@ -244,9 +246,21 @@ T * search_scope(scope_t * ptr) } template -inline T& find_scope(child_scope_t& scope, bool skip_this = true) +inline T& find_scope(child_scope_t& scope, bool skip_this = true, + bool prefer_direct_parents = false) { - if (T * sought = search_scope(skip_this ? scope.parent : &scope)) + if (T * sought = search_scope(skip_this ? scope.parent : &scope, + prefer_direct_parents)) + return *sought; + + throw_(std::runtime_error, _("Could not find scope")); + return reinterpret_cast(scope); // never executed +} + +template +inline T& find_scope(scope_t& scope, bool prefer_direct_parents = false) +{ + if (T * sought = search_scope(&scope, prefer_direct_parents)) return *sought; throw_(std::runtime_error, _("Could not find scope")); diff --git a/test/baseline/cmd-script.test b/test/baseline/cmd-script.test index 12e9c868..ce920ebb 100644 --- a/test/baseline/cmd-script.test +++ b/test/baseline/cmd-script.test @@ -10,3 +10,8 @@ end test test eval 'total_expr=$100;amount_expr=$15;x=total_expr;x=x/5;x=amount_expr-x*5;x' $-85 end test + +test eval 'foo = x, y, z -> print(x, y, z); foo(1, 2, 3)' +123 +1 +end test diff --git a/tools/proof b/tools/proof index f37c1488..3b997143 100755 --- a/tools/proof +++ b/tools/proof @@ -15,12 +15,12 @@ ledger_proof() { exit 0 fi - rm -fr $DEST/ledger-proof + sudo rm -fr $DEST/ledger-proof time nice -n 20 \ ./acprep --debug --enable-doxygen --universal --clang -j16 proof 2>&1 | \ tee $LOGDIR/ledger-proof.log - rm -fr $DEST/ledger-proof + sudo rm -fr $DEST/ledger-proof time nice -n 20 \ ./acprep --debug --enable-doxygen --universal --python --clang -j16 proof 2>&1 | \ tee -a $LOGDIR/ledger-proof.log -- cgit v1.2.3 From 4d011434003262c9a49b0b4636b2ac479a84e058 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 8 Mar 2012 00:44:27 -0600 Subject: Many improvements to lambdas and function calling --- src/op.cc | 340 ++++++++++++++++++++++++++---------------- src/op.h | 8 + src/parser.cc | 3 - test/baseline/cmd-script.test | 4 + 4 files changed, 223 insertions(+), 132 deletions(-) (limited to 'test/baseline/cmd-script.test') diff --git a/src/op.cc b/src/op.cc index 6a1a8f54..0773c093 100644 --- a/src/op.cc +++ b/src/op.cc @@ -178,7 +178,10 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth, ptr_op_t varname = sym->kind == O_CONS ? sym->left() : sym; if (! varname->is_ident()) { - throw_(calc_error, _("Invalid function or lambda parameter")); + std::ostringstream buf; + varname->dump(buf, 0); + throw_(calc_error, + _("Invalid function or lambda parameter: %1") << buf.str()); } else { DEBUG("expr.compile", "Defining function parameter " << varname->as_ident()); @@ -224,6 +227,23 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth, return result; } +namespace { + expr_t::ptr_op_t lookup_ident(expr_t::ptr_op_t op, scope_t& scope) + { + expr_t::ptr_op_t def = op->left(); + + // If no definition was pre-compiled for this identifier, look it up + // in the current scope. + if (! def || def->kind == expr_t::op_t::PLUG) { + DEBUG("scope.symbols", "Looking for IDENT '" << op->as_ident() << "'"); + def = scope.lookup(symbol_t::FUNCTION, op->as_ident()); + } + if (! def) + throw_(calc_error, _("Unknown identifier '%1'") << op->as_ident()); + return def; + } +} + value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) { try { @@ -248,23 +268,14 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) result = NULL_VALUE; break; - case IDENT: { - ptr_op_t definition = left(); - // If no definition was pre-compiled for this identifier, look it up - // in the current scope. - if (! definition || definition->kind == PLUG) { - DEBUG("scope.symbols", "Looking for IDENT '" << as_ident() << "'"); - definition = scope.lookup(symbol_t::FUNCTION, as_ident()); + case IDENT: + if (ptr_op_t definition = lookup_ident(this, scope)) { + // Evaluating an identifier is the same as calling its definition + // directly + result = definition->calc(scope, locus, depth + 1); + check_type_context(scope, result); } - if (! definition) - throw_(calc_error, _("Unknown identifier '%1'") << as_ident()); - - // Evaluating an identifier is the same as calling its definition - // directly - result = definition->calc(scope, locus, depth + 1); - check_type_context(scope, result); break; - } case FUNCTION: { // Evaluating a FUNCTION is the same as calling it directly; this @@ -302,81 +313,14 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) break; } - case O_CALL: { - ptr_op_t func = left(); - string name; - - if (func->is_ident()) { - name = func->as_ident(); - func = func->left(); - if (! func) - func = scope.lookup(symbol_t::FUNCTION, name); - if (! func) - throw_(calc_error, _("Calling unknown function '%1'") << name); - } else { - name = ""; - } - - call_scope_t call_args(scope, locus, depth + 1); - if (has_right()) - call_args.set_args(split_cons_expr(right())); - - try { - if (func->is_function()) - result = func->as_function()(call_args); - else - result = func->calc(call_args, locus, depth + 1); - } - catch (const std::exception&) { - add_error_context(_("While calling function '%1':" << name)); - throw; - } - + case O_CALL: + result = calc_call(scope, locus, depth); check_type_context(scope, result); break; - } - - case O_LAMBDA: { - call_scope_t& call_args(find_scope(scope, true)); - std::size_t args_count(call_args.size()); - std::size_t args_index(0); - symbol_scope_t args_scope(*scope_t::empty_scope); - - for (ptr_op_t sym = left(); - sym; - sym = sym->has_right() ? sym->right() : NULL) { - ptr_op_t varname = sym->kind == O_CONS ? sym->left() : sym; - if (! varname->is_ident()) { - throw_(calc_error, _("Invalid function definition")); - } - else if (args_index == args_count) { - DEBUG("expr.compile", "Defining function argument as null: " - << varname->as_ident()); - args_scope.define(symbol_t::FUNCTION, varname->as_ident(), - wrap_value(NULL_VALUE)); - } - else { - DEBUG("expr.compile", "Defining function argument from call_args: " - << varname->as_ident()); - args_scope.define(symbol_t::FUNCTION, varname->as_ident(), - wrap_value(call_args[args_index++])); - } - } - if (args_index < args_count) - throw_(calc_error, - _("Too few arguments in function call (saw %1, wanted %2)") - << args_count << args_index); - - if (right()->is_scope()) { - bind_scope_t outer_scope(scope, *right()->as_scope()); - bind_scope_t bound_scope(outer_scope, args_scope); - result = right()->left()->calc(bound_scope, locus, depth + 1); - } else { - result = right()->calc(args_scope, locus, depth + 1); - } + case O_LAMBDA: + result = expr_value(this); break; - } case O_MATCH: result = (right()->calc(scope, locus, depth + 1).as_mask() @@ -457,51 +401,12 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) break; case O_CONS: - result = left()->calc(scope, locus, depth + 1); - if (has_right()) { - value_t temp; - temp.push_back(result); - - ptr_op_t next = right(); - while (next) { - ptr_op_t value_op; - if (next->kind == O_CONS) { - value_op = next->left(); - next = next->has_right() ? next->right() : NULL; - } else { - value_op = next; - next = NULL; - } - temp.push_back(value_op->calc(scope, locus, depth + 1)); - } - result = temp; - } + result = calc_cons(scope, locus, depth); break; - case O_SEQ: { - // An O_SEQ is very similar to an O_CONS except that only the last - // result value in the series is kept. O_CONS builds up a list. - // - // Another feature of O_SEQ is that it pushes a new symbol scope - // onto the stack. We evaluate the left side here to catch any - // side-effects, such as definitions in the case of 'x = 1; x'. - result = left()->calc(scope, locus, depth + 1); - if (has_right()) { - ptr_op_t next = right(); - while (next) { - ptr_op_t value_op; - if (next->kind == O_SEQ) { - value_op = next->left(); - next = next->right(); - } else { - value_op = next; - next = NULL; - } - result = value_op->calc(scope, locus, depth + 1); - } - } + case O_SEQ: + result = calc_seq(scope, locus, depth); break; - } default: throw_(calc_error, _("Unexpected expr node '%1'") << op_context(this)); @@ -527,6 +432,183 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) } } +namespace { + expr_t::ptr_op_t find_definition(expr_t::ptr_op_t op, scope_t& scope, + expr_t::ptr_op_t * locus, const int depth, + int recursion_depth = 0) + { + // If the object we are apply call notation to is a FUNCTION value + // or a O_LAMBDA expression, then this is the object we want to + // call. + if (op->is_function() || op->kind == expr_t::op_t::O_LAMBDA) + return op; + + if (recursion_depth > 256) + throw_(value_error, _("Function recursion_depth too deep (> 256)")); + + // If it's an identifier, look up its definition and see if it's a + // function. + if (op->is_ident()) + return find_definition(lookup_ident(op, scope), scope, + locus, depth, recursion_depth + 1); + + // Value objects might be callable if they contain an expression. + if (op->is_value()) { + value_t def(op->as_value()); + if (is_expr(def)) + return find_definition(as_expr(def), scope, locus, depth, + recursion_depth + 1); + else + throw_(value_error, _("Cannot call %1 as a function") << def.label()); + } + + // Resolve ordinary expressions. + return find_definition(expr_t::op_t::wrap_value(op->calc(scope, locus, + depth + 1)), + scope, locus, depth + 1, recursion_depth + 1); + } + + value_t call_lambda(expr_t::ptr_op_t func, scope_t& scope, + call_scope_t& call_args, expr_t::ptr_op_t * locus, + const int depth) + { + std::size_t args_index(0); + std::size_t args_count(call_args.size()); + + symbol_scope_t args_scope(*scope_t::empty_scope); + + for (expr_t::ptr_op_t sym = func->left(); + sym; + sym = sym->has_right() ? sym->right() : NULL) { + expr_t::ptr_op_t varname = + sym->kind == expr_t::op_t::O_CONS ? sym->left() : sym; + if (! varname->is_ident()) { + throw_(calc_error, _("Invalid function definition")); + } + else if (args_index == args_count) { + DEBUG("expr.calc", "Defining function argument as null: " + << varname->as_ident()); + args_scope.define(symbol_t::FUNCTION, varname->as_ident(), + expr_t::op_t::wrap_value(NULL_VALUE)); + } + else { + DEBUG("expr.calc", "Defining function argument from call_args: " + << varname->as_ident()); + args_scope.define(symbol_t::FUNCTION, varname->as_ident(), + expr_t::op_t::wrap_value(call_args[args_index++])); + } + } + + if (args_index < args_count) + throw_(calc_error, + _("Too few arguments in function call (saw %1, wanted %2)") + << args_count << args_index); + + if (func->right()->is_scope()) { + bind_scope_t outer_scope(scope, *func->right()->as_scope()); + bind_scope_t bound_scope(outer_scope, args_scope); + + return func->right()->left()->calc(bound_scope, locus, depth + 1); + } else { + return func->right()->calc(args_scope, locus, depth + 1); + } + } +} + + +value_t expr_t::op_t::call(const value_t& args, scope_t& scope, + ptr_op_t * locus, const int depth) +{ + call_scope_t call_args(scope, locus, depth + 1); + call_args.set_args(args); + + if (is_function()) + return as_function()(call_args); + else if (kind == O_LAMBDA) + return call_lambda(this, scope, call_args, locus, depth); + else + return find_definition(this, scope, locus, depth) + ->calc(call_args, locus, depth); +} + +value_t expr_t::op_t::calc_call(scope_t& scope, ptr_op_t * locus, + const int depth) +{ + ptr_op_t func = left(); + string name = func->is_ident() ? func->as_ident() : ""; + + try { + func = find_definition(func, scope, locus, depth); + + call_scope_t call_args(scope, locus, depth + 1); + if (has_right()) + call_args.set_args(split_cons_expr(right())); + + if (func->is_function()) { + return func->as_function()(call_args); + } else { + assert(func->kind == O_LAMBDA); + return call_lambda(func, scope, call_args, locus, depth); + } + } + catch (const std::exception&) { + add_error_context(_("While calling function '%1':" << name)); + throw; + } +} + +value_t expr_t::op_t::calc_cons(scope_t& scope, ptr_op_t * locus, + const int depth) +{ + value_t result = left()->calc(scope, locus, depth + 1); + if (has_right()) { + value_t temp; + temp.push_back(result); + + ptr_op_t next = right(); + while (next) { + ptr_op_t value_op; + if (next->kind == O_CONS) { + value_op = next->left(); + next = next->has_right() ? next->right() : NULL; + } else { + value_op = next; + next = NULL; + } + temp.push_back(value_op->calc(scope, locus, depth + 1)); + } + result = temp; + } + return result; +} + +value_t expr_t::op_t::calc_seq(scope_t& scope, ptr_op_t * locus, + const int depth) +{ + // An O_SEQ is very similar to an O_CONS except that only the last + // result value in the series is kept. O_CONS builds up a list. + // + // Another feature of O_SEQ is that it pushes a new symbol scope onto + // the stack. We evaluate the left side here to catch any + // side-effects, such as definitions in the case of 'x = 1; x'. + value_t result = left()->calc(scope, locus, depth + 1); + if (has_right()) { + ptr_op_t next = right(); + while (next) { + ptr_op_t value_op; + if (next->kind == O_SEQ) { + value_op = next->left(); + next = next->right(); + } else { + value_op = next; + next = NULL; + } + result = value_op->calc(scope, locus, depth + 1); + } + } + return result; +} + namespace { bool print_cons(std::ostream& out, const expr_t::const_ptr_op_t op, const expr_t::op_t::context_t& context) diff --git a/src/op.h b/src/op.h index c93f218b..03fcf816 100644 --- a/src/op.h +++ b/src/op.h @@ -280,6 +280,9 @@ public: value_t calc(scope_t& scope, ptr_op_t * locus = NULL, const int depth = 0); + value_t call(const value_t& args, scope_t& scope, + ptr_op_t * locus = NULL, const int depth = 0); + struct context_t { ptr_op_t expr_op; @@ -307,6 +310,11 @@ public: static ptr_op_t wrap_functor(expr_t::func_t fobj); static ptr_op_t wrap_scope(shared_ptr sobj); +private: + value_t calc_call(scope_t& scope, ptr_op_t * locus, const int depth); + value_t calc_cons(scope_t& scope, ptr_op_t * locus, const int depth); + value_t calc_seq(scope_t& scope, ptr_op_t * locus, const int depth); + #if defined(HAVE_BOOST_SERIALIZATION) private: /** Serialization. */ diff --git a/src/parser.cc b/src/parser.cc index ce70a49e..360ac93d 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -87,9 +87,6 @@ expr_t::parser_t::parse_call_expr(std::istream& in, node->set_left(prev); push_token(tok); // let the parser see the '(' again node->set_right(parse_value_expr(in, tflags.plus_flags(PARSE_SINGLE))); - if (! node->right()) - throw_(parse_error, - _("%1 operator not followed by argument") << tok.symbol); } else { push_token(tok); break; diff --git a/test/baseline/cmd-script.test b/test/baseline/cmd-script.test index ce920ebb..b33dd82d 100644 --- a/test/baseline/cmd-script.test +++ b/test/baseline/cmd-script.test @@ -15,3 +15,7 @@ test eval 'foo = x, y, z -> print(x, y, z); foo(1, 2, 3)' 123 1 end test + +test eval 'foo(x,y)=y(1, 2, 3);foo(amount_expr, (s,d,t -> t))' +3 +end test -- cgit v1.2.3