diff options
-rw-r--r-- | src/op.cc | 90 | ||||
-rw-r--r-- | src/op.h | 1 | ||||
-rw-r--r-- | src/parser.cc | 3 | ||||
-rw-r--r-- | src/report.cc | 5 | ||||
-rw-r--r-- | src/report.h | 4 | ||||
-rw-r--r-- | src/scope.cc | 5 | ||||
-rw-r--r-- | src/scope.h | 17 | ||||
-rw-r--r-- | src/session.cc | 3 | ||||
-rw-r--r-- | src/session.h | 2 | ||||
-rw-r--r-- | src/token.cc | 9 | ||||
-rw-r--r-- | src/token.h | 1 | ||||
-rw-r--r-- | src/value.cc | 2 |
12 files changed, 117 insertions, 25 deletions
@@ -36,7 +36,7 @@ namespace ledger { expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope) { - if (kind == IDENT) { + if (is_ident()) { DEBUG("expr.compile", "Looking up identifier '" << as_ident() << "'"); if (ptr_op_t def = scope.lookup(as_ident())) { @@ -58,6 +58,23 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope) if (kind < TERMINALS) return this; + if (kind == O_DEFINE) { + switch (left()->kind) { + case IDENT: + scope.define(left()->as_ident(), right()); + break; + case O_CALL: + if (left()->left()->is_ident()) + scope.define(left()->left()->as_ident(), this); + else + throw_(compile_error, "Invalid function definition"); + break; + default: + throw_(compile_error, "Invalid function definition"); + } + return wrap_value(value_t()); + } + ptr_op_t lhs(left()->compile(scope)); ptr_op_t rhs(kind > UNARY_OPERATORS && has_right() ? (kind == O_LOOKUP ? right() : right()->compile(scope)) : NULL); @@ -68,7 +85,7 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope) ptr_op_t intermediate(copy(lhs, rhs)); // Reduce constants immediately if possible - if (lhs->is_value() && (! rhs || rhs->is_value())) + if ((! lhs || lhs->is_value()) && (! rhs || rhs->is_value())) return wrap_value(intermediate->calc(scope)); return intermediate; @@ -87,11 +104,17 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * context) result = as_value(); break; - case IDENT: + case IDENT: { if (! left()) throw_(calc_error, "Unknown identifier '" << as_ident() << "'"); - result = left()->calc(scope, context); + + // 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); + result = left()->calc(call_args, context); break; + } case FUNCTION: { // Evaluating a FUNCTION is the same as calling it directly; this happens @@ -102,9 +125,41 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * context) break; } + case O_DEFINE: { + symbol_scope_t local_scope; + call_scope_t& call_args(downcast<call_scope_t>(scope)); + std::size_t args_count = call_args.size(); + std::size_t args_index = 0; + + assert(left()->kind == O_CALL); + + for (ptr_op_t sym = left()->right(); + sym; + sym = sym->has_right() ? sym->right() : NULL) { + ptr_op_t varname = sym; + if (sym->kind == O_COMMA) + varname = sym->left(); + + if (! varname->is_ident()) + throw_(calc_error, "Invalid function definition"); + else if (args_index == args_count) + local_scope.define(varname->as_ident(), wrap_value(false)); + else + local_scope.define(varname->as_ident(), + wrap_value(call_args[args_index++])); + } + + if (args_index < args_count) + throw_(calc_error, + "Too many arguments in function call (saw " << args_count << ")"); + + result = right()->compile(local_scope)->calc(local_scope, context); + break; + } + case O_LOOKUP: - if (left()->kind == IDENT && - left()->left() && left()->left()->kind == FUNCTION) { + if (left()->is_ident() && + left()->left() && left()->left()->is_function()) { call_scope_t call_args(scope); if (value_t obj = left()->left()->as_function()(call_args)) { if (obj.is_pointer()) { @@ -134,18 +189,20 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * context) const string& name(func->as_ident()); func = func->left(); - if (! func || func->kind != FUNCTION) - throw_(calc_error, "Calling non-function '" << name << "'"); + if (! func) + throw_(calc_error, "Calling unknown function '" << name << "'"); - result = func->as_function()(call_args); + if (func->is_function()) + result = func->as_function()(call_args); + else + result = func->calc(call_args, context); break; } case O_MATCH: -#if 0 if (! right()->is_value() || ! right()->as_value().is_mask()) throw_(calc_error, "Right-hand argument to match operator must be a regex"); -#endif + result = (right()->calc(scope, context).as_mask() .match(left()->calc(scope, context).to_string())); break; @@ -417,6 +474,14 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const found = true; break; + case O_DEFINE: + if (left() && left()->print(out, context)) + found = true; + out << " := "; + if (has_right() && right()->print(out, context)) + found = true; + break; + case O_LOOKUP: if (left() && left()->print(out, context)) found = true; @@ -485,6 +550,7 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const out << "FUNCTION"; break; + case O_DEFINE: out << "O_DEFINE"; break; case O_LOOKUP: out << "O_LOOKUP"; break; case O_CALL: out << "O_CALL"; break; case O_MATCH: out << "O_MATCH"; break; @@ -521,7 +587,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 || kind == IDENT) { + if (kind > TERMINALS || is_ident()) { if (left()) { left()->dump(out, depth + 1); if (kind > UNARY_OPERATORS && has_right()) @@ -110,6 +110,7 @@ public: O_COMMA, + O_DEFINE, O_LOOKUP, O_CALL, O_MATCH, diff --git a/src/parser.cc b/src/parser.cc index eeff59f0..a5fe9097 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -226,6 +226,9 @@ expr_t::parser_t::parse_logic_expr(std::istream& in, bool negate = false; switch (tok.kind) { + case token_t::DEFINE: + kind = op_t::O_DEFINE; + break; case token_t::EQUAL: if (tflags.has_flags(PARSE_NO_ASSIGN)) tok.rewind(in); diff --git a/src/report.cc b/src/report.cc index 209ad502..e510ac8d 100644 --- a/src/report.cc +++ b/src/report.cc @@ -504,6 +504,9 @@ option_t<report_t> * report_t::lookup_option(const char * p) expr_t::ptr_op_t report_t::lookup(const string& name) { + if (expr_t::ptr_op_t def = session.lookup(name)) + return def; + const char * p = name.c_str(); switch (*p) { case 'a': @@ -639,7 +642,7 @@ expr_t::ptr_op_t report_t::lookup(const string& name) if (option_t<report_t> * handler = lookup_option(p)) return MAKE_OPT_FUNCTOR(report_t, handler); - return session.lookup(name); + return NULL; } } // namespace ledger diff --git a/src/report.h b/src/report.h index c4ef7f92..1aff9212 100644 --- a/src/report.h +++ b/src/report.h @@ -166,6 +166,10 @@ public: option_t<report_t> * lookup_option(const char * p); + virtual void define(const string& name, expr_t::ptr_op_t def) { + session.define(name, def); + } + virtual expr_t::ptr_op_t lookup(const string& name); /** diff --git a/src/scope.cc b/src/scope.cc index 29a3e03a..f4b2dfab 100644 --- a/src/scope.cc +++ b/src/scope.cc @@ -35,7 +35,7 @@ namespace ledger { void symbol_scope_t::define(const string& name, expr_t::ptr_op_t def) { - DEBUG("ledger.xpath.syms", "Defining '" << name << "' = " << def); + DEBUG("scope.symbols", "Defining '" << name << "' = " << def); std::pair<symbol_map::iterator, bool> result = symbols.insert(symbol_map::value_type(name, def)); @@ -47,8 +47,7 @@ void symbol_scope_t::define(const string& name, expr_t::ptr_op_t def) std::pair<symbol_map::iterator, bool> result2 = symbols.insert(symbol_map::value_type(name, def)); if (! result2.second) - throw_(compile_error, - "Redefinition of '" << name << "' in same scope"); + throw_(compile_error, "Redefinition of '" << name << "' in same scope"); } } diff --git a/src/scope.h b/src/scope.h index 37ca1ccd..8323f224 100644 --- a/src/scope.h +++ b/src/scope.h @@ -66,6 +66,7 @@ public: TRACE_DTOR(scope_t); } + virtual void define(const string& name, expr_t::ptr_op_t def) {} virtual expr_t::ptr_op_t lookup(const string& name) = 0; value_t resolve(const string& name) { @@ -98,6 +99,11 @@ public: TRACE_DTOR(child_scope_t); } + virtual void define(const string& name, expr_t::ptr_op_t def) { + if (parent) + parent->define(name, def); + } + virtual expr_t::ptr_op_t lookup(const string& name) { if (parent) return parent->lookup(name); @@ -127,12 +133,6 @@ public: TRACE_DTOR(symbol_scope_t); } - void define(const string& name, const value_t& val) { - define(name, expr_t::op_t::wrap_value(val)); - } - void define(const string& name, const function_t& func) { - define(name, expr_t::op_t::wrap_functor(func)); - } virtual void define(const string& name, expr_t::ptr_op_t def); virtual expr_t::ptr_op_t lookup(const string& name); @@ -211,6 +211,11 @@ public: TRACE_DTOR(bind_scope_t); } + virtual void define(const string& name, expr_t::ptr_op_t def) { + parent->define(name, def); + grandchild.define(name, def); + } + virtual expr_t::ptr_op_t lookup(const string& name) { if (expr_t::ptr_op_t def = grandchild.lookup(name)) return def; diff --git a/src/session.cc b/src/session.cc index fad51da3..5dbd5593 100644 --- a/src/session.cc +++ b/src/session.cc @@ -206,7 +206,8 @@ expr_t::ptr_op_t session_t::lookup(const string& name) } break; } - return NULL; + + return symbol_scope_t::lookup(name); } } // namespace ledger diff --git a/src/session.h b/src/session.h index 231b6c73..179d2b75 100644 --- a/src/session.h +++ b/src/session.h @@ -58,7 +58,7 @@ namespace ledger { * * Long. */ -class session_t : public noncopyable, public scope_t +class session_t : public symbol_scope_t { friend void set_session_context(session_t * session); diff --git a/src/token.cc b/src/token.cc index f7133af3..9311f60a 100644 --- a/src/token.cc +++ b/src/token.cc @@ -258,6 +258,15 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags) break; case ':': in.get(c); + c = in.peek(); + if (c == '=') { + in.get(c); + symbol[1] = c; + symbol[2] = '\0'; + kind = DEFINE; + length = 2; + break; + } kind = COLON; break; diff --git a/src/token.h b/src/token.h index 47658749..55e222ba 100644 --- a/src/token.h +++ b/src/token.h @@ -73,6 +73,7 @@ struct expr_t::token_t : public noncopyable GREATER, // > GREATEREQ, // >= + DEFINE, // := ASSIGN, // = MATCH, // =~ NMATCH, // !~ diff --git a/src/value.cc b/src/value.cc index 4cfee747..c3731f7b 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1715,7 +1715,7 @@ void value_t::dump(std::ostream& out, const bool relaxed) const { switch (type()) { case VOID: - out << ""; + out << "<null>"; break; case BOOLEAN: |