Added support for value expression definitions.
Example: ] expr f(x) := x + 100 ] expr f(100) 200
This commit is contained in:
parent
f7f5ed3d0f
commit
2d5ad7dee8
12 changed files with 117 additions and 25 deletions
90
src/op.cc
90
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())
|
||||
|
|
|
|||
1
src/op.h
1
src/op.h
|
|
@ -110,6 +110,7 @@ public:
|
|||
|
||||
O_COMMA,
|
||||
|
||||
O_DEFINE,
|
||||
O_LOOKUP,
|
||||
O_CALL,
|
||||
O_MATCH,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
17
src/scope.h
17
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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ struct expr_t::token_t : public noncopyable
|
|||
GREATER, // >
|
||||
GREATEREQ, // >=
|
||||
|
||||
DEFINE, // :=
|
||||
ASSIGN, // =
|
||||
MATCH, // =~
|
||||
NMATCH, // !~
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue