From d4e3c6be764dfa0d70fa96847d1325bcd37bc0cc Mon Sep 17 00:00:00 2001 From: Igbanam Ogbuluijah <390059+igbanam@users.noreply.github.com> Date: Tue, 25 Jun 2024 09:34:03 +0000 Subject: Fix Query Parser for Automated Transactions (#1) * Add failing test for use case TBH I don't know what I'm doing here, but this seems to fail for the right reasons enough to reflect the parser bug here. * Append to the ident on a closing brace ')' When parsing the automated rule, a scanner reads the line left-to-right char-by-char. The default behaviour is to append the char under the cursor to some `ident` string. When the cursor is on a ')', it skips the default handling and switches into some special handling: it tries to test the string it's reading if it's one of the keywords it knows, to select which type of token just got scanned. If what was scanned is not a known token, it defaults to `token_t::TERM` and returns a new token with the currently accumulated `ident` as a `token_t::TERM`. Issue is, since it skipped the appending to do some custom handling, the `token_t::TERM` will always be without its closing brace. The scanner needs to append the character under the cursor if it's falling through to default processing. * fix test case - ensure proper spacing for the posting to have an amount - ensure the posting balances against an account - the meaning of the number after `->` is the exit code * undo wrong approach * consume_next if unbalanced_braces * how this can be extended --- src/query.cc | 13 +++++++++++++ src/query.h | 2 ++ test/regress/1182_3.test | 14 ++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 test/regress/1182_3.test diff --git a/src/query.cc b/src/query.cc index 705ba151..a157b4d1 100644 --- a/src/query.cc +++ b/src/query.cc @@ -36,6 +36,17 @@ namespace ledger { +// TODO: Extend this to handle all characters which define enclosures +bool query_t::lexer_t::unbalanced_braces(string str) { + int balance = 0; + for (char& c : str) { + if (c == '(') ++balance; + else if (c == ')') + if (--balance < 0) return true; + } + return balance != 0; +} + query_t::lexer_t::token_t query_t::lexer_t::next_token(query_t::lexer_t::token_t::kind_t tok_context) { @@ -157,6 +168,8 @@ query_t::lexer_t::next_token(query_t::lexer_t::token_t::kind_t tok_context) break; case ')': + if (unbalanced_braces(ident)) + consume_next = true; if (! consume_next && tok_context == token_t::TOK_EXPR) goto test_ident; // fall through... diff --git a/src/query.h b/src/query.h index 20a7bc18..581c7533 100644 --- a/src/query.h +++ b/src/query.h @@ -231,6 +231,8 @@ public: token_cache = next_token(tok_context); return token_cache; } + + bool unbalanced_braces(const string str); }; enum kind_t { diff --git a/test/regress/1182_3.test b/test/regress/1182_3.test new file mode 100644 index 00000000..cc7cf06c --- /dev/null +++ b/test/regress/1182_3.test @@ -0,0 +1,14 @@ += expr has_tag('SecondTag') + Expenses:Cookies $1 + $account -$1 + +2004/05/27 Book Store + ; This note applies to all postings. :SecondTag: + Expenses:Books 20 BOOK @ $10 + ; Metadata: Some Value + ; Typed:: $100 + $200 + ; :ExampleTag: + ; Here follows a note describing the posting. + Liabilities:MasterCard $-200.00 + +test bal Expenses:Cookies -> 0 -- cgit v1.2.3