bus-bfl

bus-bfl — developer CLI for BusDK Formula Language expressions

Synopsis

bus bfl <command> [options]
bus-bfl <command> [options]

bus-bfl funcset list [-f <format>] [-o <file>] [--chdir <dir>] [--color <mode>] [-v] [-q] [-h] [-V]
bus-bfl parse --expr <expression> [--dialect <name>] [--source-name <name>] [-f <format>] [-o <file>] [--chdir <dir>] [global flags]
bus-bfl format [--expr <expression>] [--dialect <name>] [-f <format>] [-o <file>] [--chdir <dir>] [global flags]
bus-bfl validate --expr <expression> --schema <file> [-f <format>] [-o <file>] [--chdir <dir>] [global flags]
bus-bfl eval --expr <expression> --context <file> [--schema <file>] [-f <format>] [-o <file>] [--chdir <dir>] [global flags]

Description

Command names follow CLI command naming.

BusDK Formula Language (BFL) is a small deterministic expression language for computed fields. It supports spreadsheet-style references and ranges.

bus-bfl lets you parse, format, validate, and evaluate expressions from the command line. It does not read workspace datasets or write results back. It operates only on the expression and JSON files you pass in.

Getting started

Run bus-bfl --help to see commands and options. Run bus-bfl --version for version.

Standard global flags are supported; see Standard global flags. The most common in this module are --format json for machine-readable output, --output <file> to write results, --chdir <dir> to resolve relative file paths, and --color <mode> (or --no-color) for diagnostics coloring.

--quiet and --verbose are mutually exclusive. Invalid global flag combinations and unknown format/color values return usage error (2).

Listing function sets

The compiled-in function sets determine which functions are available in expressions. To list them, run funcset list. The default output is one name per line (for example basic and none). With --format json you get a JSON object with a funcsets array. The order is deterministic. The default build currently reports basic and none.

Parsing expressions

Use parse to see how the tool interprets an expression. Give the expression with --expr and optionally set a dialect or source name for diagnostics:

bus-bfl parse --expr "1 + 2"

The output is a single line showing the abstract syntax tree, for example (binary + (literal 1) (literal 2)). With --format json you get a JSON object with an ast field whose expr property is that same string. The parse command accepts --dialect <name> (default dialect.spreadsheet) and --source-name <name> for diagnostics. If the expression is invalid, the tool prints diagnostics to stderr and exits with a non-zero status; stdout is left empty.

Range expressions are supported using Excel-like A1 notation and the colon operator. Examples include A1:B10, A1:A, and A:A. Parsing a range yields a deterministic AST like (range A1:B2). Range expressions evaluate to array values and can only be used where the language expects an array, such as a function parameter. The core language has no array literals, so arrays are only produced by ranges, by registered functions, or by array-typed symbols supplied through the evaluation context.

Formatting expressions

Use format to rewrite an expression into canonical form (spacing and structure normalized). Pass the expression with --expr:

bus-bfl format --expr "1+2"

The result is printed to stdout, for example 1 + 2. With --format json (or --json) you get a JSON object with an expression field. You can also omit --expr and supply the expression on standard input, which is useful in pipelines:

printf '%s' '1+2' | bus-bfl format

The --dialect option selects a formatting profile (for example dialect.sheets_like). Supported profiles include dialect.spreadsheet, dialect.excel_like, dialect.sheets_like, and dialect.programmer. The default is dialect.spreadsheet. Range expressions are preserved in canonical form, so A1:B2 formats as A1:B2.

Validating expressions

Use validate to check that an expression parses and typechecks against a schema. You must supply the expression with --expr and the schema with --schema:

bus-bfl validate --expr "price * qty" --schema schema.json

On success the default output is the single line ok. With --format json you get { "status": "ok" }. If you omit --schema, the tool prints an error to stderr and exits with a non-zero status. Range expressions are accepted when the schema provides a compatible typing environment. Array-typed symbols are accepted when declared in the schema.

The schema file is a JSON object that defines identifiers and their types. For example:

{
  "symbols": {
    "price": { "kind": "number", "nullable": false },
    "qty": { "kind": "integer", "nullable": false }
  }
}

