Accounting track: to use the parser in an application, see Accounting: Getting started and Parsing (Livebook). This page documents the library implementation.

Beancount.Parser is a native regex-lexer and line-grammar parser that turns .bean text into the same typed directive structs you build with the public Beancount.* constructors.

Public API

# Parse text
{:ok, directives} = Beancount.parse_text(bean_text)

# Parse a file
{:ok, directives} = Beancount.parse_file("ledger.bean")

# Lists pass through unchanged
{:ok, directives} = Beancount.parse(directives)

# Raise on failure
directives = Beancount.parse!(bean_text)

Parse failures return {:error, %Beancount.Parser.Error{}} with line, column, message, and optional token fields - never a bare FunctionClauseError.

Grammar coverage

The parser covers the full Beancount surface syntax used in production ledgers:

  • dated directives: open, close, commodity, balance, price, event, note, document, pad, query, custom
  • undated directives: include, option, plugin, pushtag, poptag
  • transactions with flags, payee, narration, tags, links, metadata, and postings (amount elision, cost specs {…}, and price annotations @ / @@)
  • comments (;), metadata (key: value), and multi-line metadata blocks

New directive structs (Query, Plugin, PushTag, PopTag) implement the Beancount.Directive protocol so they round-trip through Beancount.render/1.

Round-trip contract

For every golden fixture:

expected = File.read!("expected.bean")
{:ok, directives} = Beancount.parse_text(expected)
assert Beancount.render(directives) == expected

Property tests also assert parse(render(ledger)) recovers equivalent text for generated ledgers from Beancount.Property.ledger/0.

Relationship to engines

Beancount.Engine.Elixir uses the parser internally for check/1 and canned reports in query/2. Beancount.Engine.CLI shells out to bean-check / bean-query and remains the default engine for validation and arbitrary BQL. Native parity on the canned report set is proven; swap engines via config :beancount_ex, engine: Beancount.Engine.Elixir when you want to run without Python tooling.

Parsing is independent of validation semantics: the parser produces structs; engines decide what is valid.