Skip to content

Commit

Permalink
basic working parser
Browse files Browse the repository at this point in the history
  • Loading branch information
carderne committed Jan 19, 2024
1 parent 1a0c596 commit b165275
Show file tree
Hide file tree
Showing 11 changed files with 851 additions and 2 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: test
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo test --all-features
16 changes: 16 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "bean-rs"
license = "MIT"
authors = ["Chris Arderne <[email protected]"]
description = "beancount clone in Rust"
version = "0.1.0"
edition = "2021"

[dependencies]
clap = { version = "4.4.18", features = ["derive"] }
pest = "2.7.6"
pest_derive = "2.7.6"

[[bin]]
name = "bean"
path = "src/main.rs"
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.DEFAULT_GOAL = build

.PHONY: build
build: fmt
cargo build

.PHONY: release
release: fmt
cargo build --release

.PHONY: run
run:
cargo run balance example.bean

.PHONY: fmt
fmt:
cargo fmt

.PHONY: test
test:
cargo test
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,42 @@
# beaners
beancount clone in Rust
# bean-rs

Basic [beancount](https://github.com/beancount/beancount) clone (one day...) in Rust!

Using [pest](https://pest.rs/) for parsing. Two useful links:
- [pest bootstrap parsing](https://github.com/pest-parser/pest/tree/master/meta/src)
- [playground](https://pest.rs/#editor)

Planned featuers:
- [x] Parse beancount files
- [x] Stricter transaction keywords
- [x] Propagate line numbers for debugging
- [ ] Calculate account balances
- [ ] Use proper Decimal handling
- [ ] Validate transactions against `open`/`close` directives
- [ ] Validate `balance` directives
- [ ] Open/close with multiple currencies

## Usage
### Install
```bash
cargo install bean-rs
```

### Run
```
$ bean
Commands:
balance
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
```

## Development
### Build
```bash
make build
```
41 changes: 41 additions & 0 deletions example.bean
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
* Example beancount file

** Random metadata stuff
option "operating_currency" "GBP"
2000-01-01 custom "fava-option" "language" "en"
2000-01-01 commodity GBP
name: "British Pound"

** Open accounts
2023-01-01 open Equity:Bals
2023-01-01 open Assets:Bank GBP
2023-01-02 open Assets:Invest GOO "FIFO"
portfolio: "all"
2023-01-03 open Expenses:Food GBP ;, USD
2023-01-04 open Income:Job GBP

** Transactions
2023-02-01 * "Salary" ; comment
user: "Chris"
Assets:Bank 1000 GBP
Income:Job

2023-02-02 * "Buy food"
Assets:Bank -100 GBP
; comment
Expenses:Food 100 GBP

2023-02-05 * "Shop" "More food" #tag ^link
Assets:Bank -40.00 GBP
Expenses:Food 40.00 GBP

** Balances and pads
2023-03-01 pad Assets:Invest Equity:Bals
2023-03-02 balance Assets:Invest 111 GOO
2023-03-03 balance Assets:Bank 860 GBP

** Close an account
2023-12-01 close Assets:Bank

** Prices
2023-01-01 price GOOG 50 GBP
63 changes: 63 additions & 0 deletions grammar.pest
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
root = { SOI ~ (NEWLINE* ~ entry ~ NEWLINE*)* ~ EOI }

entry = _{
heading
| option
| custom
| commodity
| open
| close
| balance
| pad
| price
| transaction
| COMMENT
| space+
}

heading = _{ "*" ~ anyline }
option = { "option" ~ (space+ ~ quoted){2} }
custom = { date ~ space ~ "custom" ~ (space+ ~ quoted){3} }

commodity = { date ~ space+ ~ "commodity" ~ space+ ~ ccy ~ metadata_added }
open = { date ~ space+ ~ "open" ~ space+ ~ account ~ (space+ ~ ccy)? ~ (space+ ~ quoted)? ~ metadata_added* }
close = { date ~ space+ ~ "close" ~ space+ ~ account }

price = { date ~ space+ ~ "price" ~ space+ ~ ccy ~ space+ ~ amount }
balance = { date ~ space+ ~ "balance" ~ space+ ~ account ~ space+ ~ amount }
pad = { date ~ space+ ~ "pad" ~ space+ ~ account ~ space+ ~ account }

transaction = { date ~ space+ ~ txn_type ~ space+ ~ payee ~ (space+ ~ narration)? ~ (space+ ~ tag)? ~ (space+ ~ link)? ~ (posting_added | metadata_added)* }
payee = { quoted }
narration = { quoted }
posting_added = _{ (NEWLINE ~ posting) }
posting = { space+ ~ account ~ (space+ ~ amount)? }

metadata_added = _{ (NEWLINE ~ metadata) }
metadata = { space+ ~ key ~ ":" ~ space* ~ val }

txn_type = { "*" | "!" | "txn" }
key = @{ ASCII_ALPHA_LOWER+ }
val = @{ quoted }
space = _{ " " | "\t" }
quoted = _{ quote ~ inner_quoted ~ quote }
inner_quoted = { (!quote ~ ANY)* }
quote = _{ "\"" }

date = @{ year ~ "-" ~ month ~ "-" ~ day }
year = @{ '1'..'2' ~ ASCII_DIGIT ~ ASCII_DIGIT ~ ASCII_DIGIT }
month = @{ '0'..'1' ~ ASCII_DIGIT }
day = @{ '0'..'3' ~ ASCII_DIGIT }

amount = { number ~ space+ ~ ccy }
number = @{ "-"? ~ ASCII_DIGIT ~ (ASCII_DIGIT | ".")* }
ccy = @{ ASCII_ALPHA_UPPER{3, 9} }

account = @{ account_root ~ (":" ~ ASCII_ALPHA_UPPER ~ ASCII_ALPHA_LOWER+)+ }
account_root = _{ "Assets" | "Liabilities" | "Income" | "Expenses" | "Equity" }

tag = @{ "#" ~ ASCII_ALPHA_LOWER+ }
link = @{ "^" ~ ASCII_ALPHA_LOWER+ }

COMMENT = _{ NEWLINE? ~ space* ~ ";" ~ anyline }
anyline = _{ (!NEWLINE ~ ANY)* }
Loading

0 comments on commit b165275

Please sign in to comment.