Valid kind values are null, bool, string, integer, number, date, datetime, array, and any. Each symbol can have nullable set to true or false. Array symbols use kind: "array" with an elem definition describing the element type.

Evaluating expressions

Use eval to evaluate an expression against a concrete context. You must supply the expression with --expr and the context with --context. You may optionally supply a schema with --schema; if you omit it, the tool infers types from the context.

bus-bfl eval --expr "price * qty" --context context.json --schema schema.json

The default output is a typed value on one line, for example number 59.85. Array values are rendered as a shape plus a flattened item list, for example array 2x2 [integer 1, integer 2, integer 3, integer 4]. With --format json you get a JSON object with type and value (for example { "type": "number", "value": "59.85" }, or an array value with rows, cols, and items). If you omit --context, the tool prints an error to stderr and exits with a non-zero status.

Range evaluation requires a runtime context that can resolve ranges deterministically. If you evaluate an expression containing a range and the context does not provide range resolution, evaluation fails with a deterministic error. Array values in the context must have a consistent rows, cols, and items length; if the shape does not match the item count, evaluation fails with an error.

The context file is a JSON object that supplies values for the symbols used in the expression. Numbers and integers are encoded as strings to preserve precision:

{
  "values": {
    "price": { "type": "number", "value": "19.95" },
    "qty": { "type": "integer", "value": "3" }
  }
}

Date values use YYYY-MM-DD; datetime values use RFC3339 with an explicit time zone. A null value uses { "type": "null", "value": null }. Array values use type: "array" with a value object containing rows, cols, and a row-major items array of typed values.

Integration: workbook and formula parity

Consumers such as bus-data use the BFL library to evaluate formula fields during table read and workbook-style read. Formula options for workbook extraction include --formula, --formula-source, and --formula-dialect; locale is controlled by --decimal-sep and --thousands-sep. When a beside-the-table schema exists and formula evaluation is enabled, output contains the evaluated result for formula cells (parity-level behaviour); use --formula-source to include formula source text. Formula-driven report totals in source spreadsheets can be reproduced deterministically by mapping Table Schema formula metadata to BFL options, setting locale-aware dialect options from the source, and registering a function set (e.g. SUM, IF, ROUND) when formulas use functions. See Formula metadata and evaluation for workbook extraction for the delegation contract, supported functions, locale handling, and recommended minimal function set.

Examples

bus bfl parse --expr "=A1*1.24" --dialect spreadsheet
bus bfl eval --expr "a+b" --context ./examples/bfl-context.json

Exit status

The tool exits with status 0 on success. It exits with status 2 on invalid usage such as invalid --color, unknown --format, both --quiet and --verbose, or a subcommand invoked without required arguments after -- terminates global flag parsing. It exits with status 1 when a required file or directory is missing or unreadable, or when an output file cannot be written. It exits with a non-zero status on parse errors, validation failures, or evaluation failures. Error messages are always written to standard error.

Using from .bus files

Inside a .bus file, write this module target without the bus prefix.

# same as: bus bfl parse --expr "a + b"
bfl parse --expr "a + b"

# same as: bus bfl validate --expr "price * qty" --schema ./schema.json
bfl validate --expr "price * qty" --schema ./schema.json

# same as: bus bfl eval --expr "price * qty" --context ./context.json --format json
bfl eval --expr "price * qty" --context ./context.json --format json

Development state

Value promise: Parse, format, validate, and evaluate BFL expressions so bus-data and the workbook can use formula-enabled fields with deterministic, testable semantics.

Use cases: Workbook and validated tabular editing.

Completeness: 60% — parse, format, validate, eval, and funcset list plus global flags verified by e2e; conformance, limits, and library behavior verified by unit tests; formula engine ready for bus-data integration.

Use case readiness: Workbook and validated tabular editing: 60% — CLI and library contract verified; formula engine ready for bus-data projection.

Current: Parse/format/validate/eval/funcset flows and global-flag behavior are test-verified. Detailed test matrix and implementation notes are maintained in Module SDD: bus-bfl.

Planned next: None in PLAN; optional benchmark metadata and CI artifact follow-ups per SDD.

Blockers: None known.

Depends on: None.

Used by: bus-data for formula validation and projection.

See Development status.