XML-related bits -- I just wanted the better infrastructure that had been created during the rewrite. It doesn't work, but it compiles and links now. This means that all of the previous 3.0 code has been moved over, although there are still snippets of code in pending/old that need to be restored.
2142 lines
50 KiB
C++
2142 lines
50 KiB
C++
/*
|
|
* Copyright (c) 2003-2007, John Wiegley. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* - Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* - Neither the name of New Artisans LLC nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "parsexp.h"
|
|
|
|
namespace ledger {
|
|
namespace expr {
|
|
|
|
void parser_t::token_t::parse_ident(std::istream& in)
|
|
{
|
|
if (in.eof()) {
|
|
kind = TOK_EOF;
|
|
return;
|
|
}
|
|
assert(in.good());
|
|
|
|
char c = peek_next_nonws(in);
|
|
|
|
if (in.eof()) {
|
|
kind = TOK_EOF;
|
|
return;
|
|
}
|
|
assert(in.good());
|
|
|
|
kind = IDENT;
|
|
length = 0;
|
|
|
|
char buf[256];
|
|
READ_INTO_(in, buf, 255, c, length,
|
|
std::isalnum(c) || c == '_' || c == '.' || c == '-');
|
|
|
|
switch (buf[0]) {
|
|
case 'a':
|
|
if (std::strcmp(buf, "and") == 0)
|
|
kind = KW_AND;
|
|
break;
|
|
case 'd':
|
|
if (std::strcmp(buf, "div") == 0)
|
|
kind = KW_DIV;
|
|
break;
|
|
case 'e':
|
|
if (std::strcmp(buf, "eq") == 0)
|
|
kind = EQUAL;
|
|
break;
|
|
case 'f':
|
|
if (std::strcmp(buf, "false") == 0) {
|
|
kind = VALUE;
|
|
value = false;
|
|
}
|
|
break;
|
|
case 'g':
|
|
if (std::strcmp(buf, "gt") == 0)
|
|
kind = GREATER;
|
|
else if (std::strcmp(buf, "ge") == 0)
|
|
kind = GREATEREQ;
|
|
break;
|
|
case 'i':
|
|
if (std::strcmp(buf, "is") == 0)
|
|
kind = EQUAL;
|
|
break;
|
|
case 'l':
|
|
if (std::strcmp(buf, "lt") == 0)
|
|
kind = LESS;
|
|
else if (std::strcmp(buf, "le") == 0)
|
|
kind = LESSEQ;
|
|
break;
|
|
case 'm':
|
|
if (std::strcmp(buf, "mod") == 0)
|
|
kind = KW_MOD;
|
|
break;
|
|
case 'n':
|
|
if (std::strcmp(buf, "ne") == 0)
|
|
kind = NEQUAL;
|
|
break;
|
|
case 'o':
|
|
if (std::strcmp(buf, "or") == 0)
|
|
kind = KW_OR;
|
|
break;
|
|
case 't':
|
|
if (std::strcmp(buf, "true") == 0) {
|
|
kind = VALUE;
|
|
value = true;
|
|
}
|
|
break;
|
|
#if 0
|
|
case 'u':
|
|
if (std::strcmp(buf, "union") == 0)
|
|
kind = KW_UNION;
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
if (kind == IDENT)
|
|
value.set_string(buf);
|
|
}
|
|
|
|
void parser_t::token_t::next(std::istream& in, const flags_t flags)
|
|
{
|
|
if (in.eof()) {
|
|
kind = TOK_EOF;
|
|
return;
|
|
}
|
|
assert(in.good());
|
|
|
|
char c = peek_next_nonws(in);
|
|
|
|
if (in.eof()) {
|
|
kind = TOK_EOF;
|
|
return;
|
|
}
|
|
assert(in.good());
|
|
|
|
symbol[0] = c;
|
|
symbol[1] = '\0';
|
|
|
|
length = 1;
|
|
|
|
if (! (flags & EXPR_PARSE_RELAXED) &&
|
|
(std::isalpha(c) || c == '_')) {
|
|
parse_ident(in);
|
|
return;
|
|
}
|
|
|
|
switch (c) {
|
|
case '@':
|
|
in.get(c);
|
|
kind = AT_SYM;
|
|
break;
|
|
case '$':
|
|
in.get(c);
|
|
kind = DOLLAR;
|
|
break;
|
|
|
|
case '(':
|
|
in.get(c);
|
|
kind = LPAREN;
|
|
break;
|
|
case ')':
|
|
in.get(c);
|
|
kind = RPAREN;
|
|
break;
|
|
|
|
case '[': {
|
|
in.get(c);
|
|
if (flags & EXPR_PARSE_ALLOW_DATE) {
|
|
char buf[256];
|
|
READ_INTO_(in, buf, 255, c, length, c != ']');
|
|
if (c != ']')
|
|
unexpected(c, ']');
|
|
in.get(c);
|
|
length++;
|
|
interval_t timespan(buf);
|
|
kind = VALUE;
|
|
value = timespan.first();
|
|
} else {
|
|
kind = LBRACKET;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ']': {
|
|
in.get(c);
|
|
kind = RBRACKET;
|
|
break;
|
|
}
|
|
|
|
case '\'':
|
|
case '"': {
|
|
char delim;
|
|
in.get(delim);
|
|
char buf[4096];
|
|
READ_INTO_(in, buf, 4095, c, length, c != delim);
|
|
if (c != delim)
|
|
unexpected(c, delim);
|
|
in.get(c);
|
|
length++;
|
|
kind = VALUE;
|
|
value.set_string(buf);
|
|
break;
|
|
}
|
|
|
|
case '{': {
|
|
in.get(c);
|
|
amount_t temp;
|
|
temp.parse(in, AMOUNT_PARSE_NO_MIGRATE);
|
|
in.get(c);
|
|
if (c != '}')
|
|
unexpected(c, '}');
|
|
length++;
|
|
kind = VALUE;
|
|
value = temp;
|
|
break;
|
|
}
|
|
|
|
case '!':
|
|
in.get(c);
|
|
c = in.peek();
|
|
if (c == '=') {
|
|
in.get(c);
|
|
symbol[1] = c;
|
|
symbol[2] = '\0';
|
|
kind = NEQUAL;
|
|
length = 2;
|
|
break;
|
|
}
|
|
kind = EXCLAM;
|
|
break;
|
|
|
|
case '-':
|
|
in.get(c);
|
|
kind = MINUS;
|
|
break;
|
|
case '+':
|
|
in.get(c);
|
|
kind = PLUS;
|
|
break;
|
|
|
|
case '*':
|
|
in.get(c);
|
|
kind = STAR;
|
|
break;
|
|
|
|
case '/':
|
|
in.get(c);
|
|
kind = SLASH;
|
|
break;
|
|
|
|
case '=':
|
|
in.get(c);
|
|
kind = EQUAL;
|
|
break;
|
|
|
|
case '<':
|
|
in.get(c);
|
|
if (in.peek() == '=') {
|
|
in.get(c);
|
|
symbol[1] = c;
|
|
symbol[2] = '\0';
|
|
kind = LESSEQ;
|
|
length = 2;
|
|
break;
|
|
}
|
|
kind = LESS;
|
|
break;
|
|
|
|
case '>':
|
|
in.get(c);
|
|
if (in.peek() == '=') {
|
|
in.get(c);
|
|
symbol[1] = c;
|
|
symbol[2] = '\0';
|
|
kind = GREATEREQ;
|
|
length = 2;
|
|
break;
|
|
}
|
|
kind = GREATER;
|
|
break;
|
|
|
|
#if 0
|
|
case '|':
|
|
in.get(c);
|
|
kind = PIPE;
|
|
break;
|
|
#endif
|
|
case ',':
|
|
in.get(c);
|
|
kind = COMMA;
|
|
break;
|
|
|
|
case '.':
|
|
in.get(c);
|
|
c = in.peek();
|
|
if (c == '.') {
|
|
in.get(c);
|
|
length++;
|
|
kind = DOTDOT;
|
|
break;
|
|
}
|
|
else if (! std::isdigit(c)) {
|
|
kind = DOT;
|
|
break;
|
|
}
|
|
in.unget(); // put the first '.' back
|
|
// fall through...
|
|
|
|
default:
|
|
if (! (flags & EXPR_PARSE_RELAXED)) {
|
|
kind = UNKNOWN;
|
|
} else {
|
|
amount_t temp;
|
|
unsigned long pos = 0;
|
|
|
|
// When in relaxed parsing mode, we want to migrate commodity
|
|
// flags so that any precision specified by the user updates the
|
|
// current maximum displayed precision.
|
|
try {
|
|
pos = (long)in.tellg();
|
|
|
|
unsigned char parse_flags = 0;
|
|
if (flags & EXPR_PARSE_NO_MIGRATE)
|
|
parse_flags |= AMOUNT_PARSE_NO_MIGRATE;
|
|
if (flags & EXPR_PARSE_NO_REDUCE)
|
|
parse_flags |= AMOUNT_PARSE_NO_REDUCE;
|
|
|
|
temp.parse(in, parse_flags);
|
|
|
|
kind = VALUE;
|
|
value = temp;
|
|
}
|
|
catch (amount_error& err) {
|
|
// If the amount had no commodity, it must be an unambiguous
|
|
// variable reference
|
|
|
|
// jww (2007-04-19): There must be a more efficient way to do this!
|
|
if (std::strcmp(err.what(), "No quantity specified for amount") == 0) {
|
|
in.clear();
|
|
in.seekg(pos, std::ios::beg);
|
|
|
|
c = in.peek();
|
|
assert(! (std::isdigit(c) || c == '.'));
|
|
parse_ident(in);
|
|
} else {
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void parser_t::token_t::rewind(std::istream& in)
|
|
{
|
|
for (unsigned int i = 0; i < length; i++)
|
|
in.unget();
|
|
}
|
|
|
|
|
|
void parser_t::token_t::unexpected()
|
|
{
|
|
switch (kind) {
|
|
case TOK_EOF:
|
|
throw_(parse_error, "Unexpected end of expression");
|
|
case IDENT:
|
|
throw_(parse_error, "Unexpected symbol '" << value << "'");
|
|
case VALUE:
|
|
throw_(parse_error, "Unexpected value '" << value << "'");
|
|
default:
|
|
throw_(parse_error, "Unexpected operator '" << symbol << "'");
|
|
}
|
|
}
|
|
|
|
void parser_t::token_t::unexpected(char c, char wanted)
|
|
{
|
|
if ((unsigned char) c == 0xff) {
|
|
if (wanted)
|
|
throw_(parse_error, "Missing '" << wanted << "'");
|
|
else
|
|
throw_(parse_error, "Unexpected end");
|
|
} else {
|
|
if (wanted)
|
|
throw_(parse_error, "Invalid char '" << c <<
|
|
"' (wanted '" << wanted << "')");
|
|
else
|
|
throw_(parse_error, "Invalid char '" << c << "'");
|
|
}
|
|
}
|
|
|
|
ptr_op_t
|
|
parser_t::parse_value_term(std::istream& in, scope_t& scope, const flags_t tflags) const
|
|
{
|
|
ptr_op_t node;
|
|
|
|
token_t& tok = next_token(in, tflags);
|
|
|
|
switch (tok.kind) {
|
|
case token_t::VALUE:
|
|
node = new op_t(op_t::VALUE);
|
|
node->set_value(tok.value);
|
|
break;
|
|
|
|
case token_t::IDENT: {
|
|
#if 0
|
|
#ifdef USE_BOOST_PYTHON
|
|
if (tok.value->as_string() == "lambda") // special
|
|
try {
|
|
char c, buf[4096];
|
|
|
|
std::strcpy(buf, "lambda ");
|
|
READ_INTO(in, &buf[7], 4000, c, true);
|
|
|
|
ptr_op_t eval = new op_t(op_t::O_EVAL);
|
|
ptr_op_t lambda = new op_t(op_t::FUNCTION);
|
|
lambda->functor = new python_functor_t(python_eval(buf));
|
|
eval->set_left(lambda);
|
|
ptr_op_t sym = new op_t(op_t::SYMBOL);
|
|
sym->name = new string("__ptr");
|
|
eval->set_right(sym);
|
|
|
|
node = eval;
|
|
|
|
goto done;
|
|
}
|
|
catch(const boost::python::error_already_set&) {
|
|
throw_(parse_error, "Error parsing lambda expression");
|
|
}
|
|
#endif /* USE_BOOST_PYTHON */
|
|
#endif
|
|
|
|
string ident = tok.value.as_string();
|
|
|
|
// An identifier followed by ( represents a function call
|
|
tok = next_token(in, tflags);
|
|
#if 0
|
|
if (tok.kind == token_t::LPAREN) {
|
|
node = new op_t(op_t::FUNC_NAME);
|
|
node->set_string(ident);
|
|
|
|
ptr_op_t call_node(new op_t(op_t::O_CALL));
|
|
call_node->set_left(node);
|
|
call_node->set_right(parse_value_expr(in, scope, tflags | EXPR_PARSE_PARTIAL));
|
|
|
|
tok = next_token(in, tflags);
|
|
if (tok.kind != token_t::RPAREN)
|
|
tok.unexpected(0xff, ')');
|
|
|
|
node = call_node;
|
|
} else {
|
|
if (std::isdigit(ident[0])) {
|
|
node = new op_t(op_t::ARG_INDEX);
|
|
node->set_long(lexical_cast<unsigned int>(ident.c_str()));
|
|
}
|
|
else if (optional<node_t::nameid_t> id =
|
|
document_t::lookup_builtin_id(ident)) {
|
|
node = new op_t(op_t::NODE_ID);
|
|
node->set_name(*id);
|
|
}
|
|
else {
|
|
node = new op_t(op_t::NODE_NAME);
|
|
node->set_string(ident);
|
|
}
|
|
push_token(tok);
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case token_t::AT_SYM: {
|
|
tok = next_token(in, tflags);
|
|
if (tok.kind != token_t::IDENT)
|
|
throw_(parse_error, "@ symbol must be followed by attribute name");
|
|
|
|
#if 0
|
|
string ident = tok.value.as_string();
|
|
if (optional<node_t::nameid_t> id = document_t::lookup_builtin_id(ident)) {
|
|
node = new op_t(op_t::ATTR_ID);
|
|
node->set_name(*id);
|
|
}
|
|
else {
|
|
node = new op_t(op_t::ATTR_NAME);
|
|
node->set_string(ident);
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case token_t::DOLLAR:
|
|
tok = next_token(in, tflags);
|
|
if (tok.kind != token_t::IDENT)
|
|
throw parse_error("$ symbol must be followed by variable name");
|
|
|
|
#if 0
|
|
node = new op_t(op_t::VAR_NAME);
|
|
node->set_string(tok.value.as_string());
|
|
#endif
|
|
break;
|
|
|
|
#if 0
|
|
case token_t::DOT:
|
|
node = new op_t(op_t::NODE_ID);
|
|
node->set_name(document_t::CURRENT);
|
|
break;
|
|
case token_t::DOTDOT:
|
|
node = new op_t(op_t::NODE_ID);
|
|
node->set_name(document_t::PARENT);
|
|
break;
|
|
case token_t::SLASH:
|
|
node = new op_t(op_t::NODE_ID);
|
|
node->set_name(document_t::ROOT);
|
|
push_token();
|
|
break;
|
|
case token_t::STAR:
|
|
node = new op_t(op_t::NODE_ID);
|
|
node->set_name(document_t::ALL);
|
|
break;
|
|
#endif
|
|
|
|
case token_t::LPAREN:
|
|
node = new op_t(op_t::O_COMMA);
|
|
node->set_left(parse_value_expr(in, scope, tflags | EXPR_PARSE_PARTIAL));
|
|
if (! node->left())
|
|
throw_(parse_error, tok.symbol << " operator not followed by argument");
|
|
|
|
tok = next_token(in, tflags);
|
|
if (tok.kind != token_t::RPAREN)
|
|
tok.unexpected(0xff, ')');
|
|
break;
|
|
|
|
default:
|
|
push_token(tok);
|
|
break;
|
|
}
|
|
|
|
#if 0
|
|
#ifdef USE_BOOST_PYTHON
|
|
done:
|
|
#endif
|
|
#endif
|
|
return node;
|
|
}
|
|
|
|
ptr_op_t
|
|
parser_t::parse_unary_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
|
|
{
|
|
ptr_op_t node;
|
|
|
|
token_t& tok = next_token(in, tflags);
|
|
|
|
switch (tok.kind) {
|
|
case token_t::EXCLAM: {
|
|
ptr_op_t term(parse_value_term(in, scope, tflags));
|
|
if (! term)
|
|
throw_(parse_error,
|
|
tok.symbol << " operator not followed by argument");
|
|
|
|
// A very quick optimization
|
|
if (term->kind == op_t::VALUE) {
|
|
term->as_value().in_place_negate();
|
|
node = term;
|
|
} else {
|
|
node = new op_t(op_t::O_NOT);
|
|
node->set_left(term);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case token_t::MINUS: {
|
|
ptr_op_t term(parse_value_term(in, scope, tflags));
|
|
if (! term)
|
|
throw_(parse_error,
|
|
tok.symbol << " operator not followed by argument");
|
|
|
|
// A very quick optimization
|
|
if (term->kind == op_t::VALUE) {
|
|
term->as_value().in_place_negate();
|
|
node = term;
|
|
} else {
|
|
node = new op_t(op_t::O_NEG);
|
|
node->set_left(term);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
push_token(tok);
|
|
node = parse_value_term(in, scope, tflags);
|
|
break;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
#if 0
|
|
ptr_op_t
|
|
parser_t::parse_union_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
|
|
{
|
|
ptr_op_t node(parse_unary_expr(in, scope, tflags));
|
|
|
|
if (node) {
|
|
token_t& tok = next_token(in, tflags);
|
|
if (tok.kind == token_t::PIPE || tok.kind == token_t::KW_UNION) {
|
|
ptr_op_t prev(node);
|
|
node = new op_t(op_t::O_UNION);
|
|
node->set_left(prev);
|
|
node->set_right(parse_union_expr(in, scope, tflags));
|
|
if (! node->right())
|
|
throw_(parse_error,
|
|
tok.symbol << " operator not followed by argument");
|
|
} else {
|
|
push_token(tok);
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
#endif
|
|
|
|
ptr_op_t
|
|
parser_t::parse_mul_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
|
|
{
|
|
ptr_op_t node(parse_unary_expr(in, scope, tflags));
|
|
|
|
if (node) {
|
|
token_t& tok = next_token(in, tflags);
|
|
if (tok.kind == token_t::STAR || tok.kind == token_t::KW_DIV) {
|
|
ptr_op_t prev(node);
|
|
node = new op_t(tok.kind == token_t::STAR ?
|
|
op_t::O_MUL : op_t::O_DIV);
|
|
node->set_left(prev);
|
|
node->set_right(parse_mul_expr(in, scope, tflags));
|
|
if (! node->right())
|
|
throw_(parse_error,
|
|
tok.symbol << " operator not followed by argument");
|
|
|
|
tok = next_token(in, tflags);
|
|
}
|
|
push_token(tok);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
ptr_op_t
|
|
parser_t::parse_add_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
|
|
{
|
|
ptr_op_t node(parse_mul_expr(in, scope, tflags));
|
|
|
|
if (node) {
|
|
token_t& tok = next_token(in, tflags);
|
|
if (tok.kind == token_t::PLUS ||
|
|
tok.kind == token_t::MINUS) {
|
|
ptr_op_t prev(node);
|
|
node = new op_t(tok.kind == token_t::PLUS ?
|
|
op_t::O_ADD : op_t::O_SUB);
|
|
node->set_left(prev);
|
|
node->set_right(parse_add_expr(in, scope, tflags));
|
|
if (! node->right())
|
|
throw_(parse_error,
|
|
tok.symbol << " operator not followed by argument");
|
|
|
|
tok = next_token(in, tflags);
|
|
}
|
|
push_token(tok);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
ptr_op_t
|
|
parser_t::parse_logic_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
|
|
{
|
|
ptr_op_t node(parse_add_expr(in, scope, tflags));
|
|
|
|
if (node) {
|
|
op_t::kind_t kind = op_t::LAST;
|
|
flags_t _flags = tflags;
|
|
token_t& tok = next_token(in, tflags);
|
|
switch (tok.kind) {
|
|
case token_t::EQUAL:
|
|
kind = op_t::O_EQ;
|
|
break;
|
|
case token_t::NEQUAL:
|
|
kind = op_t::O_NEQ;
|
|
break;
|
|
case token_t::LESS:
|
|
kind = op_t::O_LT;
|
|
break;
|
|
case token_t::LESSEQ:
|
|
kind = op_t::O_LTE;
|
|
break;
|
|
case token_t::GREATER:
|
|
kind = op_t::O_GT;
|
|
break;
|
|
case token_t::GREATEREQ:
|
|
kind = op_t::O_GTE;
|
|
break;
|
|
default:
|
|
push_token(tok);
|
|
break;
|
|
}
|
|
|
|
if (kind != op_t::LAST) {
|
|
ptr_op_t prev(node);
|
|
node = new op_t(kind);
|
|
node->set_left(prev);
|
|
node->set_right(parse_add_expr(in, scope, _flags));
|
|
|
|
if (! node->right()) {
|
|
if (tok.kind == token_t::PLUS)
|
|
throw_(parse_error,
|
|
tok.symbol << " operator not followed by argument");
|
|
else
|
|
throw_(parse_error,
|
|
tok.symbol << " operator not followed by argument");
|
|
}
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
ptr_op_t
|
|
parser_t::parse_and_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
|
|
{
|
|
ptr_op_t node(parse_logic_expr(in, scope, tflags));
|
|
|
|
if (node) {
|
|
token_t& tok = next_token(in, tflags);
|
|
if (tok.kind == token_t::KW_AND) {
|
|
ptr_op_t prev(node);
|
|
node = new op_t(op_t::O_AND);
|
|
node->set_left(prev);
|
|
node->set_right(parse_and_expr(in, scope, tflags));
|
|
if (! node->right())
|
|
throw_(parse_error,
|
|
tok.symbol << " operator not followed by argument");
|
|
} else {
|
|
push_token(tok);
|
|
}
|
|
}
|
|
return node;
|
|
}
|
|
|
|
ptr_op_t
|
|
parser_t::parse_or_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
|
|
{
|
|
ptr_op_t node(parse_and_expr(in, scope, tflags));
|
|
|
|
if (node) {
|
|
token_t& tok = next_token(in, tflags);
|
|
if (tok.kind == token_t::KW_OR) {
|
|
ptr_op_t prev(node);
|
|
node = new op_t(op_t::O_OR);
|
|
node->set_left(prev);
|
|
node->set_right(parse_or_expr(in, scope, tflags));
|
|
if (! node->right())
|
|
throw_(parse_error,
|
|
tok.symbol << " operator not followed by argument");
|
|
} else {
|
|
push_token(tok);
|
|
}
|
|
}
|
|
return node;
|
|
}
|
|
|
|
ptr_op_t
|
|
parser_t::parse_value_expr(std::istream& in, scope_t& scope, const flags_t tflags) const
|
|
{
|
|
ptr_op_t node(parse_or_expr(in, scope, tflags));
|
|
|
|
if (node) {
|
|
token_t& tok = next_token(in, tflags);
|
|
if (tok.kind == token_t::COMMA) {
|
|
ptr_op_t prev(node);
|
|
node = new op_t(op_t::O_COMMA);
|
|
node->set_left(prev);
|
|
node->set_right(parse_value_expr(in, scope, tflags));
|
|
if (! node->right())
|
|
throw_(parse_error,
|
|
tok.symbol << " operator not followed by argument");
|
|
tok = next_token(in, tflags);
|
|
}
|
|
|
|
if (tok.kind != token_t::TOK_EOF) {
|
|
if (tflags & EXPR_PARSE_PARTIAL)
|
|
push_token(tok);
|
|
else
|
|
tok.unexpected();
|
|
}
|
|
}
|
|
else if (! (tflags & EXPR_PARSE_PARTIAL)) {
|
|
throw_(parse_error, "Failed to parse value expression");
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
#if 0
|
|
bool print(std::ostream& out, op_t::print_context_t& context) const
|
|
{
|
|
if (ptr)
|
|
ptr->print(out, context);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
bool op_t::print(std::ostream& out, print_context_t& context) const
|
|
{
|
|
bool found = false;
|
|
|
|
if (context.start_pos && this == context.op_to_find) {
|
|
*context.start_pos = (long)out.tellp() - 1;
|
|
found = true;
|
|
}
|
|
|
|
string symbol;
|
|
|
|
switch (kind) {
|
|
case VALUE: {
|
|
const value_t& value(as_value());
|
|
switch (value.type()) {
|
|
case value_t::VOID:
|
|
out << "<VOID>";
|
|
break;
|
|
case value_t::BOOLEAN:
|
|
if (value)
|
|
out << "1";
|
|
else
|
|
out << "0";
|
|
break;
|
|
case value_t::INTEGER:
|
|
out << value;
|
|
break;
|
|
case value_t::AMOUNT:
|
|
if (! context.relaxed)
|
|
out << '{';
|
|
out << value;
|
|
if (! context.relaxed)
|
|
out << '}';
|
|
break;
|
|
case value_t::BALANCE:
|
|
case value_t::BALANCE_PAIR:
|
|
assert(false);
|
|
break;
|
|
case value_t::DATETIME:
|
|
out << '[' << value << ']';
|
|
break;
|
|
case value_t::STRING:
|
|
out << '"' << value << '"';
|
|
break;
|
|
|
|
#if 0
|
|
case value_t::XML_NODE:
|
|
out << '<' << value << '>';
|
|
break;
|
|
#endif
|
|
case value_t::POINTER:
|
|
out << '&' << value;
|
|
break;
|
|
case value_t::SEQUENCE:
|
|
out << '~' << value << '~';
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
#if 0
|
|
case ATTR_ID:
|
|
out << '@';
|
|
// fall through...
|
|
case NODE_ID: {
|
|
context_scope_t& node_scope(CONTEXT_SCOPE(context.scope));
|
|
if (optional<const char *> name =
|
|
node_scope.xml_node().document().lookup_name(as_name()))
|
|
out << *name;
|
|
else
|
|
out << '#' << as_name();
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
case NODE_NAME:
|
|
case FUNC_NAME:
|
|
out << as_string();
|
|
break;
|
|
|
|
case ATTR_NAME:
|
|
out << '@' << as_string();
|
|
break;
|
|
|
|
case VAR_NAME:
|
|
out << '$' << as_string();
|
|
break;
|
|
#endif
|
|
|
|
case FUNCTION:
|
|
out << "<FUNCTION>";
|
|
break;
|
|
|
|
case ARG_INDEX:
|
|
out << '@' << as_long();
|
|
break;
|
|
|
|
case O_NOT:
|
|
out << "!";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
break;
|
|
case O_NEG:
|
|
out << "-";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
break;
|
|
|
|
#if 0
|
|
case O_UNION:
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << " | ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
break;
|
|
#endif
|
|
|
|
case O_ADD:
|
|
out << "(";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << " + ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case O_SUB:
|
|
out << "(";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << " - ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case O_MUL:
|
|
out << "(";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << " * ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case O_DIV:
|
|
out << "(";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << " / ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
|
|
case O_NEQ:
|
|
out << "(";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << " != ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case O_EQ:
|
|
out << "(";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << " == ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case O_LT:
|
|
out << "(";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << " < ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case O_LTE:
|
|
out << "(";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << " <= ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case O_GT:
|
|
out << "(";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << " > ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case O_GTE:
|
|
out << "(";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << " >= ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
|
|
case O_AND:
|
|
out << "(";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << " & ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case O_OR:
|
|
out << "(";
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << " | ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
|
|
case O_COMMA:
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << ", ";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
break;
|
|
|
|
#if 0
|
|
case O_CALL:
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << "(";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
|
|
case O_FIND:
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << "/";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
break;
|
|
case O_RFIND:
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << "//";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
break;
|
|
case O_PRED:
|
|
if (left() && left()->print(out, context))
|
|
found = true;
|
|
out << "[";
|
|
if (right() && right()->print(out, context))
|
|
found = true;
|
|
out << "]";
|
|
break;
|
|
#endif
|
|
|
|
case LAST:
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
if (! symbol.empty()) {
|
|
if (amount_t::current_pool->find(symbol))
|
|
out << '@';
|
|
out << symbol;
|
|
}
|
|
|
|
if (context.end_pos && this == context.op_to_find)
|
|
*context.end_pos = (long)out.tellp() - 1;
|
|
|
|
return found;
|
|
}
|
|
|
|
void op_t::dump(std::ostream& out, const int depth) const
|
|
{
|
|
out.setf(std::ios::left);
|
|
out.width(10);
|
|
out << this << " ";
|
|
|
|
for (int i = 0; i < depth; i++)
|
|
out << " ";
|
|
|
|
switch (kind) {
|
|
case VALUE:
|
|
out << "VALUE - " << as_value();
|
|
break;
|
|
|
|
#if 0
|
|
case NODE_NAME:
|
|
out << "NODE_NAME - " << as_string();
|
|
break;
|
|
case NODE_ID:
|
|
out << "NODE_ID - " << as_name();
|
|
break;
|
|
|
|
case ATTR_NAME:
|
|
out << "ATTR_NAME - " << as_string();
|
|
break;
|
|
case ATTR_ID:
|
|
out << "ATTR_ID - " << as_name();
|
|
break;
|
|
|
|
case FUNC_NAME:
|
|
out << "FUNC_NAME - " << as_string();
|
|
break;
|
|
|
|
case VAR_NAME:
|
|
out << "VAR_NAME - " << as_string();
|
|
break;
|
|
#endif
|
|
|
|
case ARG_INDEX:
|
|
out << "ARG_INDEX - " << as_long();
|
|
break;
|
|
|
|
case FUNCTION:
|
|
out << "FUNCTION";
|
|
break;
|
|
|
|
#if 0
|
|
case O_CALL: out << "O_CALL"; break;
|
|
#endif
|
|
|
|
case O_NOT: out << "O_NOT"; break;
|
|
case O_NEG: out << "O_NEG"; break;
|
|
|
|
#if 0
|
|
case O_UNION: out << "O_UNION"; break;
|
|
#endif
|
|
|
|
case O_ADD: out << "O_ADD"; break;
|
|
case O_SUB: out << "O_SUB"; break;
|
|
case O_MUL: out << "O_MUL"; break;
|
|
case O_DIV: out << "O_DIV"; break;
|
|
|
|
case O_NEQ: out << "O_NEQ"; break;
|
|
case O_EQ: out << "O_EQ"; break;
|
|
case O_LT: out << "O_LT"; break;
|
|
case O_LTE: out << "O_LTE"; break;
|
|
case O_GT: out << "O_GT"; break;
|
|
case O_GTE: out << "O_GTE"; break;
|
|
|
|
case O_AND: out << "O_AND"; break;
|
|
case O_OR: out << "O_OR"; break;
|
|
|
|
case O_COMMA: out << "O_COMMA"; break;
|
|
|
|
#if 0
|
|
case O_FIND: out << "O_FIND"; break;
|
|
case O_RFIND: out << "O_RFIND"; break;
|
|
case O_PRED: out << "O_PRED"; break;
|
|
#endif
|
|
|
|
case LAST:
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
out << " (" << refc << ')' << std::endl;
|
|
|
|
if (kind > TERMINALS) {
|
|
if (left()) {
|
|
left()->dump(out, depth + 1);
|
|
if (right())
|
|
right()->dump(out, depth + 1);
|
|
} else {
|
|
assert(! right());
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
ptr_op_t parse_value_term(std::istream& in, scope_t * scope,
|
|
const short flags)
|
|
{
|
|
value_expr node;
|
|
|
|
char buf[256];
|
|
char c = peek_next_nonws(in);
|
|
|
|
if (flags & PARSE_VALEXPR_RELAXED) {
|
|
if (c == '@') {
|
|
in.get(c);
|
|
c = peek_next_nonws(in);
|
|
}
|
|
else if (! (c == '(' || c == '[' || c == '{' || c == '/')) {
|
|
amount_t temp;
|
|
char prev_c = c;
|
|
unsigned long pos = 0;
|
|
// When in relaxed parsing mode, we do want to migrate commodity
|
|
// flags, so that any precision specified by the user updates
|
|
// the current maximum precision displayed.
|
|
try {
|
|
pos = (long)in.tellg();
|
|
|
|
unsigned char parse_flags = 0;
|
|
if (flags & PARSE_VALEXPR_NO_MIGRATE)
|
|
parse_flags |= AMOUNT_PARSE_NO_MIGRATE;
|
|
if (flags & PARSE_VALEXPR_NO_REDUCE)
|
|
parse_flags |= AMOUNT_PARSE_NO_REDUCE;
|
|
|
|
temp.parse(in, parse_flags);
|
|
}
|
|
catch (amount_error * err) {
|
|
// If the amount had no commodity, it must be an unambiguous
|
|
// variable reference
|
|
if (std::strcmp(err->what(), "No quantity specified for amount") == 0) {
|
|
in.clear();
|
|
in.seekg(pos, std::ios::beg);
|
|
c = prev_c;
|
|
goto parse_ident;
|
|
} else {
|
|
throw err;
|
|
}
|
|
}
|
|
node.reset(new op_t(op_t::VALUE));
|
|
node->set_value(temp);
|
|
goto parsed;
|
|
}
|
|
}
|
|
|
|
parse_ident:
|
|
if (std::isdigit(c) || c == '.') {
|
|
READ_INTO(in, buf, 255, c, std::isdigit(c) || c == '.');
|
|
amount_t temp;
|
|
temp.parse(buf, AMOUNT_PARSE_NO_MIGRATE);
|
|
node.reset(new op_t(op_t::VALUE));
|
|
node->set_value(temp);
|
|
goto parsed;
|
|
}
|
|
else if (std::isalnum(c) || c == '_') {
|
|
bool have_args = false;
|
|
istream_pos_type beg;
|
|
|
|
READ_INTO(in, buf, 255, c, std::isalnum(c) || c == '_');
|
|
c = peek_next_nonws(in);
|
|
if (c == '(') {
|
|
in.get(c);
|
|
beg = in.tellg();
|
|
|
|
int paren_depth = 0;
|
|
while (! in.eof()) {
|
|
in.get(c);
|
|
if (c == '(' || c == '{' || c == '[')
|
|
paren_depth++;
|
|
else if (c == ')' || c == '}' || c == ']') {
|
|
if (paren_depth == 0)
|
|
break;
|
|
paren_depth--;
|
|
}
|
|
}
|
|
if (c != ')')
|
|
unexpected(c, ')');
|
|
|
|
have_args = true;
|
|
c = peek_next_nonws(in);
|
|
}
|
|
|
|
bool definition = false;
|
|
if (c == '=') {
|
|
in.get(c);
|
|
if (peek_next_nonws(in) == '=') {
|
|
in.unget();
|
|
c = '\0';
|
|
} else {
|
|
definition = true;
|
|
}
|
|
}
|
|
|
|
if (definition) {
|
|
std::auto_ptr<call_scope_t> params(new call_scope_t(*scope));
|
|
|
|
long arg_index = 0;
|
|
if (have_args) {
|
|
bool done = false;
|
|
|
|
in.clear();
|
|
in.seekg(beg, std::ios::beg);
|
|
while (! done && ! in.eof()) {
|
|
char ident[32];
|
|
READ_INTO(in, ident, 31, c, std::isalnum(c) || c == '_');
|
|
|
|
c = peek_next_nonws(in);
|
|
in.get(c);
|
|
if (c != ',' && c != ')')
|
|
unexpected(c, ')');
|
|
else if (c == ')')
|
|
done = true;
|
|
|
|
// Define the parameter so that on lookup the parser will find
|
|
// an O_ARG value.
|
|
node.reset(new op_t(op_t::O_ARG));
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(arg_index++);
|
|
params->define(ident, node.release());
|
|
}
|
|
|
|
if (peek_next_nonws(in) != '=') {
|
|
in.get(c);
|
|
unexpected(c, '=');
|
|
}
|
|
in.get(c);
|
|
}
|
|
|
|
#if 0
|
|
// Define the value associated with the defined identifier
|
|
value_expr def(parse_boolean_expr(in, scope, params.get(), flags));
|
|
if (! def.get())
|
|
throw new value_expr_error(string("Definition failed for '") + buf + "'");
|
|
|
|
node.reset(new op_t(op_t::O_DEF));
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(arg_index);
|
|
node->set_right(def.release());
|
|
#endif
|
|
|
|
scope->define(buf, node.get());
|
|
} else {
|
|
assert(scope);
|
|
ptr_op_t def = scope->lookup(buf);
|
|
if (! def) {
|
|
if (buf[1] == '\0' &&
|
|
(buf[0] == 'c' || buf[0] == 'C' || buf[0] == 'p' ||
|
|
buf[0] == 'w' || buf[0] == 'W' || buf[0] == 'e')) {
|
|
in.unget();
|
|
goto find_term;
|
|
}
|
|
throw new value_expr_error(string("Unknown identifier '") +
|
|
buf + "'");
|
|
}
|
|
else if (def->kind == op_t::O_DEF) {
|
|
node.reset(new op_t(op_t::O_REF));
|
|
node->set_left(def->right());
|
|
|
|
unsigned int count = 0;
|
|
if (have_args) {
|
|
in.clear();
|
|
in.seekg(beg, std::ios::beg);
|
|
value_expr args
|
|
(parse_value_expr(in, scope, flags | PARSE_VALEXPR_PARTIAL));
|
|
|
|
if (peek_next_nonws(in) != ')') {
|
|
in.get(c);
|
|
unexpected(c, ')');
|
|
}
|
|
in.get(c);
|
|
|
|
if (args.get()) {
|
|
count = count_leaves(args.get());
|
|
node->set_right(args.release());
|
|
}
|
|
}
|
|
|
|
if (count != def->left()->as_long()) {
|
|
std::ostringstream errmsg;
|
|
errmsg << "Wrong number of arguments to '" << buf
|
|
<< "': saw " << count
|
|
<< ", wanted " << def->left()->as_long();
|
|
throw new value_expr_error(errmsg.str());
|
|
}
|
|
}
|
|
else {
|
|
node.reset(def);
|
|
}
|
|
}
|
|
goto parsed;
|
|
}
|
|
|
|
find_term:
|
|
in.get(c);
|
|
switch (c) {
|
|
// Functions
|
|
case '^':
|
|
node.reset(new op_t(op_t::F_PARENT));
|
|
node->set_left(parse_value_term(in, scope, flags));
|
|
break;
|
|
|
|
// Other
|
|
#if 0
|
|
case 'c':
|
|
case 'C':
|
|
case 'p':
|
|
case 'w':
|
|
case 'W':
|
|
case 'e':
|
|
case '/': {
|
|
bool code_mask = c == 'c';
|
|
bool commodity_mask = c == 'C';
|
|
bool payee_mask = c == 'p';
|
|
bool note_mask = c == 'e';
|
|
bool short_account_mask = c == 'w';
|
|
|
|
if (c == '/') {
|
|
c = peek_next_nonws(in);
|
|
if (c == '/') {
|
|
in.get(c);
|
|
c = in.peek();
|
|
if (c == '/') {
|
|
in.get(c);
|
|
c = in.peek();
|
|
short_account_mask = true;
|
|
} else {
|
|
payee_mask = true;
|
|
}
|
|
}
|
|
} else {
|
|
in.get(c);
|
|
}
|
|
|
|
// Read in the regexp
|
|
READ_INTO(in, buf, 255, c, c != '/');
|
|
if (c != '/')
|
|
unexpected(c, '/');
|
|
|
|
op_t::kind_t kind;
|
|
|
|
if (short_account_mask)
|
|
kind = op_t::F_SHORT_ACCOUNT_MASK;
|
|
else if (code_mask)
|
|
kind = op_t::F_CODE_MASK;
|
|
else if (commodity_mask)
|
|
kind = op_t::F_COMMODITY_MASK;
|
|
else if (payee_mask)
|
|
kind = op_t::F_PAYEE_MASK;
|
|
else if (note_mask)
|
|
kind = op_t::F_NOTE_MASK;
|
|
else
|
|
kind = op_t::F_ACCOUNT_MASK;
|
|
|
|
in.get(c);
|
|
node.reset(new op_t(kind));
|
|
node->mask = new mask_t(buf);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
case '{': {
|
|
amount_t temp;
|
|
temp.parse(in, AMOUNT_PARSE_NO_MIGRATE);
|
|
in.get(c);
|
|
if (c != '}')
|
|
unexpected(c, '}');
|
|
|
|
node.reset(new op_t(op_t::VALUE));
|
|
node->set_value(temp);
|
|
break;
|
|
}
|
|
|
|
case '(': {
|
|
std::auto_ptr<symbol_scope_t> locals(new symbol_scope_t(*scope));
|
|
node.reset(parse_value_expr(in, locals.get(),
|
|
flags | PARSE_VALEXPR_PARTIAL));
|
|
in.get(c);
|
|
if (c != ')')
|
|
unexpected(c, ')');
|
|
break;
|
|
}
|
|
|
|
case '[': {
|
|
READ_INTO(in, buf, 255, c, c != ']');
|
|
if (c != ']')
|
|
unexpected(c, ']');
|
|
in.get(c);
|
|
|
|
interval_t timespan(buf);
|
|
node.reset(new op_t(op_t::VALUE));
|
|
node->set_value(timespan.first());
|
|
break;
|
|
}
|
|
|
|
default:
|
|
in.unget();
|
|
break;
|
|
}
|
|
|
|
parsed:
|
|
return node.release();
|
|
}
|
|
|
|
void init_value_expr()
|
|
{
|
|
global_scope.reset(new symbol_scope_t());
|
|
symbol_scope_t * globals = global_scope.get();
|
|
|
|
ptr_op_t node;
|
|
|
|
// Basic terms
|
|
node = new op_t(op_t::F_NOW);
|
|
globals->define("m", node);
|
|
globals->define("now", node);
|
|
globals->define("today", node);
|
|
|
|
node = new op_t(op_t::AMOUNT);
|
|
globals->define("a", node);
|
|
globals->define("amount", node);
|
|
|
|
node = new op_t(op_t::PRICE);
|
|
globals->define("i", node);
|
|
globals->define("price", node);
|
|
|
|
node = new op_t(op_t::COST);
|
|
globals->define("b", node);
|
|
globals->define("cost", node);
|
|
|
|
node = new op_t(op_t::DATE);
|
|
globals->define("d", node);
|
|
globals->define("date", node);
|
|
|
|
node = new op_t(op_t::ACT_DATE);
|
|
globals->define("act_date", node);
|
|
globals->define("actual_date", node);
|
|
|
|
node = new op_t(op_t::EFF_DATE);
|
|
globals->define("eff_date", node);
|
|
globals->define("effective_date", node);
|
|
|
|
node = new op_t(op_t::CLEARED);
|
|
globals->define("X", node);
|
|
globals->define("cleared", node);
|
|
|
|
node = new op_t(op_t::PENDING);
|
|
globals->define("Y", node);
|
|
globals->define("pending", node);
|
|
|
|
node = new op_t(op_t::REAL);
|
|
globals->define("R", node);
|
|
globals->define("real", node);
|
|
|
|
node = new op_t(op_t::ACTUAL);
|
|
globals->define("L", node);
|
|
globals->define("actual", node);
|
|
|
|
node = new op_t(op_t::INDEX);
|
|
globals->define("n", node);
|
|
globals->define("index", node);
|
|
|
|
node = new op_t(op_t::COUNT);
|
|
globals->define("N", node);
|
|
globals->define("count", node);
|
|
|
|
node = new op_t(op_t::DEPTH);
|
|
globals->define("l", node);
|
|
globals->define("depth", node);
|
|
|
|
node = new op_t(op_t::TOTAL);
|
|
globals->define("O", node);
|
|
globals->define("total", node);
|
|
|
|
node = new op_t(op_t::PRICE_TOTAL);
|
|
globals->define("I", node);
|
|
globals->define("total_price", node);
|
|
|
|
node = new op_t(op_t::COST_TOTAL);
|
|
globals->define("B", node);
|
|
globals->define("total_cost", node);
|
|
|
|
// Relating to format_t
|
|
globals->define("t", ptr_op_t(new op_t(op_t::VALUE_EXPR)));
|
|
globals->define("T", ptr_op_t(new op_t(op_t::TOTAL_EXPR)));
|
|
|
|
// Functions
|
|
node = new op_t(op_t::O_DEF);
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(1);
|
|
node->set_right(new op_t(op_t::F_ABS));
|
|
globals->define("U", node);
|
|
globals->define("abs", node);
|
|
|
|
node = new op_t(op_t::O_DEF);
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(1);
|
|
node->set_right(new op_t(op_t::F_ROUND));
|
|
globals->define("round", node);
|
|
|
|
node = new op_t(op_t::O_DEF);
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(1);
|
|
node->set_right(new op_t(op_t::F_QUANTITY));
|
|
globals->define("S", node);
|
|
globals->define("quant", node);
|
|
globals->define("quantity", node);
|
|
|
|
node = new op_t(op_t::O_DEF);
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(1);
|
|
node->set_right(new op_t(op_t::F_COMMODITY));
|
|
globals->define("comm", node);
|
|
globals->define("commodity", node);
|
|
|
|
node = new op_t(op_t::O_DEF);
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(2);
|
|
node->set_right(new op_t(op_t::F_SET_COMMODITY));
|
|
globals->define("setcomm", node);
|
|
globals->define("set_commodity", node);
|
|
|
|
node = new op_t(op_t::O_DEF);
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(1);
|
|
node->set_right(new op_t(op_t::F_ARITH_MEAN));
|
|
globals->define("A", node);
|
|
globals->define("avg", node);
|
|
globals->define("mean", node);
|
|
globals->define("average", node);
|
|
|
|
node = new op_t(op_t::O_DEF);
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(2);
|
|
node->set_right(new op_t(op_t::F_VALUE));
|
|
globals->define("P", node);
|
|
|
|
parse_value_definition("@value=@P(@t,@m)", globals);
|
|
parse_value_definition("@total_value=@P(@T,@m)", globals);
|
|
parse_value_definition("@valueof(x)=@P(@x,@m)", globals);
|
|
parse_value_definition("@datedvalueof(x,y)=@P(@x,@y)", globals);
|
|
|
|
node = new op_t(op_t::O_DEF);
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(1);
|
|
node->set_right(new op_t(op_t::F_PRICE));
|
|
globals->define("priceof", node);
|
|
|
|
node = new op_t(op_t::O_DEF);
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(1);
|
|
node->set_right(new op_t(op_t::F_DATE));
|
|
globals->define("dateof", node);
|
|
|
|
node = new op_t(op_t::O_DEF);
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(2);
|
|
node->set_right(new op_t(op_t::F_DATECMP));
|
|
globals->define("datecmp", node);
|
|
|
|
node = new op_t(op_t::O_DEF);
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(1);
|
|
node->set_right(new op_t(op_t::F_YEAR));
|
|
globals->define("yearof", node);
|
|
|
|
node = new op_t(op_t::O_DEF);
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(1);
|
|
node->set_right(new op_t(op_t::F_MONTH));
|
|
globals->define("monthof", node);
|
|
|
|
node = new op_t(op_t::O_DEF);
|
|
node->set_left(new op_t(op_t::ARG_INDEX));
|
|
node->left()->set_long(1);
|
|
node->set_right(new op_t(op_t::F_DAY));
|
|
globals->define("dayof", node);
|
|
|
|
parse_value_definition("@year=@yearof(@d)", globals);
|
|
parse_value_definition("@month=@monthof(@d)", globals);
|
|
parse_value_definition("@day=@dayof(@d)", globals);
|
|
|
|
// Macros
|
|
node = parse_value_expr("@P(@a,@d)");
|
|
globals->define("v", node);
|
|
globals->define("market", node);
|
|
|
|
node = parse_value_expr("@P(@O,@d)");
|
|
globals->define("V", node);
|
|
globals->define("total_market", node);
|
|
|
|
node = parse_value_expr("@v-@b");
|
|
globals->define("g", node);
|
|
globals->define("gain", node);
|
|
|
|
node = parse_value_expr("@V-@B");
|
|
globals->define("G", node);
|
|
globals->define("total_gain", node);
|
|
|
|
parse_value_definition("@min(x,y)=@x<@y?@x:@y", globals);
|
|
parse_value_definition("@max(x,y)=@x>@y?@x:@y", globals);
|
|
}
|
|
|
|
bool print_value_expr(std::ostream& out,
|
|
const ptr_op_t node,
|
|
const bool relaxed,
|
|
const ptr_op_t op_to_find,
|
|
unsigned long * start_pos,
|
|
unsigned long * end_pos)
|
|
{
|
|
bool found = false;
|
|
|
|
if (start_pos && node == op_to_find) {
|
|
*start_pos = (long)out.tellp() - 1;
|
|
found = true;
|
|
}
|
|
|
|
string symbol;
|
|
|
|
switch (node->kind) {
|
|
case op_t::ARG_INDEX:
|
|
out << node->as_long();
|
|
break;
|
|
|
|
case op_t::VALUE:
|
|
switch (node->as_value().type()) {
|
|
case value_t::BOOLEAN:
|
|
assert(false);
|
|
break;
|
|
case value_t::DATETIME:
|
|
out << '[' << node->as_value().as_datetime() << ']';
|
|
break;
|
|
case value_t::INTEGER:
|
|
case value_t::AMOUNT:
|
|
if (! relaxed)
|
|
out << '{';
|
|
out << node->as_value();
|
|
if (! relaxed)
|
|
out << '}';
|
|
break;
|
|
//case value_t::BALANCE:
|
|
//case value_t::BALANCE_PAIR:
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case op_t::AMOUNT:
|
|
symbol = "amount"; break;
|
|
case op_t::PRICE:
|
|
symbol = "price"; break;
|
|
case op_t::COST:
|
|
symbol = "cost"; break;
|
|
case op_t::DATE:
|
|
symbol = "date"; break;
|
|
case op_t::ACT_DATE:
|
|
symbol = "actual_date"; break;
|
|
case op_t::EFF_DATE:
|
|
symbol = "effective_date"; break;
|
|
case op_t::CLEARED:
|
|
symbol = "cleared"; break;
|
|
case op_t::PENDING:
|
|
symbol = "pending"; break;
|
|
case op_t::REAL:
|
|
symbol = "real"; break;
|
|
case op_t::ACTUAL:
|
|
symbol = "actual"; break;
|
|
case op_t::INDEX:
|
|
symbol = "index"; break;
|
|
case op_t::COUNT:
|
|
symbol = "count"; break;
|
|
case op_t::DEPTH:
|
|
symbol = "depth"; break;
|
|
case op_t::TOTAL:
|
|
symbol = "total"; break;
|
|
case op_t::PRICE_TOTAL:
|
|
symbol = "total_price"; break;
|
|
case op_t::COST_TOTAL:
|
|
symbol = "total_cost"; break;
|
|
case op_t::F_NOW:
|
|
symbol = "now"; break;
|
|
|
|
case op_t::VALUE_EXPR:
|
|
if (print_value_expr(out, amount_expr.get(), relaxed,
|
|
op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
break;
|
|
case op_t::TOTAL_EXPR:
|
|
if (print_value_expr(out, total_expr.get(), relaxed,
|
|
op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
break;
|
|
|
|
case op_t::F_ARITH_MEAN:
|
|
symbol = "average"; break;
|
|
case op_t::F_ABS:
|
|
symbol = "abs"; break;
|
|
case op_t::F_QUANTITY:
|
|
symbol = "quantity"; break;
|
|
case op_t::F_COMMODITY:
|
|
symbol = "commodity"; break;
|
|
case op_t::F_SET_COMMODITY:
|
|
symbol = "set_commodity"; break;
|
|
case op_t::F_VALUE:
|
|
symbol = "valueof"; break;
|
|
case op_t::F_PRICE:
|
|
symbol = "priceof"; break;
|
|
case op_t::F_DATE:
|
|
symbol = "dateof"; break;
|
|
case op_t::F_DATECMP:
|
|
symbol = "datecmp"; break;
|
|
case op_t::F_YEAR:
|
|
symbol = "yearof"; break;
|
|
case op_t::F_MONTH:
|
|
symbol = "monthof"; break;
|
|
case op_t::F_DAY:
|
|
symbol = "dayof"; break;
|
|
|
|
#if 0
|
|
case op_t::F_CODE_MASK:
|
|
out << "c/" << node->mask->expr.str() << "/";
|
|
break;
|
|
case op_t::F_PAYEE_MASK:
|
|
out << "p/" << node->mask->expr.str() << "/";
|
|
break;
|
|
case op_t::F_NOTE_MASK:
|
|
out << "e/" << node->mask->expr.str() << "/";
|
|
break;
|
|
case op_t::F_ACCOUNT_MASK:
|
|
out << "W/" << node->mask->expr.str() << "/";
|
|
break;
|
|
case op_t::F_SHORT_ACCOUNT_MASK:
|
|
out << "w/" << node->mask->expr.str() << "/";
|
|
break;
|
|
case op_t::F_COMMODITY_MASK:
|
|
out << "C/" << node->mask->expr.str() << "/";
|
|
break;
|
|
#endif
|
|
|
|
case op_t::O_NOT:
|
|
out << "!";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
break;
|
|
case op_t::O_NEG:
|
|
out << "-";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
break;
|
|
case op_t::O_PERC:
|
|
out << "%";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
break;
|
|
|
|
case op_t::O_ARG:
|
|
out << "@arg" << node->as_long();
|
|
break;
|
|
case op_t::O_DEF:
|
|
out << "<def args=\"";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << "\" value=\"";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << "\">";
|
|
break;
|
|
|
|
case op_t::O_REF:
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
if (node->right()) {
|
|
out << "(";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
}
|
|
break;
|
|
|
|
case op_t::O_COM:
|
|
if (node->left() &&
|
|
print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ", ";
|
|
if (node->right() &&
|
|
print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
break;
|
|
case op_t::O_QUES:
|
|
out << "(";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " ? ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case op_t::O_COL:
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " : ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
break;
|
|
|
|
case op_t::O_AND:
|
|
out << "(";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " & ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case op_t::O_OR:
|
|
out << "(";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " | ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
|
|
case op_t::O_NEQ:
|
|
out << "(";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " != ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case op_t::O_EQ:
|
|
out << "(";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " == ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case op_t::O_LT:
|
|
out << "(";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " < ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case op_t::O_LTE:
|
|
out << "(";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " <= ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case op_t::O_GT:
|
|
out << "(";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " > ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case op_t::O_GTE:
|
|
out << "(";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " >= ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
|
|
case op_t::O_ADD:
|
|
out << "(";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " + ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case op_t::O_SUB:
|
|
out << "(";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " - ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case op_t::O_MUL:
|
|
out << "(";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " * ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
case op_t::O_DIV:
|
|
out << "(";
|
|
if (print_value_expr(out, node->left(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << " / ";
|
|
if (print_value_expr(out, node->right(), relaxed, op_to_find, start_pos, end_pos))
|
|
found = true;
|
|
out << ")";
|
|
break;
|
|
|
|
case op_t::LAST:
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
if (! symbol.empty()) {
|
|
if (amount_t::current_pool->find(symbol))
|
|
out << '@';
|
|
out << symbol;
|
|
}
|
|
|
|
if (end_pos && node == op_to_find)
|
|
*end_pos = (long)out.tellp() - 1;
|
|
|
|
return found;
|
|
}
|
|
|
|
void dump_value_expr(std::ostream& out, const ptr_op_t node,
|
|
const int depth)
|
|
{
|
|
out.setf(std::ios::left);
|
|
out.width(10);
|
|
out << node << " ";
|
|
|
|
for (int i = 0; i < depth; i++)
|
|
out << " ";
|
|
|
|
switch (node->kind) {
|
|
case op_t::ARG_INDEX:
|
|
out << "ARG_INDEX - " << node->as_long();
|
|
break;
|
|
case op_t::VALUE:
|
|
out << "VALUE - " << node->as_value();
|
|
break;
|
|
|
|
case op_t::AMOUNT: out << "AMOUNT"; break;
|
|
case op_t::PRICE: out << "PRICE"; break;
|
|
case op_t::COST: out << "COST"; break;
|
|
case op_t::DATE: out << "DATE"; break;
|
|
case op_t::ACT_DATE: out << "ACT_DATE"; break;
|
|
case op_t::EFF_DATE: out << "EFF_DATE"; break;
|
|
case op_t::CLEARED: out << "CLEARED"; break;
|
|
case op_t::PENDING: out << "PENDING"; break;
|
|
case op_t::REAL: out << "REAL"; break;
|
|
case op_t::ACTUAL: out << "ACTUAL"; break;
|
|
case op_t::INDEX: out << "INDEX"; break;
|
|
case op_t::COUNT: out << "COUNT"; break;
|
|
case op_t::DEPTH: out << "DEPTH"; break;
|
|
case op_t::TOTAL: out << "TOTAL"; break;
|
|
case op_t::PRICE_TOTAL: out << "PRICE_TOTAL"; break;
|
|
case op_t::COST_TOTAL: out << "COST_TOTAL"; break;
|
|
|
|
case op_t::VALUE_EXPR: out << "VALUE_EXPR"; break;
|
|
case op_t::TOTAL_EXPR: out << "TOTAL_EXPR"; break;
|
|
|
|
case op_t::F_NOW: out << "F_NOW"; break;
|
|
case op_t::F_ARITH_MEAN: out << "F_ARITH_MEAN"; break;
|
|
case op_t::F_ABS: out << "F_ABS"; break;
|
|
case op_t::F_QUANTITY: out << "F_QUANTITY"; break;
|
|
case op_t::F_COMMODITY: out << "F_COMMODITY"; break;
|
|
case op_t::F_SET_COMMODITY: out << "F_SET_COMMODITY"; break;
|
|
case op_t::F_CODE_MASK: out << "F_CODE_MASK"; break;
|
|
case op_t::F_PAYEE_MASK: out << "F_PAYEE_MASK"; break;
|
|
case op_t::F_NOTE_MASK: out << "F_NOTE_MASK"; break;
|
|
case op_t::F_ACCOUNT_MASK:
|
|
out << "F_ACCOUNT_MASK"; break;
|
|
case op_t::F_SHORT_ACCOUNT_MASK:
|
|
out << "F_SHORT_ACCOUNT_MASK"; break;
|
|
case op_t::F_COMMODITY_MASK:
|
|
out << "F_COMMODITY_MASK"; break;
|
|
case op_t::F_VALUE: out << "F_VALUE"; break;
|
|
case op_t::F_PRICE: out << "F_PRICE"; break;
|
|
case op_t::F_DATE: out << "F_DATE"; break;
|
|
case op_t::F_DATECMP: out << "F_DATECMP"; break;
|
|
case op_t::F_YEAR: out << "F_YEAR"; break;
|
|
case op_t::F_MONTH: out << "F_MONTH"; break;
|
|
case op_t::F_DAY: out << "F_DAY"; break;
|
|
|
|
case op_t::O_NOT: out << "O_NOT"; break;
|
|
case op_t::O_ARG: out << "O_ARG"; break;
|
|
case op_t::O_DEF: out << "O_DEF"; break;
|
|
case op_t::O_REF: out << "O_REF"; break;
|
|
case op_t::O_COM: out << "O_COM"; break;
|
|
case op_t::O_QUES: out << "O_QUES"; break;
|
|
case op_t::O_COL: out << "O_COL"; break;
|
|
case op_t::O_AND: out << "O_AND"; break;
|
|
case op_t::O_OR: out << "O_OR"; break;
|
|
case op_t::O_NEQ: out << "O_NEQ"; break;
|
|
case op_t::O_EQ: out << "O_EQ"; break;
|
|
case op_t::O_LT: out << "O_LT"; break;
|
|
case op_t::O_LTE: out << "O_LTE"; break;
|
|
case op_t::O_GT: out << "O_GT"; break;
|
|
case op_t::O_GTE: out << "O_GTE"; break;
|
|
case op_t::O_NEG: out << "O_NEG"; break;
|
|
case op_t::O_ADD: out << "O_ADD"; break;
|
|
case op_t::O_SUB: out << "O_SUB"; break;
|
|
case op_t::O_MUL: out << "O_MUL"; break;
|
|
case op_t::O_DIV: out << "O_DIV"; break;
|
|
case op_t::O_PERC: out << "O_PERC"; break;
|
|
|
|
case op_t::LAST:
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
out << " (" << node->refc << ')' << std::endl;
|
|
|
|
if (node->kind > op_t::TERMINALS) {
|
|
dump_value_expr(out, node->left(), depth + 1);
|
|
if (node->right())
|
|
dump_value_expr(out, node->right(), depth + 1);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
} // namespace expr
|
|
} // namespace ledger
|