from datetime import datetime from journal_lib.parse.parsewrapper import ParseWrapper from journal_lib.parse.lexers.l_ledger import JournalLexer from journal_lib.dataclasses import Journal, JournalEntry, JournalEntryTransaction, JournalAccountDef, JournalCommodityDef class JournalParser(ParseWrapper): tokens = JournalLexer.tokens def p_journal(self, p): '''journal : elements ''' p[0] = Journal.from_elements(p[1]) if self.debug: for x in p[1]: print(repr(x)) def p_elements(self, p): '''elements : elements element | element''' if len(p) == 3: p[0] = p[1] + [p[2]] else: p[0] = [p[1]] def p_element_entry(self, p): '''element : DATE effective_date status TEXT transactions''' p[0] = JournalEntry( date = p[1], effective_date = p[2], cleared = p[3]['cleared'], pending = p[3]['pending'], title = p[4], transactions = p[5]['transactions'], comments = p[5]['comments'] ) def p_element_account(self, p): '''element : KW_ACCOUNT TEXT COMMENT | KW_ACCOUNT TEXT''' p[0] = JournalAccountDef(account=p[2], comment=p[3] if len(p) > 3 else None) def p_element_commodity(self, p): '''element : KW_COMMODITY TEXT commodity_attributes ''' p[0] = JournalCommodityDef(commodity=p[2], **p[3]) def p_commodity_attributes(self, p): '''commodity_attributes : COMMODITY_FORMAT TEXT commodity_attributes | COMMODITY_NOTE TEXT commodity_attributes | COMMODITY_NOMARKET commodity_attributes | COMMODITY_DEFAULT commodity_attributes | empty ''' p[0] = {} if p[1] == "format": p[0]["format"] = p[2] elif p[1] == "note": p[0]["note"] = p[2] elif p[1] == "nomarket": p[0]["nomarket"] = True elif p[1] == "default": p[0]["default"] = True if len(p) > 3 and p[3]: p[0].update(p[3]) def p_effective_date(self, p): '''effective_date : ENTRY_EFFECTIVE_DATE_SEPARATOR DATE | empty ''' p[0] = p[2] if p[1] else None def p_status(self, p): '''status : ENTRY_STATUS | empty ''' p[0] = {'cleared': p[1] == "*", 'pending': p[1] == "!"} if p[1] else {'cleared': False, 'pending': False} def p_empty(self, p): '''empty :''' pass def p_transactions(self, p): '''transactions : transactions transaction''' p[0] = {"comments": p[1]['comments'], "transactions": p[1]['transactions'] + [p[2]]} def p_comments(self, p): '''transactions : transactions COMMENT''' p[0] = {"comments": p[1]['comments'] + [p[2]], "transactions": p[1]['transactions']} def p_transactions_single(self, p): '''transactions : transaction ''' p[0] = {"comments": [], "transactions": [p[1]]} def p_transactions_comment_single(self, p): '''transactions : COMMENT ''' p[0] = {"comments": [p[1]], "transactions": []} def p_amount_prefixed(self, p): '''amount : CURRENCY AMOUNT''' p[0] = {'currency': p[1], 'amount': p[2]} def p_amount_suffixed(self, p): '''amount : AMOUNT CURRENCY''' p[0] = {'currency': p[2], 'amount': p[1]} def p_transaction_with_amount(self, p): '''transaction : TEXT amount INLINE_COMMENT | TEXT amount''' p[0] = JournalEntryTransaction( account = p[1], currency = p[2]['currency'], amount = p[2]['amount'], comment = p[3] if len(p) > 3 else None ) def p_transaction_without_amount(self, p): '''transaction : TEXT INLINE_COMMENT | TEXT''' p[0] = JournalEntryTransaction( account = p[1], currency = None, amount = None, comment = p[2] if len(p) > 2 else None ) def p_error(self, p): if p: self._hl_token(p) else: print("Syntax error at EOF")