commit 8b5eb06ff3db5f05ca5bebc5a57da3eb298ee719
parent 2a0b60d627b59c3a4eff1f72306707e246bfafb8
Author: Bharatvaj Hemanth <bharatvaj@yahoo.com>
Date: Wed, 26 Mar 2025 07:28:42 +0530
Amount, sign and denom parsing works!
Add support decimals with upto two decimal places
Move commented hot-reload code from payredu.c to hot.c
Fix hot-reload debugging
Diffstat:
M | CHANGELOG | | | 9 | ++++++++- |
M | Makefile | | | 4 | ++-- |
M | book.c | | | 206 | ++++++++++++++++++++++++++++++++++++++++++++++--------------------------------- |
M | hot.c | | | 47 | +++++++++++++++++++++++++++++++++++++++++++---- |
M | october-2023.txt | | | 2 | +- |
M | payredu.c | | | 42 | ++---------------------------------------- |
6 files changed, 176 insertions(+), 134 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
@@ -1,6 +1,13 @@
+0.2 (2025/03/27)
+---
+- Amount, sign and denom parsing works!
+- Add support decimals with upto two decimal places
+- Move commented hot-reload code from payredu.c to hot.c
+- Fix hot-reload debugging
+
Dark Ages
---------
-- Starting keeping CHANGELOG file for payredo
+- Start keeping CHANGELOG file for payredu
- Basic combined parser/lexer for the ledger format has been
implemented in book.c
- Fix Makefile issue with tests/tests.mk
diff --git a/Makefile b/Makefile
@@ -19,7 +19,7 @@ bal:
balance: balance.c ledger.h
-hot: hot.c libbalance.so
+hot: hot.c libbook.so
libbook.a: book.o account.o
ar cr $@ $>
@@ -36,4 +36,4 @@ format:
include tests/tests.mk
clean:
- -rm *.so *.o hotbook libbook.so $(TESTS)
+ -rm *.so *.o hotbook libbook.a libbook.so $(TESTS)
diff --git a/book.c b/book.c
@@ -4,27 +4,13 @@
#include <inttypes.h>
#include <ctype.h>
#include <limits.h>
+#include <errno.h>
+
#include "common.h"
#include "strn.h"
#include <unistd.h>
-#ifdef ENABLE_GUI
-#include <GLFW/glfw3.h>
-#define NK_INCLUDE_FIXED_TYPES
-#define NK_INCLUDE_STANDARD_IO
-#define NK_INCLUDE_STANDARD_VARARGS
-#define NK_INCLUDE_DEFAULT_ALLOCATOR
-#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
-#define NK_INCLUDE_FONT_BAKING
-#define NK_INCLUDE_DEFAULT_FONT
-#define NK_IMPLEMENTATION
-#define NK_GLFW_GL2_IMPLEMENTATION
-#define NK_KEYSTATE_BASED_INPUT
-#include <nuklear.h>
-#include <nuklear_glfw_gl2.h>
-#endif
-
#define _XOPEN_SOURCE
#include <time.h>
@@ -74,7 +60,8 @@ map_tree_t *rootp = NULL;
typedef struct {
vstr_t *denom;
- size_t amount;
+ size_t quantity;
+ int8_t decimal;
} LedgerValue;
typedef struct {
@@ -88,7 +75,8 @@ typedef struct {
LedgerRecord **records;
} LedgerEntry;
-time_t ledger_timestamp_from_ledger_date(char *date_str)
+time_t
+ledger_timestamp_from_ledger_date(char *date_str)
{
// converts string 'YYYY-MM-DD' to unix timestamp
// date_str should be exactly 10 characters
@@ -108,12 +96,8 @@ const char *states_str[] = {
"ENTRY START",
"ENTRY SPACE",
"ENTRY WHO",
- "ENTRY SIGN",
- "ENTRY SIGN OR AMOUNT",
"ENTRY AMOUNT",
"ENTRY DENOM",
- "ENTRY DENOM OR AMOUNT",
- "ENTRY SIGN OR DENOM OR AMOUNT",
"ENTRY END",
};
@@ -123,19 +107,19 @@ typedef enum {
ENTRY_START, // entry starts after a comment
ENTRY_SPACE,
ENTRY_WHO,
- ENTRY_SIGN,
- ENTRY_SIGN_AMOUNT,
ENTRY_AMOUNT,
ENTRY_DENOM ,
- ENTRY_DENOM_AMOUNT,
- ENTRY_SIGN_DENOM_AMOUNT,
ENTRY_END, // finish up entry if encountering any \n\n or \n text_len == i or text_len == i, otherwise set state to ENTRY_SPACE
POSTING_END,
} LedgerParseStates;
-void ledger_parse_data(char *text, size_t text_len)
+void
+ledger_parse_data(char *text, size_t text_len)
{
+ char *denom_list[256];
+ memset(denom_list, 0, 256);
+ char* denomptr = NULL;
setvbuf(stdout, NULL, _IONBF, 0);
LedgerParseStates state = DATE;
size_t line_no = 1;
@@ -148,14 +132,17 @@ void ledger_parse_data(char *text, size_t text_len)
time_t hold_date;
vstr_t hold_comment = { 0 };
vstr_t hold_register = { 0 };
- long int hold_amount = LONG_MAX;
- short hold_sign = -1;
+ long hold_amount = LONG_MAX;
+ char hold_sign = 0;
size_t hold_denom_id = { 0 };
short n_count = 0;
+ char hold_fquantity = 0;
+
+ hold_amount = LONG_MAX;
while (i < text_len) {
char c = text[i];
- // we use \n to identify entry done in ledger
+ /* \n identifies an entry done in ledger */
switch (c) {
case '\n':
case '\r':
@@ -165,25 +152,23 @@ void ledger_parse_data(char *text, size_t text_len)
// after parsing the amount seq, we set the state to ENTRY_WHO
case ENTRY_WHO:
case ENTRY_END:
- hold_sign = -1;
+ hold_sign = 0;
hold_amount = LONG_MAX;
+ hold_denom_id = 0;
// if entry_count <= 1 throw error
if (text[i - 1] == '\n') {
state = DATE;
// TODO push the entries to stack or somethin
warning("\n==\n");
// state = POSTING_END;
- } else {
- state = ENTRY_WHO;
- warning(",");
+ break;
}
+ state = ENTRY_WHO;
+ warning(",");
break;
case COMMENT:
state = ENTRY_START;
break;
- case ENTRY_SIGN_DENOM_AMOUNT:
- state = ENTRY_WHO;
- break;
case ENTRY_DENOM:
//warningf("%s", "denom not found, setting state WHO");
state = ENTRY_WHO;
@@ -218,17 +203,15 @@ void ledger_parse_data(char *text, size_t text_len)
case COMMENT:
if (isalnum(c)) {
// we hit alphanumerical after whitespace
- size_t comment_len = 0;
vstr_t comment = {
.str = text + i,
.len = 0
};
while (i < text_len && *(text + i) != '\n') {
i++;
- comment_len++;
+ comment.len++;
}
- comment.len = comment_len;
- warningf("Comment: %.*s", comment_len,
+ warningf("Comment: %.*s", comment.len,
comment);
state = ENTRY_START;
}
@@ -260,63 +243,110 @@ void ledger_parse_data(char *text, size_t text_len)
ledger_who_parsed:
who_len = i - who_len;
account_add(&rootp, who.str, who_len);
- warningf("\n(%d) Who: %.*s", i, who_len, who);
- state = ENTRY_SIGN_DENOM_AMOUNT;
- // add to tags here
+ warningf("\n@%d Who=%.*s", i, who_len, who);
+ state = ENTRY_DENOM;
+ /* TODO add to tags here */
}
break;
- case ENTRY_SIGN_DENOM_AMOUNT:
- if (*(text + i) == '-' ) {
- // TODO throw already set error
- if (hold_sign >= 0) goto ledger_parse_error_handle;
- state = ENTRY_SIGN;
- } else if (isdigit(*(text + i))) state = ENTRY_AMOUNT;
- else state = ENTRY_DENOM;
- continue;
- case ENTRY_SIGN_AMOUNT:
- if (*(text + i) == '-' ) {
- // TODO throw already set error
- if (hold_sign >= 0) goto ledger_parse_error_handle;
- state = ENTRY_SIGN;
- } else if (isdigit(*(text + i))) state = ENTRY_AMOUNT;
- else goto ledger_parse_error_handle;
- break;
- case ENTRY_SIGN: {
- if (*(text + i) == '-') {
- i++;
- // AMOUNT cannot be set before SIGN
- if (hold_amount != LONG_MAX) goto ledger_parse_error_handle;
- hold_sign = 1;
- state = ENTRY_SIGN_DENOM_AMOUNT;
- }
- } break;
case ENTRY_DENOM: {
char _c;
- warningf(" %d: D:", i + 1);
+ if (hold_denom_id) {
+ warning("Denom already parsed\n");
+ goto ledger_parse_error_handle;
+ }
char *denom = text + i;
size_t denom_len = 0;
+
+ if (*denom == '-') {
+ if (hold_sign) goto ledger_parse_error_handle;
+ hold_sign = 1;
+ i++;
+ state = ENTRY_DENOM;
+ break;
+ }
+
+ warningf(" @%d D", i + 1);
+
while (i < text_len &&
( isalpha(*(text + i))
+ /* TODO Search denomlist instead of just checking '$' */
|| *(text + i) == '$')) i++;
denom_len = (text + i) - denom;
- if (hold_amount == LONG_MAX)
- state = hold_sign? ENTRY_AMOUNT: ENTRY_SIGN_AMOUNT;
- else
- state = ENTRY_END;
- warningf(" %.*s(%d)", denom_len, denom, denom_len);
+ /* FIXME Use proper id */
+ hold_denom_id = *denom;//get_denom_id(denom, denom_len);
+ // hold_denom_len = denom_len;
+ state = hold_amount == LONG_MAX? ENTRY_AMOUNT : ENTRY_END;
+ warningf("#%d(%.*s)", denom_len, denom_len, denom);
break;
}
case ENTRY_AMOUNT: {
char _c;
- warningf(" %d A:", i + 1);
+ if (hold_amount != LONG_MAX) {
+ warning("Amount already parsed\n");
+ goto ledger_parse_error_handle;
+ }
+
char *amount = text + i;
size_t amount_len = 0;
- while (i < text_len && (_c = *(text + i)) == '.' || isdigit(_c) || _c == ',') i++;
- amount_len = (text + i) - amount;
- // TODO convert amount to hold_amount integer
- hold_amount = 0;
- state = hold_denom_id == 0? ENTRY_DENOM : ENTRY_END;
- warningf(" %.*s(%d)", amount_len, amount, amount_len);
+
+ char *eptr = NULL;
+ char *efptr = NULL;
+
+ if (*amount == '-') {
+ if (hold_sign) goto ledger_parse_error_handle;
+ hold_sign = 1;
+ i++;
+ state = ENTRY_AMOUNT;
+ break;
+ }
+
+ warningf(" @%d A", i + 1);
+
+ hold_amount = strtol(amount, &eptr, 10);
+
+ if (errno == ERANGE) {
+ perror("FATAL: Big ints are not supported at the moment");
+ exit(-1);
+ }
+ if (errno) {
+ perror("Some unknown error occured while parsing quantity");
+ exit(-1);
+ }
+
+ amount_len = eptr - amount;
+
+ if (*eptr == ',') *eptr = '.';
+ if (amount_len && *eptr == '.') {
+ float temp = strtof(eptr, &efptr);
+ if (efptr == eptr) {
+ /*
+ TODO Check if '.' should end a transaction
+ TODO Handle @
+ */
+ printf("Searching for decimal value got '%c'\n", *efptr);
+ exit(-1);
+ }
+
+ if (efptr - eptr > 3) {
+ warning("FATAL: Only 2 decimal places are supported at the moment\n");
+ exit(-1);
+ }
+ hold_fquantity = (char) (temp * 100);
+ eptr = efptr;
+ }
+
+ amount_len = eptr - amount;
+ i += amount_len;
+
+ /* number parse failed */
+ if (eptr == amount) {
+ if (hold_denom_id) goto ledger_parse_error_handle;
+ state = ENTRY_DENOM;
+ break;
+ }
+
+ state = hold_denom_id ? ENTRY_END : ENTRY_DENOM;
+ warningf("#%d(%.*s)", amount_len, amount_len, amount);
}
break;
default:
@@ -326,10 +356,12 @@ ledger_who_parsed:
warning("read complete\n");
return;
ledger_parse_error_handle:
- warningf("Parse failed at %ld b:(%d), Expected %s, got '%c'",
- line_no, i, states_str[state], text[i]);
+ warningf("\nParse failed @%d line:%ld, Expected %s, got '%c'\n",
+ i, line_no, states_str[state], text[i]);
}
-Entry** ledger_read_file(const char* filename, time_t date_start, time_t date_end) {
+
+Entry**
+ledger_read_file(const char* filename, time_t date_start, time_t date_end) {
Entity me = {"Account:Income"};
// list population, read from
FILE* file = fopen(filename, "r");
@@ -348,10 +380,12 @@ Entry** ledger_read_file(const char* filename, time_t date_start, time_t date_en
return new_list;
}
-void *module_main(char *data, size_t data_len)
+void
+*module_main(char *data, size_t data_len)
{
// printf("%s\n", data);
warning("\n=======| Startality |=======\n");
ledger_parse_data(data, data_len);
warning("\n========| Fatality |========\n");
}
+
diff --git a/hot.c b/hot.c
@@ -2,7 +2,9 @@
#include <stdlib.h>
#include <dlfcn.h>
#include <signal.h>
+#include <unistd.h>
+#ifdef ENABLE_GUI
#include <GLFW/glfw3.h>
#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
@@ -16,18 +18,24 @@
#define NK_KEYSTATE_BASED_INPUT
#include <nuklear.h>
#include <nuklear_glfw_gl2.h>
+#endif
+
#include "common.h"
#define MAX_MEMORY 4064
#define WINDOW_WIDTH 512
#define WINDOW_HEIGHT 512
+#define BUFFER_SIZE 256
void* state = NULL;
int should_exit = 0;
// init gui state
int width,height;
+
+#ifdef ENABLE_GUI
struct nk_context* ctx;
GLFWwindow* win;
+#endif
void sig_handle() {
printf("Reloaded\n");
@@ -35,13 +43,15 @@ void sig_handle() {
should_exit = 1;
}
-typedef void* (*module_main_func)(void*, int, int);
+typedef void* (*module_main_func)(void*, int);
static void error_callback(int e, const char *d)
{printf("Error %d: %s\n", e, d);}
-int main(int argc, char* argv[]) {
+int
+main(int argc, char* argv[]) {
signal(SIGQUIT, sig_handle);
+#ifdef ENABLE_GUI
glfwSetErrorCallback(error_callback);
if (!glfwInit()) {
fprintf(stdout, "[GFLW] failed to init!\n");
@@ -64,12 +74,12 @@ int main(int argc, char* argv[]) {
nk_style_set_font(ctx, &droid->handle);
}
while(1) {
- void* module = dlopen("./libbalance.so", RTLD_NOW);
+ void* module = dlopen("./libbook.so", RTLD_NOW);
while(module == NULL) {
fprintf(stderr, "Failed to load module. (%s)\n", dlerror());
fprintf(stderr, "Press return to try again.\n");
getchar();
- module = dlopen("./libbalance.so", RTLD_NOW);
+ module = dlopen("./libbook.so", RTLD_NOW);
}
module_main_func module_main = dlsym(module, "module_main");
while (!glfwWindowShouldClose(win) && !should_exit) {
@@ -90,6 +100,35 @@ int main(int argc, char* argv[]) {
dlclose(module);
should_exit = 0;
}
+#else
+ //while(1) {
+ void* module = dlopen("./libbook.so", RTLD_NOW);
+ while(module == NULL){
+ fprintf(stderr, "Failed to load module. (%s)\n", dlerror());
+ fprintf(stderr, "Press return to try again.\n");
+ getchar();
+ module = dlopen("./libbook.so", RTLD_NOW);
+ }
+ module_main_func module_main = dlsym(module, "module_main");
+ FILE* in = fopen("october-2023.txt", "r");
+ char* data = (char*)malloc(2048 * sizeof(char));
+ size_t data_size = 0;
+ size_t c_read = 0;
+ while((c_read = fread(data + data_size + 0, 1, BUFFER_SIZE, in)) != 0) {
+ data_size += c_read;
+ }
+ if (ferror(in)) fprintf(stderr, "Error reading file\n");
+ fprintf(stdout, "Startig loop\n");
+ module_main(data, data_size);
+ while(should_exit == 0) {
+ sleep(1);
+ }
+ should_exit = 0;
+ dlclose(module);
+ fprintf(stderr, "Continue?\n");
+ //}
+#endif
return 0;
}
+
diff --git a/october-2023.txt b/october-2023.txt
@@ -21,4 +21,4 @@
2015/10/12 Zoho
Income:Salary
- Assets:Bank:Checking $10,000
+ Assets:Bank:Checking $10,00
diff --git a/payredu.c b/payredu.c
@@ -7,8 +7,6 @@
#include <book.h>
#define MAX_MEMORY 4064
-#define WINDOW_WIDTH 512
-#define WINDOW_HEIGHT 512
#define BUFFER_SIZE 256
int should_exit = 0;
@@ -19,9 +17,8 @@ void sig_handle() {
should_exit = 1;
}
-typedef void* (*module_main_func)(const char*, size_t);
-
-int main(int argc, char* argv[]) {
+int
+main(int argc, char* argv[]) {
FILE* in = fopen("october-2023.txt", "r");
char* data = (char*)malloc(2048 * sizeof(char));
size_t data_size = 0;
@@ -35,38 +32,3 @@ int main(int argc, char* argv[]) {
return 0;
}
-
-/*
-int main(int argc, char* argv[]) {
- signal(SIGQUIT, sig_handle);
- //while(1) {
- void* module = dlopen("./libbook.so", RTLD_NOW);
- while(module == NULL){
- fprintf(stderr, "Failed to load module. (%s)\n", dlerror());
- fprintf(stderr, "Press return to try again.\n");
- getchar();
- module = dlopen("./libbook.so", RTLD_NOW);
- }
- module_main_func module_main = dlsym(module, "module_main");
- FILE* in = fopen("october-2023.txt", "r");
- char* data = (char*)malloc(2048 * sizeof(char));
- size_t data_size = 0;
- size_t c_read = 0;
- while((c_read = fread(data + data_size + 0, 1, BUFFER_SIZE, in)) != 0) {
- data_size += c_read;
- }
- if (ferror(in)) fprintf(stderr, "Error reading file\n");
- fprintf(stdout, "Startig loop\n");
- module_main(data, data_size);
-
- while(should_exit == 0) {
- sleep(1);
- }
- should_exit = 0;
- dlclose(module);
- fprintf(stderr, "Continue?\n");
- //}
-
- return 0;
-}
-*/