summaryrefslogtreecommitdiff
path: root/parse.cc
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2003-09-29 07:06:02 +0000
committerJohn Wiegley <johnw@newartisans.com>2003-09-29 07:06:02 +0000
commite95ea133d0953953ba74f4e5c6163706194971cb (patch)
treeb134c7092820e96d4da40c0b56bc8493e0043085 /parse.cc
downloadfork-ledger-e95ea133d0953953ba74f4e5c6163706194971cb.tar.gz
fork-ledger-e95ea133d0953953ba74f4e5c6163706194971cb.tar.bz2
fork-ledger-e95ea133d0953953ba74f4e5c6163706194971cb.zip
Initial revision
Diffstat (limited to 'parse.cc')
-rw-r--r--parse.cc189
1 files changed, 189 insertions, 0 deletions
diff --git a/parse.cc b/parse.cc
new file mode 100644
index 00000000..3f18b8f6
--- /dev/null
+++ b/parse.cc
@@ -0,0 +1,189 @@
+#include <iostream>
+#include <vector>
+#include <cstring>
+#include <ctime>
+#include <cctype>
+#include <cassert>
+
+#include <pcre.h> // Perl regular expression library
+
+#include "ledger.h"
+
+namespace ledger {
+
+//////////////////////////////////////////////////////////////////////
+//
+// Ledger parser
+//
+
+char * next_element(char * buf, bool variable = false)
+{
+ char * p;
+
+ if (variable)
+ p = std::strstr(buf, " ");
+ else
+ p = std::strchr(buf, ' ');
+
+ if (! p)
+ return NULL;
+
+ *p++ = '\0';
+ while (std::isspace(*p))
+ p++;
+
+ return p;
+}
+
+static int linenum = 0;
+
+void finalize_entry(entry * curr, std::vector<entry *>& ledger)
+{
+ if (curr) {
+ if (! curr->validate()) {
+ std::cerr << "Failed to balance the following transaction, "
+ << "ending on line " << (linenum - 1) << std::endl;
+ curr->print(std::cerr);
+ } else {
+ ledger.push_back(curr);
+ }
+ }
+}
+
+bool parse_ledger(std::istream& in, std::vector<entry *>& ledger)
+{
+ static std::time_t now = std::time(NULL);
+ static struct std::tm * now_tm = std::localtime(&now);
+ static int current_year = now_tm->tm_year + 1900;
+
+ static char line[1024];
+
+ static struct std::tm moment;
+ memset(&moment, 0, sizeof(struct std::tm));
+
+ entry * curr = NULL;
+
+ // Compile the regular expression used for parsing amounts
+ static pcre * entry_re = NULL;
+ if (! entry_re) {
+ const char *error;
+ int erroffset;
+ static const std::string regexp =
+ "^(([0-9]{4})[./])?([0-9]{2})[./]([0-9]{2})\\s+(\\*\\s+)?"
+ "(\\(([^)]+)\\)\\s+)?(.+)";
+ entry_re = pcre_compile(regexp.c_str(), 0, &error, &erroffset, NULL);
+ }
+
+ while (! in.eof()) {
+ in.getline(line, 1023);
+ linenum++;
+
+ if (in.eof()) {
+ break;
+ }
+ else if (std::isdigit(line[0])) {
+ static char buf[256];
+ int ovector[60];
+
+ int matched = pcre_exec(entry_re, NULL, line, std::strlen(line),
+ 0, 0, ovector, 60);
+ if (! matched) {
+ std::cerr << "Failed to parse, line " << linenum << ": "
+ << line << std::endl;
+ continue;
+ }
+
+ if (curr)
+ finalize_entry(curr, ledger);
+ curr = new entry;
+
+ // Parse the date
+
+ int mday, mon, year = current_year;
+
+ if (ovector[1 * 2] >= 0) {
+ pcre_copy_substring(line, ovector, matched, 2, buf, 255);
+ year = std::atoi(buf);
+ }
+
+ if (ovector[3 * 2] >= 0) {
+ pcre_copy_substring(line, ovector, matched, 3, buf, 255);
+ mon = std::atoi(buf);
+ }
+
+ if (ovector[4 * 2] >= 0) {
+ pcre_copy_substring(line, ovector, matched, 4, buf, 255);
+ mday = std::atoi(buf);
+ }
+
+ moment.tm_mday = mday;
+ moment.tm_mon = mon - 1;
+ moment.tm_year = year - 1900;
+
+ curr->date = std::mktime(&moment);
+
+ if (ovector[5 * 2] >= 0)
+ curr->cleared = true;
+
+ if (ovector[6 * 2] >= 0) {
+ pcre_copy_substring(line, ovector, matched, 7, buf, 255);
+ curr->code = buf;
+ }
+
+ if (ovector[8 * 2] >= 0) {
+ int result = pcre_copy_substring(line, ovector, matched, 8, buf, 255);
+ assert(result >= 0);
+ curr->desc = buf;
+ }
+ }
+ else if (std::isspace(line[0])) {
+ transaction * xact = new transaction();
+
+ xact->cost = create_amount(next_element(line, true));
+
+ // jww (2003-09-28): Reverse parse the account name to find the
+ // correct account. This means that each account needs to know
+ // its children.
+ account * current = NULL;
+ for (char * tok = std::strtok(line, ":");
+ tok;
+ tok = std::strtok(NULL, ":")) {
+ if (! current) {
+ accounts_iterator i = accounts.find(tok);
+ if (i == accounts.end()) {
+ current = new account(tok);
+ accounts.insert(accounts_entry(tok, current));
+ } else {
+ current = (*i).second;
+ }
+ } else {
+ account::iterator i = current->children.find(tok);
+ if (i == current->children.end()) {
+ current = new account(tok, current);
+ current->parent->children.insert(accounts_entry(tok, current));
+ } else {
+ current = (*i).second;
+ }
+ }
+
+ // Apply transaction to account (and all parent accounts)
+
+ assert(current);
+ current->credit(curr, xact->cost);
+ }
+ xact->acct = current;
+
+ curr->xacts.push_back(xact);
+ }
+ else if (line[0] == 'Y') {
+ current_year = std::atoi(line + 2);
+ }
+ }
+
+ if (curr)
+ finalize_entry(curr, ledger);
+
+ return true;
+}
+
+} // namespace ledger