summaryrefslogtreecommitdiff
path: root/src/token.cc
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2009-01-22 17:01:46 -0400
committerJohn Wiegley <johnw@newartisans.com>2009-01-22 17:01:46 -0400
commitfac5a95a486a1c0fdba3b8563503c977683888cf (patch)
tree61ca860ffa1e26dec483fc4cfa33c0c6c84ea2d0 /src/token.cc
parent0b9f22b4d24e8fa545af2d7d448ddfe9fb3736ba (diff)
downloadfork-ledger-fac5a95a486a1c0fdba3b8563503c977683888cf.tar.gz
fork-ledger-fac5a95a486a1c0fdba3b8563503c977683888cf.tar.bz2
fork-ledger-fac5a95a486a1c0fdba3b8563503c977683888cf.zip
Accept &&/and for &, ||/or for |, and not for !. Also improved error
reporting in the tokenizer.
Diffstat (limited to 'src/token.cc')
-rw-r--r--src/token.cc152
1 files changed, 111 insertions, 41 deletions
diff --git a/src/token.cc b/src/token.cc
index 66125b93..51eb4623 100644
--- a/src/token.cc
+++ b/src/token.cc
@@ -34,46 +34,77 @@
namespace ledger {
-void expr_t::token_t::parse_ident(std::istream& in)
+int expr_t::token_t::parse_reserved_word(std::istream& in)
{
- if (in.eof()) {
- kind = TOK_EOF;
- return;
- }
- assert(in.good());
+ char c = in.peek();
- char c = peek_next_nonws(in);
+ if (c == 'a' || c == 'f' || c == 'o' || c == 'n' || c == 't') {
+ length = 0;
- if (in.eof()) {
- kind = TOK_EOF;
- return;
+ 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) {
+ symbol[0] = '&';
+ symbol[1] = '\0';
+ kind = KW_AND;
+ return 1;
+ }
+ break;
+
+ case 'f':
+ if (std::strcmp(buf, "false") == 0) {
+ kind = VALUE;
+ value = false;
+ return 1;
+ }
+ break;
+
+ case 'o':
+ if (std::strcmp(buf, "or") == 0) {
+ symbol[0] = '|';
+ symbol[1] = '\0';
+ kind = KW_OR;
+ return 1;
+ }
+ break;
+
+ case 'n':
+ if (std::strcmp(buf, "not") == 0) {
+ symbol[0] = '!';
+ symbol[1] = '\0';
+ kind = EXCLAM;
+ return 1;
+ }
+ break;
+
+ case 't':
+ if (std::strcmp(buf, "true") == 0) {
+ kind = VALUE;
+ value = true;
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
}
- assert(in.good());
+ return -1;
+}
+void expr_t::token_t::parse_ident(std::istream& in)
+{
kind = IDENT;
length = 0;
- char buf[256];
+ char c, buf[256];
READ_INTO_(in, buf, 255, c, length,
std::isalnum(c) || c == '_' || c == '.' || c == '-');
- switch (buf[0]) {
- case 'f':
- if (std::strcmp(buf, "false") == 0) {
- kind = VALUE;
- value = false;
- }
- break;
- case 't':
- if (std::strcmp(buf, "true") == 0) {
- kind = VALUE;
- value = true;
- }
- break;
- }
-
- if (kind == IDENT)
- value.set_string(buf);
+ value.set_string(buf);
}
void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
@@ -82,7 +113,8 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
kind = TOK_EOF;
return;
}
- assert(in.good());
+ if (! in.good())
+ throw_(parse_error, "Input stream no longer valid");
char c = peek_next_nonws(in);
@@ -90,7 +122,8 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
kind = TOK_EOF;
return;
}
- assert(in.good());
+ if (! in.good())
+ throw_(parse_error, "Input stream no longer valid");
symbol[0] = c;
symbol[1] = '\0';
@@ -100,10 +133,22 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
switch (c) {
case '&':
in.get(c);
+ if (c == '&') {
+ in.get(c);
+ kind = KW_AND;
+ length = 2;
+ break;
+ }
kind = KW_AND;
break;
case '|':
in.get(c);
+ if (c == '|') {
+ in.get(c);
+ kind = KW_OR;
+ length = 2;
+ break;
+ }
kind = KW_OR;
break;
@@ -201,7 +246,6 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
in.get(c);
kind = QUERY;
break;
-
case ':':
in.get(c);
kind = COLON;
@@ -234,6 +278,14 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
length = 2;
break;
}
+ else if (c == '=') {
+ in.get(c);
+ symbol[1] = c;
+ symbol[2] = '\0';
+ kind = EQUAL;
+ length = 2;
+ break;
+ }
kind = EQUAL;
break;
@@ -269,29 +321,46 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
break;
default: {
- amount_t temp;
- istream_pos_type pos = 0;
+ istream_pos_type pos = in.tellg();
- // 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.
- pos = in.tellg();
+ // First, check to see if it's a reserved word, such as: and or not
+ int result = parse_reserved_word(in);
+ if (std::isalpha(c) && result == 1)
+ break;
+ // If not, rewind back to the beginning of the word to scan it
+ // again. If the result was -1, it means no identifier was scanned
+ // so we don't have to rewind.
+ if (result == 0) {
+ in.clear();
+ in.seekg(pos, std::ios::beg);
+ if (in.fail())
+ throw_(parse_error, "Failed to reset input stream");
+ }
+
+ // 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.
amount_t::flags_t parse_flags = 0;
if (pflags & EXPR_PARSE_NO_MIGRATE)
parse_flags |= AMOUNT_PARSE_NO_MIGRATE;
if (pflags & EXPR_PARSE_NO_REDUCE)
parse_flags |= AMOUNT_PARSE_NO_REDUCE;
+ amount_t temp;
if (! temp.parse(in, parse_flags | AMOUNT_PARSE_SOFT_FAIL)) {
// If the amount had no commodity, it must be an unambiguous
// variable reference
in.clear();
in.seekg(pos, std::ios::beg);
+ if (in.fail())
+ throw_(parse_error, "Failed to reset input stream");
c = in.peek();
- assert(! (std::isdigit(c) || c == '.'));
+ if (std::isdigit(c) || c == '.')
+ expected('\0', c);
+
parse_ident(in);
} else {
kind = VALUE;
@@ -304,8 +373,9 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
void expr_t::token_t::rewind(std::istream& in)
{
- for (unsigned int i = 0; i < length; i++)
- in.unget();
+ in.seekg(- length, std::ios::cur);
+ if (in.fail())
+ throw_(parse_error, "Failed to rewind input stream");
}