summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compare.cc2
-rw-r--r--src/format.cc2
-rw-r--r--src/op.cc210
-rw-r--r--src/op.h51
-rw-r--r--src/parser.cc33
-rw-r--r--src/scope.cc8
-rw-r--r--src/scope.h2
-rw-r--r--src/value.cc2
8 files changed, 172 insertions, 138 deletions
diff --git a/src/compare.cc b/src/compare.cc
index 12114c7d..cdc96a86 100644
--- a/src/compare.cc
+++ b/src/compare.cc
@@ -44,7 +44,7 @@ void push_sort_value(std::list<sort_value_t>& sort_values,
if (node->kind == expr_t::op_t::O_CONS) {
while (node && node->kind == expr_t::op_t::O_CONS) {
push_sort_value(sort_values, node->left(), scope);
- node = node->right();
+ node = node->has_right() ? node->right() : NULL;
}
} else {
bool inverted = false;
diff --git a/src/format.cc b/src/format.cc
index 93e7ea08..65c06488 100644
--- a/src/format.cc
+++ b/src/format.cc
@@ -225,7 +225,7 @@ format_t::element_t * format_t::parse_elements(const string& fmt,
expr_t::ptr_op_t colorize_op;
if (op->kind == expr_t::op_t::O_CONS) {
amount_op = op->left();
- colorize_op = op->right();
+ colorize_op = op->has_right() ? op->right() : NULL;
} else {
amount_op = op;
}
diff --git a/src/op.cc b/src/op.cc
index 537f5371..bf6fa543 100644
--- a/src/op.cc
+++ b/src/op.cc
@@ -50,7 +50,7 @@ namespace {
expr_t::ptr_op_t value_op;
if (next->kind == expr_t::op_t::O_CONS) {
value_op = next->left();
- next = next->right();
+ next = next->has_right() ? next->right() : NULL;
} else {
value_op = next;
next = NULL;
@@ -78,10 +78,12 @@ namespace {
expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
{
+ scope_t * scope_ptr = &scope;
+
if (is_ident()) {
- DEBUG("expr.compile", "lookup: " << as_ident());
+ DEBUG("expr.compile", "Lookup: " << as_ident());
- if (ptr_op_t def = scope.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
// definition, and then at the point of every use if they could
// not be found there.
@@ -98,36 +100,40 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
}
return this;
}
-
- if (kind < TERMINALS)
+ else if (is_scope()) {
+ shared_ptr<scope_t> subscope(new symbol_scope_t(scope));
+ set_scope(subscope);
+ scope_ptr = subscope.get();
+ }
+ else if (kind < TERMINALS) {
return this;
-
- if (kind == O_DEFINE) {
+ }
+ else if (kind == O_DEFINE) {
switch (left()->kind) {
case IDENT:
- scope.define(symbol_t::FUNCTION, left()->as_ident(), right());
+ scope_ptr->define(symbol_t::FUNCTION, left()->as_ident(), right());
break;
+
case O_CALL:
if (left()->left()->is_ident()) {
ptr_op_t node(new op_t(op_t::O_LAMBDA));
node->set_left(left()->right());
node->set_right(right());
-
- scope.define(symbol_t::FUNCTION, left()->left()->as_ident(), node);
- } else {
- throw_(compile_error, _("Invalid function definition"));
+ scope_ptr->define(symbol_t::FUNCTION, left()->left()->as_ident(),
+ node);
+ break;
}
- break;
+ // fall through...
+
default:
throw_(compile_error, _("Invalid function definition"));
}
- return wrap_value(value_t());
}
- ptr_op_t lhs(left()->compile(scope, depth));
+ ptr_op_t lhs(left()->compile(*scope_ptr, depth));
ptr_op_t rhs(kind > UNARY_OPERATORS && has_right() ?
(kind == O_LOOKUP ? right() :
- right()->compile(scope, depth)) : NULL);
+ right()->compile(*scope_ptr, depth)) : NULL);
if (lhs == left() && (! rhs || rhs == right()))
return this;
@@ -136,25 +142,22 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
// Reduce constants immediately if possible
if ((! lhs || lhs->is_value()) && (! rhs || rhs->is_value()))
- return wrap_value(intermediate->calc(scope, NULL, depth));
+ return wrap_value(intermediate->calc(*scope_ptr, NULL, depth + 1));
return intermediate;
}
value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
{
-#if defined(DEBUG_ON)
- bool skip_debug = false;
-#endif
try {
value_t result;
#if defined(DEBUG_ON)
- if (! skip_debug && SHOW_DEBUG("expr.calc")) {
+ if (SHOW_DEBUG("expr.calc")) {
for (int i = 0; i < depth; i++)
ledger::_log_buffer << '.';
- ledger::_log_buffer << op_context(this) << " => ...";
+ ledger::_log_buffer << op_context(this) << " => ...";
DEBUG("expr.calc", "");
}
#endif
@@ -165,77 +168,47 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
break;
case O_DEFINE:
- //result = left()->calc(scope, locus, depth + 1);
result = NULL_VALUE;
break;
case IDENT: {
ptr_op_t definition = left();
+ // If no definition was pre-compiled for this identifier, look it up
+ // in the current scope.
if (! definition) {
- // If no definition was pre-compiled for this identifier, look it
- // up in the current scope.
+ DEBUG("scope.symbols", "Looking for IDENT '" << as_ident() << "'");
definition = scope.lookup(symbol_t::FUNCTION, as_ident());
}
if (! definition)
throw_(calc_error, _("Unknown identifier '%1'") << as_ident());
// 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, locus, depth);
- result = definition->compile(call_args, depth + 1)
- ->calc(call_args, locus, depth + 1);
+ // directly
+ result = definition->calc(scope, locus, depth + 1);
check_type_context(scope, result);
break;
}
case FUNCTION: {
- // Evaluating a FUNCTION is the same as calling it directly; this happens
- // when certain functions-that-look-like-variables (such as "amount") are
- // resolved.
- call_scope_t call_args(scope, locus, depth);
+ // Evaluating a FUNCTION is the same as calling it directly; this
+ // happens when certain functions-that-look-like-variables (such as
+ // "amount") are resolved.
+ call_scope_t call_args(scope, locus, depth + 1);
result = as_function()(call_args);
check_type_context(scope, result);
-#if defined(DEBUG_ON)
- skip_debug = true;
-#endif
break;
}
- case O_LAMBDA: {
- call_scope_t& call_args(downcast<call_scope_t>(scope));
- std::size_t args_count(call_args.size());
- std::size_t args_index(0);
- 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();
-
- if (! varname->is_ident()) {
- throw_(calc_error, _("Invalid function definition"));
- }
- else if (args_index == args_count) {
- call_scope.define(symbol_t::FUNCTION, varname->as_ident(),
- wrap_value(NULL_VALUE));
- }
- else {
- DEBUG("expr.compile",
- "Defining function parameter " << varname->as_ident());
- call_scope.define(symbol_t::FUNCTION, varname->as_ident(),
- wrap_value(call_args[args_index++]));
- }
+ case SCOPE:
+ assert(! is_scope_unset());
+ if (is_scope_unset()) {
+ symbol_scope_t subscope(scope);
+ result = left()->calc(subscope, locus, depth + 1);
+ } else {
+ bind_scope_t bound_scope(scope, *as_scope());
+ result = left()->calc(bound_scope, locus, depth + 1);
}
-
- if (args_index < args_count)
- throw_(calc_error,
- _("Too few arguments in function call (saw %1)") << args_count);
-
- result = right()->calc(call_scope, locus, depth + 1);
break;
- }
case O_LOOKUP: {
context_scope_t context_scope(scope, value_t::SCOPE);
@@ -253,10 +226,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
}
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());
@@ -266,20 +235,9 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
if (! func)
throw_(calc_error, _("Calling unknown function '%1'") << name);
-#if defined(DEBUG_ON)
- if (! skip_debug && 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
+ 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);
@@ -290,6 +248,39 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
break;
}
+ case O_LAMBDA: {
+ call_scope_t& call_args(downcast<call_scope_t>(scope));
+ std::size_t args_count(call_args.size());
+ std::size_t args_index(0);
+ symbol_scope_t call_scope(call_args);
+
+ 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()) {
+ throw_(calc_error, _("Invalid function definition"));
+ }
+ else if (args_index == args_count) {
+ call_scope.define(symbol_t::FUNCTION, varname->as_ident(),
+ wrap_value(NULL_VALUE));
+ }
+ else {
+ DEBUG("expr.compile",
+ "Defining function parameter " << varname->as_ident());
+ call_scope.define(symbol_t::FUNCTION, varname->as_ident(),
+ wrap_value(call_args[args_index++]));
+ }
+ }
+
+ if (args_index < args_count)
+ throw_(calc_error,
+ _("Too few arguments in function call (saw %1)") << args_count);
+
+ result = right()->calc(call_scope, locus, depth + 1);
+ break;
+ }
+
case O_MATCH:
result = (right()->calc(scope, locus, depth + 1).as_mask()
.match(left()->calc(scope, locus, depth + 1).to_string()));
@@ -358,7 +349,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
case O_QUERY:
assert(right());
assert(right()->kind == O_COLON);
-
if (value_t temp = left()->calc(scope, locus, depth + 1))
result = right()->left()->calc(scope, locus, depth + 1);
else
@@ -371,8 +361,6 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
case O_CONS:
result = left()->calc(scope, locus, depth + 1);
- DEBUG("op.cons", "car = " << result);
-
if (has_right()) {
value_t temp;
temp.push_back(result);
@@ -382,28 +370,25 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
ptr_op_t value_op;
if (next->kind == O_CONS) {
value_op = next->left();
- next = next->right();
+ next = next->has_right() ? next->right() : NULL;
} else {
value_op = next;
next = NULL;
}
temp.push_back(value_op->calc(scope, locus, depth + 1));
- DEBUG("op.cons", "temp now = " << temp);
}
result = temp;
}
break;
case O_SEQ: {
- symbol_scope_t seq_scope(scope);
-
- // 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.
+ // 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);
-
+ // Another feature of O_SEQ is that it pushes a new symbol scope
+ // onto the stack. We evaluate the left side here to catch any
+ // side-effects, such as definitions in the case of 'x = 1; x'.
+ result = left()->calc(scope, locus, depth + 1);
if (has_right()) {
ptr_op_t next = right();
while (next) {
@@ -415,7 +400,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
value_op = next;
next = NULL;
}
- result = value_op->calc(seq_scope, locus, depth + 1);
+ result = value_op->calc(scope, locus, depth + 1);
}
}
break;
@@ -426,7 +411,7 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
}
#if defined(DEBUG_ON)
- if (! skip_debug && SHOW_DEBUG("expr.calc")) {
+ if (SHOW_DEBUG("expr.calc")) {
for (int i = 0; i < depth; i++)
ledger::_log_buffer << '.';
ledger::_log_buffer << op_context(this) << " => ";
@@ -476,9 +461,8 @@ namespace {
if (op->has_right()) {
out << "; ";
-
- if (op->right()->kind == expr_t::op_t::O_CONS)
- found = print_cons(out, op->right(), context);
+ if (op->right()->kind == expr_t::op_t::O_SEQ)
+ found = print_seq(out, op->right(), context);
else if (op->right()->print(out, context))
found = true;
}
@@ -515,6 +499,11 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const
out << "<FUNCTION>";
break;
+ case SCOPE:
+ if (left() && left()->print(out, context))
+ found = true;
+ break;
+
case O_NOT:
out << "! ";
if (left() && left()->print(out, context))
@@ -625,7 +614,6 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const
case O_CONS:
found = print_cons(out, this, context);
break;
-
case O_SEQ:
found = print_seq(out, this, context);
break;
@@ -726,6 +714,14 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const
out << "FUNCTION";
break;
+ case SCOPE:
+ out << "SCOPE: ";
+ if (is_scope_unset())
+ out << "null";
+ else
+ out << as_scope().get();
+ break;
+
case O_DEFINE: out << "O_DEFINE"; break;
case O_LOOKUP: out << "O_LOOKUP"; break;
case O_LAMBDA: out << "O_LAMBDA"; break;
@@ -765,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
// hold the compiled definition of the identifier.
- if (kind > TERMINALS || is_ident()) {
+ if (kind > TERMINALS || is_scope()) {
if (left()) {
left()->dump(out, depth + 1);
if (kind > UNARY_OPERATORS && has_right())
diff --git a/src/op.h b/src/op.h
index 6f7d7904..c4d353dc 100644
--- a/src/op.h
+++ b/src/op.h
@@ -58,10 +58,12 @@ private:
mutable short refc;
ptr_op_t left_;
- variant<ptr_op_t, // used by all binary operators
+ variant<boost::blank,
+ ptr_op_t, // used by all binary operators
value_t, // used by constant VALUE
string, // used by constant IDENT
- expr_t::func_t // used by terminal FUNCTION
+ expr_t::func_t, // used by terminal FUNCTION
+ shared_ptr<scope_t> // used by terminal SCOPE
> data;
public:
@@ -73,6 +75,7 @@ public:
CONSTANTS,
FUNCTION,
+ SCOPE,
TERMINALS,
@@ -173,7 +176,7 @@ public:
return kind == FUNCTION;
}
expr_t::func_t& as_function_lval() {
- assert(kind == FUNCTION);
+ assert(is_function());
return boost::get<expr_t::func_t>(data);
}
const expr_t::func_t& as_function() const {
@@ -183,21 +186,41 @@ public:
data = val;
}
+ bool is_scope() const {
+ return kind == SCOPE;
+ }
+ bool is_scope_unset() const {
+ return data.which() == 0;
+ }
+ shared_ptr<scope_t> as_scope_lval() {
+ assert(is_scope());
+ return boost::get<shared_ptr<scope_t> >(data);
+ }
+ const shared_ptr<scope_t> as_scope() const {
+ return const_cast<op_t *>(this)->as_scope_lval();
+ }
+ void set_scope(shared_ptr<scope_t> val) {
+ data = val;
+ }
+
+ // These three functions must use 'kind == IDENT' rather than
+ // 'is_ident()', because they are called before the `data' member gets
+ // set, which is_ident() tests.
ptr_op_t& left() {
- assert(kind > TERMINALS || kind == IDENT);
+ assert(kind > TERMINALS || kind == IDENT || is_scope());
return left_;
}
const ptr_op_t& left() const {
- assert(kind > TERMINALS || kind == IDENT);
+ assert(kind > TERMINALS || kind == IDENT || is_scope());
return left_;
}
void set_left(const ptr_op_t& expr) {
- assert(kind > TERMINALS || kind == IDENT);
+ assert(kind > TERMINALS || kind == IDENT || is_scope());
left_ = expr;
}
ptr_op_t& as_op_lval() {
- assert(kind > TERMINALS || kind == IDENT);
+ assert(kind > TERMINALS || is_ident());
return boost::get<ptr_op_t>(data);
}
const ptr_op_t& as_op() const {
@@ -219,7 +242,7 @@ public:
bool has_right() const {
if (kind < TERMINALS)
return false;
- return as_op();
+ return data.which() != 0 && as_op();
}
private:
@@ -284,6 +307,7 @@ public:
static ptr_op_t wrap_value(const value_t& val);
static ptr_op_t wrap_functor(expr_t::func_t fobj);
+ static ptr_op_t wrap_scope(shared_ptr<scope_t> sobj);
#if defined(HAVE_BOOST_SERIALIZATION)
private:
@@ -295,13 +319,13 @@ private:
void serialize(Archive& ar, const unsigned int /* version */) {
ar & refc;
ar & kind;
- if (Archive::is_loading::value || ! left_ || left_->kind != FUNCTION) {
+ if (Archive::is_loading::value || ! left_ || ! left_->is_function()) {
ar & left_;
} else {
ptr_op_t temp_op;
ar & temp_op;
}
- if (Archive::is_loading::value || kind == VALUE || kind == IDENT ||
+ if (Archive::is_loading::value || is_value() || is_ident() ||
(kind > UNARY_OPERATORS &&
(! has_right() || ! right()->is_function()))) {
ar & data;
@@ -335,6 +359,13 @@ expr_t::op_t::wrap_functor(expr_t::func_t fobj) {
return temp;
}
+inline expr_t::ptr_op_t
+expr_t::op_t::wrap_scope(shared_ptr<scope_t> sobj) {
+ ptr_op_t temp(new op_t(op_t::SCOPE));
+ temp->set_scope(sobj);
+ return temp;
+}
+
#define MAKE_FUNCTOR(x) expr_t::op_t::wrap_functor(bind(&x, this, _1))
#define WRAP_FUNCTOR(x) expr_t::op_t::wrap_functor(x)
diff --git a/src/parser.cc b/src/parser.cc
index a18fa552..ad621106 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -434,7 +434,6 @@ expr_t::parser_t::parse_comma_expr(std::istream& in,
ptr_op_t prev(node);
node = new op_t(op_t::O_CONS);
node->set_left(prev);
-
next = node;
}
@@ -493,7 +492,9 @@ expr_t::parser_t::parse_assign_expr(std::istream& in,
ptr_op_t prev(node);
node = new op_t(op_t::O_DEFINE);
node->set_left(prev);
- node->set_right(parse_lambda_expr(in, tflags));
+ ptr_op_t scope(new op_t(op_t::SCOPE));
+ scope->set_left(parse_lambda_expr(in, tflags));
+ node->set_right(scope);
} else {
push_token(tok);
}
@@ -509,24 +510,24 @@ expr_t::parser_t::parse_value_expr(std::istream& in,
ptr_op_t node(parse_assign_expr(in, tflags));
if (node && ! tflags.has_flags(PARSE_SINGLE)) {
- ptr_op_t next;
+ ptr_op_t chain;
while (true) {
token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT));
-
if (tok.kind == token_t::SEMI) {
- if (! next) {
- ptr_op_t prev(node);
- node = new op_t(op_t::O_SEQ);
- node->set_left(prev);
-
- next = node;
+ ptr_op_t seq(new op_t(op_t::O_SEQ));
+ if (! chain) {
+ seq->set_left(node);
+ ptr_op_t scope(new op_t(op_t::SCOPE));
+ scope->set_left(seq);
+ node = scope;
+ } else {
+ seq->set_left(chain->right());
+ ptr_op_t scope(new op_t(op_t::SCOPE));
+ scope->set_left(seq);
+ chain->set_right(scope);
}
-
- ptr_op_t chain(new op_t(op_t::O_SEQ));
- chain->set_left(parse_assign_expr(in, tflags));
-
- next->set_right(chain);
- next = chain;
+ seq->set_right(parse_assign_expr(in, tflags));
+ chain = seq;
} else {
push_token(tok);
break;
diff --git a/src/scope.cc b/src/scope.cc
index e18b5a0a..95347c8d 100644
--- a/src/scope.cc
+++ b/src/scope.cc
@@ -40,7 +40,8 @@ scope_t * scope_t::default_scope = NULL;
void symbol_scope_t::define(const symbol_t::kind_t kind,
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)
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)
{
if (symbols) {
+ DEBUG("scope.symbols", "Looking for '" << name << "' in " << this);
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 child_scope_t::lookup(kind, name);
}
diff --git a/src/scope.h b/src/scope.h
index a7b3c5cb..fbc1a889 100644
--- a/src/scope.h
+++ b/src/scope.h
@@ -188,6 +188,8 @@ public:
scope_t& _grandchild)
: child_scope_t(_parent), grandchild(_grandchild) {
TRACE_CTOR(bind_scope_t, "scope_t&, scope_t&");
+ DEBUG("scope.symbols",
+ "Binding scope " << &_parent << " with " << &_grandchild);
}
virtual ~bind_scope_t() {
TRACE_DTOR(bind_scope_t);
diff --git a/src/value.cc b/src/value.cc
index c62e6f32..f54c06a5 100644
--- a/src/value.cc
+++ b/src/value.cc
@@ -1727,7 +1727,7 @@ void value_t::print(std::ostream& _out,
switch (type()) {
case VOID:
- out << "";
+ out << "(null)";
break;
case BOOLEAN: