diff options
-rw-r--r-- | src/compare.cc | 2 | ||||
-rw-r--r-- | src/format.cc | 2 | ||||
-rw-r--r-- | src/op.cc | 210 | ||||
-rw-r--r-- | src/op.h | 51 | ||||
-rw-r--r-- | src/parser.cc | 33 | ||||
-rw-r--r-- | src/scope.cc | 8 | ||||
-rw-r--r-- | src/scope.h | 2 | ||||
-rw-r--r-- | src/value.cc | 2 |
8 files changed, 172 insertions, 138 deletions
diff --git a/src/compare.cc b/src/compare.cc index 12114c7d..cdc96a86 100644 --- a/src/compare.cc +++ b/src/compare.cc @@ -44,7 +44,7 @@ void push_sort_value(std::list<sort_value_t>& sort_values, if (node->kind == expr_t::op_t::O_CONS) { while (node && node->kind == expr_t::op_t::O_CONS) { push_sort_value(sort_values, node->left(), scope); - node = node->right(); + node = node->has_right() ? node->right() : NULL; } } else { bool inverted = false; diff --git a/src/format.cc b/src/format.cc index 93e7ea08..65c06488 100644 --- a/src/format.cc +++ b/src/format.cc @@ -225,7 +225,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt, expr_t::ptr_op_t colorize_op; if (op->kind == expr_t::op_t::O_CONS) { amount_op = op->left(); - colorize_op = op->right(); + colorize_op = op->has_right() ? op->right() : NULL; } else { amount_op = op; } @@ -50,7 +50,7 @@ namespace { expr_t::ptr_op_t value_op; if (next->kind == expr_t::op_t::O_CONS) { value_op = next->left(); - next = next->right(); + next = next->has_right() ? next->right() : NULL; } else { value_op = next; next = NULL; @@ -78,10 +78,12 @@ namespace { expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) { + scope_t * scope_ptr = &scope; + if (is_ident()) { - DEBUG("expr.compile", "lookup: " << as_ident()); + DEBUG("expr.compile", "Lookup: " << as_ident()); - if (ptr_op_t def = scope.lookup(symbol_t::FUNCTION, as_ident())) { + if (ptr_op_t def = scope_ptr->lookup(symbol_t::FUNCTION, as_ident())) { // Identifier references are first looked up at the point of // definition, and then at the point of every use if they could // not be found there. @@ -98,36 +100,40 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) } return this; } - - if (kind < TERMINALS) + else if (is_scope()) { + shared_ptr<scope_t> subscope(new symbol_scope_t(scope)); + set_scope(subscope); + scope_ptr = subscope.get(); + } + else if (kind < TERMINALS) { return this; - - if (kind == O_DEFINE) { + } + else if (kind == O_DEFINE) { switch (left()->kind) { case IDENT: - scope.define(symbol_t::FUNCTION, left()->as_ident(), right()); + scope_ptr->define(symbol_t::FUNCTION, left()->as_ident(), right()); break; + case O_CALL: if (left()->left()->is_ident()) { ptr_op_t node(new op_t(op_t::O_LAMBDA)); node->set_left(left()->right()); node->set_right(right()); - - scope.define(symbol_t::FUNCTION, left()->left()->as_ident(), node); - } else { - throw_(compile_error, _("Invalid function definition")); + scope_ptr->define(symbol_t::FUNCTION, left()->left()->as_ident(), + node); + break; } - break; + // fall through... + default: throw_(compile_error, _("Invalid function definition")); } - return wrap_value(value_t()); } - ptr_op_t lhs(left()->compile(scope, depth)); + ptr_op_t lhs(left()->compile(*scope_ptr, depth)); ptr_op_t rhs(kind > UNARY_OPERATORS && has_right() ? (kind == O_LOOKUP ? right() : - right()->compile(scope, depth)) : NULL); + right()->compile(*scope_ptr, depth)) : NULL); if (lhs == left() && (! rhs || rhs == right())) return this; @@ -136,25 +142,22 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) // Reduce constants immediately if possible if ((! lhs || lhs->is_value()) && (! rhs || rhs->is_value())) - return wrap_value(intermediate->calc(scope, NULL, depth)); + return wrap_value(intermediate->calc(*scope_ptr, NULL, depth + 1)); return intermediate; } value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) { -#if defined(DEBUG_ON) - bool skip_debug = false; -#endif try { value_t result; #if defined(DEBUG_ON) - if (! skip_debug && SHOW_DEBUG("expr.calc")) { + if (SHOW_DEBUG("expr.calc")) { for (int i = 0; i < depth; i++) ledger::_log_buffer << '.'; - ledger::_log_buffer << op_context(this) << " => ..."; + ledger::_log_buffer << op_context(this) << " => ..."; DEBUG("expr.calc", ""); } #endif @@ -165,77 +168,47 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) break; case O_DEFINE: - //result = left()->calc(scope, locus, depth + 1); 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) { - // If no definition was pre-compiled for this identifier, look it - // up in the current scope. + DEBUG("scope.symbols", "Looking for IDENT '" << as_ident() << "'"); definition = scope.lookup(symbol_t::FUNCTION, as_ident()); } if (! definition) throw_(calc_error, _("Unknown identifier '%1'") << as_ident()); // Evaluating an identifier is the same as calling its definition - // directly, so we create an empty call_scope_t to reflect the scope for - // this implicit call. - call_scope_t call_args(scope, locus, depth); - result = definition->compile(call_args, depth + 1) - ->calc(call_args, locus, depth + 1); + // 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 happens - // when certain functions-that-look-like-variables (such as "amount") are - // resolved. - call_scope_t call_args(scope, locus, depth); + // Evaluating a FUNCTION is the same as calling it directly; this + // happens when certain functions-that-look-like-variables (such as + // "amount") are resolved. + call_scope_t call_args(scope, locus, depth + 1); result = as_function()(call_args); check_type_context(scope, result); -#if defined(DEBUG_ON) - skip_debug = true; -#endif break; } - case O_LAMBDA: { - call_scope_t& call_args(downcast<call_scope_t>(scope)); - std::size_t args_count(call_args.size()); - std::size_t args_index(0); - symbol_scope_t call_scope(call_args); - ptr_op_t sym(left()); - - for (; sym; sym = sym->has_right() ? sym->right() : NULL) { - ptr_op_t varname = sym; - if (sym->kind == O_CONS) - varname = sym->left(); - - if (! varname->is_ident()) { - throw_(calc_error, _("Invalid function definition")); - } - else if (args_index == args_count) { - call_scope.define(symbol_t::FUNCTION, varname->as_ident(), - wrap_value(NULL_VALUE)); - } - else { - DEBUG("expr.compile", - "Defining function parameter " << varname->as_ident()); - call_scope.define(symbol_t::FUNCTION, varname->as_ident(), - wrap_value(call_args[args_index++])); - } + case SCOPE: + assert(! is_scope_unset()); + if (is_scope_unset()) { + symbol_scope_t subscope(scope); + result = left()->calc(subscope, locus, depth + 1); + } else { + bind_scope_t bound_scope(scope, *as_scope()); + result = left()->calc(bound_scope, locus, depth + 1); } - - if (args_index < args_count) - throw_(calc_error, - _("Too few arguments in function call (saw %1)") << args_count); - - result = right()->calc(call_scope, locus, depth + 1); break; - } case O_LOOKUP: { context_scope_t context_scope(scope, value_t::SCOPE); @@ -253,10 +226,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) } case O_CALL: { - call_scope_t call_args(scope, locus, depth); - if (has_right()) - call_args.set_args(split_cons_expr(right())); - ptr_op_t func = left(); const string& name(func->as_ident()); @@ -266,20 +235,9 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) if (! func) throw_(calc_error, _("Calling unknown function '%1'") << name); -#if defined(DEBUG_ON) - if (! skip_debug && SHOW_DEBUG("expr.calc")) { - for (int i = 0; i < depth; i++) - ledger::_log_buffer << '.'; - ledger::_log_buffer << " args: "; - if (call_args.args.is_sequence()) { - foreach (value_t& arg, call_args) - ledger::_log_buffer << arg << " "; - } else { - ledger::_log_buffer << call_args.args[0] << " "; - } - DEBUG("expr.calc", ""); - } -#endif + call_scope_t call_args(scope, locus, depth + 1); + if (has_right()) + call_args.set_args(split_cons_expr(right())); if (func->is_function()) result = func->as_function()(call_args); @@ -290,6 +248,39 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) break; } + case O_LAMBDA: { + call_scope_t& call_args(downcast<call_scope_t>(scope)); + std::size_t args_count(call_args.size()); + std::size_t args_index(0); + symbol_scope_t call_scope(call_args); + + 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) { + call_scope.define(symbol_t::FUNCTION, varname->as_ident(), + wrap_value(NULL_VALUE)); + } + else { + DEBUG("expr.compile", + "Defining function parameter " << varname->as_ident()); + call_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)") << args_count); + + result = right()->calc(call_scope, locus, depth + 1); + break; + } + case O_MATCH: result = (right()->calc(scope, locus, depth + 1).as_mask() .match(left()->calc(scope, locus, depth + 1).to_string())); @@ -358,7 +349,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) case O_QUERY: assert(right()); assert(right()->kind == O_COLON); - if (value_t temp = left()->calc(scope, locus, depth + 1)) result = right()->left()->calc(scope, locus, depth + 1); else @@ -371,8 +361,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) case O_CONS: result = left()->calc(scope, locus, depth + 1); - DEBUG("op.cons", "car = " << result); - if (has_right()) { value_t temp; temp.push_back(result); @@ -382,28 +370,25 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) ptr_op_t value_op; if (next->kind == O_CONS) { value_op = next->left(); - next = next->right(); + next = next->has_right() ? next->right() : NULL; } else { value_op = next; next = NULL; } temp.push_back(value_op->calc(scope, locus, depth + 1)); - DEBUG("op.cons", "temp now = " << temp); } result = temp; } break; case O_SEQ: { - symbol_scope_t seq_scope(scope); - - // 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. + // 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. - result = left()->calc(seq_scope, locus, depth + 1); - + // 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) { @@ -415,7 +400,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) value_op = next; next = NULL; } - result = value_op->calc(seq_scope, locus, depth + 1); + result = value_op->calc(scope, locus, depth + 1); } } break; @@ -426,7 +411,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) } #if defined(DEBUG_ON) - if (! skip_debug && SHOW_DEBUG("expr.calc")) { + if (SHOW_DEBUG("expr.calc")) { for (int i = 0; i < depth; i++) ledger::_log_buffer << '.'; ledger::_log_buffer << op_context(this) << " => "; @@ -476,9 +461,8 @@ namespace { if (op->has_right()) { out << "; "; - - if (op->right()->kind == expr_t::op_t::O_CONS) - found = print_cons(out, op->right(), context); + if (op->right()->kind == expr_t::op_t::O_SEQ) + found = print_seq(out, op->right(), context); else if (op->right()->print(out, context)) found = true; } @@ -515,6 +499,11 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const out << "<FUNCTION>"; break; + case SCOPE: + if (left() && left()->print(out, context)) + found = true; + break; + case O_NOT: out << "! "; if (left() && left()->print(out, context)) @@ -625,7 +614,6 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const case O_CONS: found = print_cons(out, this, context); break; - case O_SEQ: found = print_seq(out, this, context); break; @@ -726,6 +714,14 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const out << "FUNCTION"; break; + case SCOPE: + out << "SCOPE: "; + if (is_scope_unset()) + out << "null"; + else + out << as_scope().get(); + break; + case O_DEFINE: out << "O_DEFINE"; break; case O_LOOKUP: out << "O_LOOKUP"; break; case O_LAMBDA: out << "O_LAMBDA"; break; @@ -765,7 +761,7 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const // An identifier is a special non-terminal, in that its left() can // hold the compiled definition of the identifier. - if (kind > TERMINALS || is_ident()) { + if (kind > TERMINALS || is_scope()) { if (left()) { left()->dump(out, depth + 1); if (kind > UNARY_OPERATORS && has_right()) @@ -58,10 +58,12 @@ private: mutable short refc; ptr_op_t left_; - variant<ptr_op_t, // used by all binary operators + variant<boost::blank, + ptr_op_t, // used by all binary operators value_t, // used by constant VALUE string, // used by constant IDENT - expr_t::func_t // used by terminal FUNCTION + expr_t::func_t, // used by terminal FUNCTION + shared_ptr<scope_t> // used by terminal SCOPE > data; public: @@ -73,6 +75,7 @@ public: CONSTANTS, FUNCTION, + SCOPE, TERMINALS, @@ -173,7 +176,7 @@ public: return kind == FUNCTION; } expr_t::func_t& as_function_lval() { - assert(kind == FUNCTION); + assert(is_function()); return boost::get<expr_t::func_t>(data); } const expr_t::func_t& as_function() const { @@ -183,21 +186,41 @@ public: data = val; } + bool is_scope() const { + return kind == SCOPE; + } + bool is_scope_unset() const { + return data.which() == 0; + } + shared_ptr<scope_t> as_scope_lval() { + assert(is_scope()); + return boost::get<shared_ptr<scope_t> >(data); + } + const shared_ptr<scope_t> as_scope() const { + return const_cast<op_t *>(this)->as_scope_lval(); + } + void set_scope(shared_ptr<scope_t> val) { + data = val; + } + + // These three functions must use 'kind == IDENT' rather than + // 'is_ident()', because they are called before the `data' member gets + // set, which is_ident() tests. ptr_op_t& left() { - assert(kind > TERMINALS || kind == IDENT); + assert(kind > TERMINALS || kind == IDENT || is_scope()); return left_; } const ptr_op_t& left() const { - assert(kind > TERMINALS || kind == IDENT); + assert(kind > TERMINALS || kind == IDENT || is_scope()); return left_; } void set_left(const ptr_op_t& expr) { - assert(kind > TERMINALS || kind == IDENT); + assert(kind > TERMINALS || kind == IDENT || is_scope()); left_ = expr; } ptr_op_t& as_op_lval() { - assert(kind > TERMINALS || kind == IDENT); + assert(kind > TERMINALS || is_ident()); return boost::get<ptr_op_t>(data); } const ptr_op_t& as_op() const { @@ -219,7 +242,7 @@ public: bool has_right() const { if (kind < TERMINALS) return false; - return as_op(); + return data.which() != 0 && as_op(); } private: @@ -284,6 +307,7 @@ public: static ptr_op_t wrap_value(const value_t& val); static ptr_op_t wrap_functor(expr_t::func_t fobj); + static ptr_op_t wrap_scope(shared_ptr<scope_t> sobj); #if defined(HAVE_BOOST_SERIALIZATION) private: @@ -295,13 +319,13 @@ private: void serialize(Archive& ar, const unsigned int /* version */) { ar & refc; ar & kind; - if (Archive::is_loading::value || ! left_ || left_->kind != FUNCTION) { + if (Archive::is_loading::value || ! left_ || ! left_->is_function()) { ar & left_; } else { ptr_op_t temp_op; ar & temp_op; } - if (Archive::is_loading::value || kind == VALUE || kind == IDENT || + if (Archive::is_loading::value || is_value() || is_ident() || (kind > UNARY_OPERATORS && (! has_right() || ! right()->is_function()))) { ar & data; @@ -335,6 +359,13 @@ expr_t::op_t::wrap_functor(expr_t::func_t fobj) { return temp; } +inline expr_t::ptr_op_t +expr_t::op_t::wrap_scope(shared_ptr<scope_t> sobj) { + ptr_op_t temp(new op_t(op_t::SCOPE)); + temp->set_scope(sobj); + return temp; +} + #define MAKE_FUNCTOR(x) expr_t::op_t::wrap_functor(bind(&x, this, _1)) #define WRAP_FUNCTOR(x) expr_t::op_t::wrap_functor(x) diff --git a/src/parser.cc b/src/parser.cc index a18fa552..ad621106 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -434,7 +434,6 @@ expr_t::parser_t::parse_comma_expr(std::istream& in, ptr_op_t prev(node); node = new op_t(op_t::O_CONS); node->set_left(prev); - next = node; } @@ -493,7 +492,9 @@ expr_t::parser_t::parse_assign_expr(std::istream& in, ptr_op_t prev(node); node = new op_t(op_t::O_DEFINE); node->set_left(prev); - node->set_right(parse_lambda_expr(in, tflags)); + ptr_op_t scope(new op_t(op_t::SCOPE)); + scope->set_left(parse_lambda_expr(in, tflags)); + node->set_right(scope); } else { push_token(tok); } @@ -509,24 +510,24 @@ expr_t::parser_t::parse_value_expr(std::istream& in, ptr_op_t node(parse_assign_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { - ptr_op_t next; + ptr_op_t chain; while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); - if (tok.kind == token_t::SEMI) { - if (! next) { - ptr_op_t prev(node); - node = new op_t(op_t::O_SEQ); - node->set_left(prev); - - next = node; + ptr_op_t seq(new op_t(op_t::O_SEQ)); + if (! chain) { + seq->set_left(node); + ptr_op_t scope(new op_t(op_t::SCOPE)); + scope->set_left(seq); + node = scope; + } else { + seq->set_left(chain->right()); + ptr_op_t scope(new op_t(op_t::SCOPE)); + scope->set_left(seq); + chain->set_right(scope); } - - ptr_op_t chain(new op_t(op_t::O_SEQ)); - chain->set_left(parse_assign_expr(in, tflags)); - - next->set_right(chain); - next = chain; + seq->set_right(parse_assign_expr(in, tflags)); + chain = seq; } else { push_token(tok); break; diff --git a/src/scope.cc b/src/scope.cc index e18b5a0a..95347c8d 100644 --- a/src/scope.cc +++ b/src/scope.cc @@ -40,7 +40,8 @@ scope_t * scope_t::default_scope = NULL; void symbol_scope_t::define(const symbol_t::kind_t kind, const string& name, expr_t::ptr_op_t def) { - DEBUG("scope.symbols", "Defining '" << name << "' = " << def); + DEBUG("scope.symbols", + "Defining '" << name << "' = " << def << " in " << this); if (! symbols) symbols = symbol_map(); @@ -64,9 +65,12 @@ expr_t::ptr_op_t symbol_scope_t::lookup(const symbol_t::kind_t kind, const string& name) { if (symbols) { + DEBUG("scope.symbols", "Looking for '" << name << "' in " << this); symbol_map::const_iterator i = symbols->find(symbol_t(kind, name)); - if (i != symbols->end()) + if (i != symbols->end()) { + DEBUG("scope.symbols", "Found '" << name << "' in " << this); return (*i).second; + } } return child_scope_t::lookup(kind, name); } diff --git a/src/scope.h b/src/scope.h index a7b3c5cb..fbc1a889 100644 --- a/src/scope.h +++ b/src/scope.h @@ -188,6 +188,8 @@ public: scope_t& _grandchild) : child_scope_t(_parent), grandchild(_grandchild) { TRACE_CTOR(bind_scope_t, "scope_t&, scope_t&"); + DEBUG("scope.symbols", + "Binding scope " << &_parent << " with " << &_grandchild); } virtual ~bind_scope_t() { TRACE_DTOR(bind_scope_t); diff --git a/src/value.cc b/src/value.cc index c62e6f32..f54c06a5 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1727,7 +1727,7 @@ void value_t::print(std::ostream& _out, switch (type()) { case VOID: - out << ""; + out << "(null)"; break; case BOOLEAN: |