The new SCOPE mechanism is working
This commit is contained in:
parent
bc9ff7095f
commit
1837e32391
3 changed files with 71 additions and 90 deletions
151
src/op.cc
151
src/op.cc
|
|
@ -81,7 +81,7 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
|
||||||
scope_t * scope_ptr = &scope;
|
scope_t * scope_ptr = &scope;
|
||||||
|
|
||||||
if (is_ident()) {
|
if (is_ident()) {
|
||||||
DEBUG("expr.compile", "lookup: " << as_ident());
|
DEBUG("expr.compile", "Lookup: " << as_ident());
|
||||||
|
|
||||||
if (ptr_op_t def = scope_ptr->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
|
// Identifier references are first looked up at the point of
|
||||||
|
|
@ -93,7 +93,7 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
|
||||||
def->dump(*_log_stream, 0);
|
def->dump(*_log_stream, 0);
|
||||||
}
|
}
|
||||||
#endif // defined(DEBUG_ON)
|
#endif // defined(DEBUG_ON)
|
||||||
return copy(def);
|
return copy(def);
|
||||||
}
|
}
|
||||||
else if (left()) {
|
else if (left()) {
|
||||||
return copy();
|
return copy();
|
||||||
|
|
@ -102,7 +102,6 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
|
||||||
}
|
}
|
||||||
else if (is_scope()) {
|
else if (is_scope()) {
|
||||||
shared_ptr<scope_t> subscope(new symbol_scope_t(scope));
|
shared_ptr<scope_t> subscope(new symbol_scope_t(scope));
|
||||||
DEBUG("expr.compile", "creating scope");
|
|
||||||
set_scope(subscope);
|
set_scope(subscope);
|
||||||
scope_ptr = subscope.get();
|
scope_ptr = subscope.get();
|
||||||
}
|
}
|
||||||
|
|
@ -114,22 +113,21 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
|
||||||
case IDENT:
|
case IDENT:
|
||||||
scope_ptr->define(symbol_t::FUNCTION, left()->as_ident(), right());
|
scope_ptr->define(symbol_t::FUNCTION, left()->as_ident(), right());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case O_CALL:
|
case O_CALL:
|
||||||
if (left()->left()->is_ident()) {
|
if (left()->left()->is_ident()) {
|
||||||
ptr_op_t node(new op_t(op_t::O_LAMBDA));
|
ptr_op_t node(new op_t(op_t::O_LAMBDA));
|
||||||
node->set_left(left()->right());
|
node->set_left(left()->right());
|
||||||
node->set_right(right());
|
node->set_right(right());
|
||||||
|
|
||||||
scope_ptr->define(symbol_t::FUNCTION, left()->left()->as_ident(),
|
scope_ptr->define(symbol_t::FUNCTION, left()->left()->as_ident(),
|
||||||
node);
|
node);
|
||||||
} else {
|
break;
|
||||||
throw_(compile_error, _("Invalid function definition"));
|
|
||||||
}
|
}
|
||||||
break;
|
// fall through...
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw_(compile_error, _("Invalid function definition"));
|
throw_(compile_error, _("Invalid function definition"));
|
||||||
}
|
}
|
||||||
return wrap_value(NULL_VALUE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr_op_t lhs(left()->compile(*scope_ptr, depth));
|
ptr_op_t lhs(left()->compile(*scope_ptr, depth));
|
||||||
|
|
@ -144,7 +142,7 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
|
||||||
|
|
||||||
// Reduce constants immediately if possible
|
// Reduce constants immediately if possible
|
||||||
if ((! lhs || lhs->is_value()) && (! rhs || rhs->is_value()))
|
if ((! lhs || lhs->is_value()) && (! rhs || rhs->is_value()))
|
||||||
return wrap_value(intermediate->calc(*scope_ptr, NULL, depth));
|
return wrap_value(intermediate->calc(*scope_ptr, NULL, depth + 1));
|
||||||
|
|
||||||
return intermediate;
|
return intermediate;
|
||||||
}
|
}
|
||||||
|
|
@ -159,7 +157,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
|
||||||
if (SHOW_DEBUG("expr.calc")) {
|
if (SHOW_DEBUG("expr.calc")) {
|
||||||
for (int i = 0; i < depth; i++)
|
for (int i = 0; i < depth; i++)
|
||||||
ledger::_log_buffer << '.';
|
ledger::_log_buffer << '.';
|
||||||
ledger::_log_buffer << op_context(this) << " => ...";
|
ledger::_log_buffer << op_context(this) << " => ...";
|
||||||
DEBUG("expr.calc", "");
|
DEBUG("expr.calc", "");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -170,61 +168,96 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case O_DEFINE:
|
case O_DEFINE:
|
||||||
//result = left()->calc(scope, locus, depth + 1);
|
|
||||||
result = NULL_VALUE;
|
result = NULL_VALUE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IDENT: {
|
case IDENT: {
|
||||||
ptr_op_t definition = left();
|
ptr_op_t definition = left();
|
||||||
|
// If no definition was pre-compiled for this identifier, look it up
|
||||||
|
// in the current scope.
|
||||||
if (! definition) {
|
if (! definition) {
|
||||||
// If no definition was pre-compiled for this identifier, look it
|
DEBUG("scope.symbols", "Looking for IDENT '" << as_ident() << "'");
|
||||||
// up in the current scope.
|
|
||||||
definition = scope.lookup(symbol_t::FUNCTION, as_ident());
|
definition = scope.lookup(symbol_t::FUNCTION, as_ident());
|
||||||
}
|
}
|
||||||
if (! definition)
|
if (! definition)
|
||||||
throw_(calc_error, _("Unknown identifier '%1'") << as_ident());
|
throw_(calc_error, _("Unknown identifier '%1'") << as_ident());
|
||||||
|
|
||||||
// Evaluating an identifier is the same as calling its definition
|
// Evaluating an identifier is the same as calling its definition
|
||||||
// directly, so we create an empty call_scope_t to reflect the scope for
|
// directly
|
||||||
// this implicit call.
|
result = definition->calc(scope, locus, depth + 1);
|
||||||
call_scope_t call_args(scope, locus, depth);
|
|
||||||
result = definition->compile(call_args, depth + 1)
|
|
||||||
->calc(call_args, locus, depth + 1);
|
|
||||||
check_type_context(scope, result);
|
check_type_context(scope, result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case FUNCTION: {
|
case FUNCTION: {
|
||||||
// Evaluating a FUNCTION is the same as calling it directly; this happens
|
// Evaluating a FUNCTION is the same as calling it directly; this
|
||||||
// when certain functions-that-look-like-variables (such as "amount") are
|
// happens when certain functions-that-look-like-variables (such as
|
||||||
// resolved.
|
// "amount") are resolved.
|
||||||
call_scope_t call_args(scope, locus, depth);
|
call_scope_t call_args(scope, locus, depth + 1);
|
||||||
result = as_function()(call_args);
|
result = as_function()(call_args);
|
||||||
check_type_context(scope, result);
|
check_type_context(scope, result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SCOPE:
|
case SCOPE:
|
||||||
|
assert(! is_scope_unset());
|
||||||
if (is_scope_unset()) {
|
if (is_scope_unset()) {
|
||||||
symbol_scope_t subscope(scope);
|
symbol_scope_t subscope(scope);
|
||||||
result = left()->calc(subscope, locus, depth + 1);
|
result = left()->calc(subscope, locus, depth + 1);
|
||||||
} else {
|
} else {
|
||||||
result = left()->calc(*as_scope(), locus, depth + 1);
|
bind_scope_t bound_scope(scope, *as_scope());
|
||||||
|
result = left()->calc(bound_scope, locus, depth + 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case O_LOOKUP: {
|
||||||
|
context_scope_t context_scope(scope, value_t::SCOPE);
|
||||||
|
bool scope_error = true;
|
||||||
|
if (value_t obj = left()->calc(context_scope, locus, depth + 1)) {
|
||||||
|
if (obj.is_scope() && obj.as_scope() != NULL) {
|
||||||
|
bind_scope_t bound_scope(scope, *obj.as_scope());
|
||||||
|
result = right()->calc(bound_scope, locus, depth + 1);
|
||||||
|
scope_error = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scope_error)
|
||||||
|
throw_(calc_error, _("Left operand does not evaluate to an object"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case O_CALL: {
|
||||||
|
ptr_op_t func = left();
|
||||||
|
const string& name(func->as_ident());
|
||||||
|
|
||||||
|
func = func->left();
|
||||||
|
if (! func)
|
||||||
|
func = scope.lookup(symbol_t::FUNCTION, name);
|
||||||
|
if (! func)
|
||||||
|
throw_(calc_error, _("Calling unknown function '%1'") << name);
|
||||||
|
|
||||||
|
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);
|
||||||
|
else
|
||||||
|
result = func->calc(call_args, locus, depth + 1);
|
||||||
|
|
||||||
|
check_type_context(scope, result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case O_LAMBDA: {
|
case O_LAMBDA: {
|
||||||
call_scope_t& call_args(downcast<call_scope_t>(scope));
|
call_scope_t& call_args(downcast<call_scope_t>(scope));
|
||||||
std::size_t args_count(call_args.size());
|
std::size_t args_count(call_args.size());
|
||||||
std::size_t args_index(0);
|
std::size_t args_index(0);
|
||||||
symbol_scope_t call_scope(call_args);
|
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();
|
|
||||||
|
|
||||||
|
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()) {
|
if (! varname->is_ident()) {
|
||||||
throw_(calc_error, _("Invalid function definition"));
|
throw_(calc_error, _("Invalid function definition"));
|
||||||
}
|
}
|
||||||
|
|
@ -248,59 +281,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case O_LOOKUP: {
|
|
||||||
context_scope_t context_scope(scope, value_t::SCOPE);
|
|
||||||
bool scope_error = true;
|
|
||||||
if (value_t obj = left()->calc(context_scope, locus, depth + 1)) {
|
|
||||||
if (obj.is_scope() && obj.as_scope() != NULL) {
|
|
||||||
bind_scope_t bound_scope(scope, *obj.as_scope());
|
|
||||||
result = right()->calc(bound_scope, locus, depth + 1);
|
|
||||||
scope_error = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (scope_error)
|
|
||||||
throw_(calc_error, _("Left operand does not evaluate to an object"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
func = func->left();
|
|
||||||
if (! func)
|
|
||||||
func = scope.lookup(symbol_t::FUNCTION, name);
|
|
||||||
if (! func)
|
|
||||||
throw_(calc_error, _("Calling unknown function '%1'") << name);
|
|
||||||
|
|
||||||
#if defined(DEBUG_ON)
|
|
||||||
if (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
|
|
||||||
|
|
||||||
if (func->is_function())
|
|
||||||
result = func->as_function()(call_args);
|
|
||||||
else
|
|
||||||
result = func->calc(call_args, locus, depth + 1);
|
|
||||||
|
|
||||||
check_type_context(scope, result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case O_MATCH:
|
case O_MATCH:
|
||||||
result = (right()->calc(scope, locus, depth + 1).as_mask()
|
result = (right()->calc(scope, locus, depth + 1).as_mask()
|
||||||
.match(left()->calc(scope, locus, depth + 1).to_string()));
|
.match(left()->calc(scope, locus, depth + 1).to_string()));
|
||||||
|
|
@ -369,7 +349,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
|
||||||
case O_QUERY:
|
case O_QUERY:
|
||||||
assert(right());
|
assert(right());
|
||||||
assert(right()->kind == O_COLON);
|
assert(right()->kind == O_COLON);
|
||||||
|
|
||||||
if (value_t temp = left()->calc(scope, locus, depth + 1))
|
if (value_t temp = left()->calc(scope, locus, depth + 1))
|
||||||
result = right()->left()->calc(scope, locus, depth + 1);
|
result = right()->left()->calc(scope, locus, depth + 1);
|
||||||
else
|
else
|
||||||
|
|
@ -382,8 +361,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
|
||||||
|
|
||||||
case O_CONS:
|
case O_CONS:
|
||||||
result = left()->calc(scope, locus, depth + 1);
|
result = left()->calc(scope, locus, depth + 1);
|
||||||
DEBUG("op.cons", "car = " << result);
|
|
||||||
|
|
||||||
if (has_right()) {
|
if (has_right()) {
|
||||||
value_t temp;
|
value_t temp;
|
||||||
temp.push_back(result);
|
temp.push_back(result);
|
||||||
|
|
@ -399,7 +376,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
|
||||||
next = NULL;
|
next = NULL;
|
||||||
}
|
}
|
||||||
temp.push_back(value_op->calc(scope, locus, depth + 1));
|
temp.push_back(value_op->calc(scope, locus, depth + 1));
|
||||||
DEBUG("op.cons", "temp now = " << temp);
|
|
||||||
}
|
}
|
||||||
result = temp;
|
result = temp;
|
||||||
}
|
}
|
||||||
|
|
@ -413,7 +389,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
|
||||||
// onto the stack. We evaluate the left side here to catch any
|
// onto the stack. We evaluate the left side here to catch any
|
||||||
// side-effects, such as definitions in the case of 'x = 1; x'.
|
// side-effects, such as definitions in the case of 'x = 1; x'.
|
||||||
result = left()->calc(scope, locus, depth + 1);
|
result = left()->calc(scope, locus, depth + 1);
|
||||||
|
|
||||||
if (has_right()) {
|
if (has_right()) {
|
||||||
ptr_op_t next = right();
|
ptr_op_t next = right();
|
||||||
while (next) {
|
while (next) {
|
||||||
|
|
@ -786,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
|
// An identifier is a special non-terminal, in that its left() can
|
||||||
// hold the compiled definition of the identifier.
|
// hold the compiled definition of the identifier.
|
||||||
if (kind > TERMINALS || is_ident() || is_scope()) {
|
if (kind > TERMINALS || is_scope()) {
|
||||||
if (left()) {
|
if (left()) {
|
||||||
left()->dump(out, depth + 1);
|
left()->dump(out, depth + 1);
|
||||||
if (kind > UNARY_OPERATORS && has_right())
|
if (kind > UNARY_OPERATORS && has_right())
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,8 @@ scope_t * scope_t::default_scope = NULL;
|
||||||
void symbol_scope_t::define(const symbol_t::kind_t kind,
|
void symbol_scope_t::define(const symbol_t::kind_t kind,
|
||||||
const string& name, expr_t::ptr_op_t def)
|
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)
|
if (! symbols)
|
||||||
symbols = symbol_map();
|
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)
|
const string& name)
|
||||||
{
|
{
|
||||||
if (symbols) {
|
if (symbols) {
|
||||||
|
DEBUG("scope.symbols", "Looking for '" << name << "' in " << this);
|
||||||
symbol_map::const_iterator i = symbols->find(symbol_t(kind, name));
|
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 (*i).second;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return child_scope_t::lookup(kind, name);
|
return child_scope_t::lookup(kind, name);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,8 @@ public:
|
||||||
scope_t& _grandchild)
|
scope_t& _grandchild)
|
||||||
: child_scope_t(_parent), grandchild(_grandchild) {
|
: child_scope_t(_parent), grandchild(_grandchild) {
|
||||||
TRACE_CTOR(bind_scope_t, "scope_t&, scope_t&");
|
TRACE_CTOR(bind_scope_t, "scope_t&, scope_t&");
|
||||||
|
DEBUG("scope.symbols",
|
||||||
|
"Binding scope " << &_parent << " with " << &_grandchild);
|
||||||
}
|
}
|
||||||
virtual ~bind_scope_t() {
|
virtual ~bind_scope_t() {
|
||||||
TRACE_DTOR(bind_scope_t);
|
TRACE_DTOR(bind_scope_t);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue