summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/op.cc90
-rw-r--r--src/op.h1
-rw-r--r--src/parser.cc3
-rw-r--r--src/report.cc5
-rw-r--r--src/report.h4
-rw-r--r--src/scope.cc5
-rw-r--r--src/scope.h17
-rw-r--r--src/session.cc3
-rw-r--r--src/session.h2
-rw-r--r--src/token.cc9
-rw-r--r--src/token.h1
-rw-r--r--src/value.cc2
12 files changed, 117 insertions, 25 deletions
diff --git a/src/op.cc b/src/op.cc
index 3aa2a8cd..615b7c76 100644
--- a/src/op.cc
+++ b/src/op.cc
@@ -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())
diff --git a/src/op.h b/src/op.h
index 344c752f..850e86b7 100644
--- a/src/op.h
+++ b/src/op.h
@@ -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: