Commit65b1d973Recorded25 Mar 2026Repositorysigil-ledger

Fix == strict balance assertion parsing and correct README API examples

Message

split-balance-assertion was only skipping one character past the = sign, so == assertions left the second = in the amount string. Now detects == and skips both characters. Also fixes README to use actual API names (journal-transaction, not make-journal-transaction) and correct function signatures for the report module.

Added tests for == assertions, cost+assertion combos, and -$50/$-50 negative amount formats.

Changed
 README.md            | 21 ++++++++-------------
 src/sigil/ledger.sgl |  8 ++++++--
 test/test-ledger.sgl | 39 ++++++++++++++++++++++++++++++++++++++-
 3 files changed, 52 insertions(+), 16 deletions(-)
Diff
README.mdmodified
@@ -20,21 +20,19 @@ A Sigil library for reading, writing, and querying [hledger](https://hledger.org
20
(define txns (read-journal "/path/to/finances.journal"))
21
22
;; Create a new transaction
23
(define txn (make-journal-transaction
+23
(define txn (journal-transaction
24
date: "2026-03-25"
25
status: "*"
26
description: "Coffee Shop"
27
postings: (list
28
(make-journal-posting
+28
(journal-posting
29
account: "expenses:food:coffee"
30
amount: (make-journal-amount quantity: 4.50 commodity: "EUR"))
31
(make-journal-posting
+30
amount: (journal-amount quantity: 4.50 commodity: "EUR"))
+31
(journal-posting
32
account: "assets:wise:eur"))))
33
34
;; Append to journal with dedup
35
(append-transactions "/path/to/finances.journal"
36
(list txn)
37
dedup-tag: "ref")
+34
;; Append new transactions to journal
+35
(append-transactions "/path/to/finances.journal" (list txn))
36
```
37
38
### Reports via hledger CLI
@@ -43,13 +41,10 @@ A Sigil library for reading, writing, and querying [hledger](https://hledger.org
41
(import (sigil ledger report))
42
43
;; Run a balance report
46
(define bal (hledger-balance journal: "/path/to/finances.journal"
47
args: '("--tree" "assets")))
+44
(hledger-balance "/path/to/finances.journal" "assets" tree: #t)
45
46
;; Income statement for a period
50
(define is (hledger-income-statement
51
journal: "/path/to/finances.journal"
52
args: '("--period" "2026-03")))
+47
(hledger-income-statement "/path/to/finances.journal" period: "2026-03")
48
```
49
50
## Journal Format Support
src/sigil/ledger.sglmodified
@@ -503,11 +503,15 @@
503
(amount (parse-amount-string amount-str)))
504
(list account amount cost assertion)))
505
506
;; Split off balance assertion (= amount) from end
+506
;; Split off balance assertion (= or == amount) from end
507
(define (split-balance-assertion text)
508
(let ((eq-pos (find-assertion-equals text)))
509
(if eq-pos
510
(let ((assertion-str (string-trim (substring text (+ eq-pos 1) (string-length text)))))
+510
;; Determine if this is == (strict) or = (normal) and skip accordingly
+511
(let* ((skip (if (and (< (+ eq-pos 1) (string-length text))
+512
(char=? (string-ref text (+ eq-pos 1)) #\=))
+513
2 1))
+514
(assertion-str (string-trim (substring text (+ eq-pos skip) (string-length text)))))
515
(cons (string-trim (substring text 0 eq-pos))
516
(parse-amount-string assertion-str)))
517
(cons text #f))))
test/test-ledger.sglmodified
@@ -248,6 +248,20 @@
248
(posting (car (journal-transaction-postings (car txns)))))
249
(assert-equal -500.0 (journal-amount-quantity (journal-posting-amount posting)))))
250
+251
(test "parse negative left-symbol -$50"
+252
(let* ((text "2026-03-15 * Test\n expenses:food -$50.00\n assets:checking\n")
+253
(txns (parse-journal text))
+254
(posting (car (journal-transaction-postings (car txns)))))
+255
(assert-equal -50.0 (journal-amount-quantity (journal-posting-amount posting)))
+256
(assert-equal "$" (journal-amount-commodity (journal-posting-amount posting)))))
+257
+258
(test "parse internal negative $-50"
+259
(let* ((text "2026-03-15 * Test\n expenses:food $-50.00\n assets:checking\n")
+260
(txns (parse-journal text))
+261
(posting (car (journal-transaction-postings (car txns)))))
+262
(assert-equal -50.0 (journal-amount-quantity (journal-posting-amount posting)))
+263
(assert-equal "$" (journal-amount-commodity (journal-posting-amount posting)))))
+264
265
(test "parse inferred amount posting"
266
(let* ((text "2026-03-15 * Test\n expenses:food $50.00\n assets:checking\n")
267
(txns (parse-journal text))
@@ -301,7 +315,30 @@
315
(bal (journal-posting-balance-assertion posting)))
316
(assert-true (journal-amount? bal))
317
(assert-equal 5000.0 (journal-amount-quantity bal))
304
(assert-equal "$" (journal-amount-commodity bal)))))
+318
(assert-equal "$" (journal-amount-commodity bal))))
+319
+320
(test "parse strict balance assertion (==)"
+321
(let* ((text "2026-03-15 * Verify\n assets:checking $0 == $5000.00\n income:salary\n")
+322
(txns (parse-journal text))
+323
(posting (car (journal-transaction-postings (car txns))))
+324
(bal (journal-posting-balance-assertion posting)))
+325
(assert-true (journal-amount? bal))
+326
(assert-equal 5000.0 (journal-amount-quantity bal))
+327
(assert-equal "$" (journal-amount-commodity bal))))
+328
+329
(test "parse cost with balance assertion"
+330
(let* ((text "2026-03-15 * Test\n assets:wise:eur 100.00 EUR @ 1.08 USD = 200.00 EUR\n assets:wise:usd\n")
+331
(txns (parse-journal text))
+332
(posting (car (journal-transaction-postings (car txns))))
+333
(cost (journal-posting-cost posting))
+334
(bal (journal-posting-balance-assertion posting)))
+335
(assert-true (journal-cost? cost))
+336
(assert-equal 'per-unit (journal-cost-type cost))
+337
(assert-true (journal-amount? bal))
+338
(assert-equal "EUR" (journal-amount-commodity bal))
+339
;; Verify quantity parses correctly (use = for numeric comparison
+340
;; since exact/inexact integers may not be equal? to each other)
+341
(assert-true (= 200 (journal-amount-quantity bal))))))
342
343
;; ========== Round-trip Tests ==========
344