Fixes to the value expression parser and evaluator

This commit is contained in:
John Wiegley 2009-11-10 02:26:20 -05:00
parent f49b7b2166
commit bf24b93818
6 changed files with 80 additions and 73 deletions

View file

@ -263,12 +263,15 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
args3_node->set_left(call1_node);
args3_node->set_right(args2_node);
expr_t::ptr_op_t seq1_node(new expr_t::op_t(expr_t::op_t::O_SEQ));
seq1_node->set_left(args3_node);
expr_t::ptr_op_t justify_node(new expr_t::op_t(expr_t::op_t::IDENT));
justify_node->set_ident("justify");
expr_t::ptr_op_t call2_node(new expr_t::op_t(expr_t::op_t::O_CALL));
call2_node->set_left(justify_node);
call2_node->set_right(args3_node);
call2_node->set_right(seq1_node);
string prev_expr = boost::get<expr_t>(current->data).text();
@ -280,9 +283,12 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
args4_node->set_left(call2_node);
args4_node->set_right(colorize_op);
expr_t::ptr_op_t seq2_node(new expr_t::op_t(expr_t::op_t::O_SEQ));
seq2_node->set_left(args4_node);
expr_t::ptr_op_t call3_node(new expr_t::op_t(expr_t::op_t::O_CALL));
call3_node->set_left(ansify_if_node);
call3_node->set_right(args4_node);
call3_node->set_right(seq2_node);
current->data = expr_t(call3_node);
} else {

View file

@ -118,7 +118,8 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
// 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, locus, depth + 1);
result = left()->compile(call_args, depth + 1)
->calc(call_args, locus, depth + 1);
break;
}
@ -135,7 +136,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
}
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;
@ -152,38 +152,32 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
if (! varname->is_ident())
throw_(calc_error, _("Invalid function definition"));
else if (args_index == args_count)
local_scope.define(symbol_t::FUNCTION, varname->as_ident(),
wrap_value(false));
scope.define(symbol_t::FUNCTION, varname->as_ident(),
wrap_value(false));
else
local_scope.define(symbol_t::FUNCTION, varname->as_ident(),
wrap_value(call_args[args_index++]));
scope.define(symbol_t::FUNCTION, varname->as_ident(),
wrap_value(call_args[args_index++]));
}
if (args_index < args_count)
throw_(calc_error,
_("Too many arguments in function call (saw %1)") << args_count);
result = right()->compile(local_scope, depth + 1)
->calc(local_scope, locus, depth + 1);
result = right()->calc(scope, locus, depth + 1);
break;
}
case O_LOOKUP:
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_scope()) {
if (obj.as_scope() == NULL) {
throw_(calc_error,
_("Left operand of . operator is NULL"));
} else {
scope_t& objscope(*obj.as_scope());
if (ptr_op_t member =
objscope.lookup(symbol_t::FUNCTION, right()->as_ident())) {
result = member->calc(objscope, NULL, depth + 1);
break;
}
if (value_t obj = left()->calc(scope, locus, depth + 1)) {
if (obj.is_scope()) {
if (obj.as_scope() == NULL) {
throw_(calc_error, _("Left operand of . operator is NULL"));
} else {
scope_t& objscope(*obj.as_scope());
if (ptr_op_t member =
objscope.lookup(symbol_t::FUNCTION, right()->as_ident())) {
result = member->calc(objscope, NULL, depth + 1);
break;
}
}
}
@ -321,20 +315,28 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
break;
case O_SEQ: {
left()->calc(scope, locus, depth + 1);
assert(has_right());
symbol_scope_t seq_scope(scope);
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;
// 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);
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(seq_scope, locus, depth + 1);
}
result = value_op->calc(scope, locus, depth + 1);
}
break;
}
@ -394,13 +396,14 @@ namespace {
if (op->left()->print(out, context))
found = true;
assert(op->has_right());
out << "; ";
if (op->has_right()) {
out << "; ";
if (op->right()->kind == expr_t::op_t::O_CONS)
found = print_cons(out, op->right(), context);
else if (op->right()->print(out, context))
found = true;
if (op->right()->kind == expr_t::op_t::O_CONS)
found = print_cons(out, op->right(), context);
else if (op->right()->print(out, context))
found = true;
}
return found;
}
@ -563,9 +566,7 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const
break;
case O_CONS:
out << "(";
found = print_cons(out, this, context);
out << ")";
break;
case O_SEQ:
@ -594,7 +595,7 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const
if (left() && left()->print(out, context))
found = true;
if (has_right()) {
if (right()->kind == O_CONS) {
if (right()->kind == O_SEQ) {
if (right()->print(out, context))
found = true;
} else {

View file

@ -243,9 +243,6 @@ private:
op->release();
}
static ptr_op_t new_node(kind_t _kind, ptr_op_t _left = NULL,
ptr_op_t _right = NULL);
ptr_op_t copy(ptr_op_t _left = NULL, ptr_op_t _right = NULL) const {
ptr_op_t node(new_node(kind, _left, _right));
if (kind < TERMINALS)
@ -254,6 +251,9 @@ private:
}
public:
static ptr_op_t new_node(kind_t _kind, ptr_op_t _left = NULL,
ptr_op_t _right = NULL);
ptr_op_t compile(scope_t& scope, const int depth = 0);
value_t calc(scope_t& scope, ptr_op_t * locus = NULL,
const int depth = 0);

View file

@ -82,7 +82,7 @@ expr_t::parser_t::parse_value_term(std::istream& in,
if (node->kind == op_t::O_CONS) {
ptr_op_t prev(node);
node = new op_t(op_t::O_CONS);
node = new op_t(op_t::O_SEQ);
node->set_left(prev);
}
break;

View file

@ -226,29 +226,30 @@ namespace {
2 /* account_abbrev_length */);
else
name = env->reported_account()->fullname();
}
else if (env.value_at(0).is_string()) {
name = env.get<string>(0);
account = env->xact->journal->find_account(name, false);
seeking_account = true;
}
else if (env.value_at(0).is_mask()) {
name = env.get<mask_t>(0).str();
account = env->xact->journal->find_account_re(name);
seeking_account = true;
}
else {
throw_(std::runtime_error,
_("Expected string or mask for argument 1, but received %1")
<< env.value_at(0).label());
}
} else {
account_t * master = env->account;
while (master->parent)
master = master->parent;
if (env.value_at(0).is_string()) {
name = env.get<string>(0);
account = master->find_account(name, false);
}
else if (env.value_at(0).is_mask()) {
name = env.get<mask_t>(0).str();
account = master->find_account_re(name);
}
else {
throw_(std::runtime_error,
_("Expected string or mask for argument 1, but received %1")
<< env.value_at(0).label());
}
if (seeking_account) {
if (! account)
throw_(std::runtime_error,
_("Could not find an account matching ") << env.value_at(0));
else
return account; // return a scope object
return value_t(static_cast<scope_t *>(account));
}
} else {
name = env->reported_account()->fullname();

View file

@ -307,15 +307,14 @@ query_t::parser_t::parse_query_term(query_t::lexer_t::token_t::kind_t tok_contex
throw_(parse_error,
_("Metadata equality operator not followed by term"));
expr_t::ptr_op_t cons = new expr_t::op_t(expr_t::op_t::O_CONS);
expr_t::ptr_op_t arg2 = new expr_t::op_t(expr_t::op_t::VALUE);
assert(tok.value);
arg2->set_value(mask_t(*tok.value));
cons->set_left(arg1);
cons->set_right(arg2);
node->set_right(cons);
node->set_right(expr_t::op_t::new_node
(expr_t::op_t::O_SEQ,
expr_t::op_t::new_node
(expr_t::op_t::O_CONS, arg1, arg2)));
} else {
node->set_right(arg1);
}