Replace the hand-written parser in calc.c with a tree-sitter grammar (subprojects/rizin-math-parser) and a typed evaluator. The old parser could only ever produce a ut64 and folded anything it failed to read to 0, which left callers unable to tell a failed expression from one that evaluated to zero. Expressions now evaluate to an RzNumValue, a tagged union over ut64, double, RzBitVector, arbitrary-precision integer and arbitrary-precision decimal, carrying an RzNumError rather than signalling failure as 0. Literals keep the width they were written with (5u8, 0xffu128, any width from 1 to 65536), results that outgrow 64 bits promote to a big number on their own, and a parse error, division by zero or unresolved identifier reaches the caller. rz_num_math() is deprecated. rz_num_math_ut64() keeps its exact behaviour for callers that want a ut64, and rz_num_math_value() exposes the typed result. rz_core_math() adds the RzCore-backed form used by the % command, with rz_core_math_ut64() deprecated alongside it. rz-ax routes through the typed API, so it prints values at full precision, reports errors on stderr and exits non-zero. rz_il_lift_num() converts an expression to an RzILOpPure, so a numeric argument can be lifted instead of pre-evaluated. Legacy input still works: trailing base suffixes (101b, 35o, 212t), the trailing-'h' hex form and the k/m/g scale suffixes are all accepted and warn once, pointing at the 0b/0o/0t prefixes. doc/math.md documents the language and doc/math-il-lift.md the lift; the grammar, the evaluator, rz-ax and the % command are covered by unit and db tests. |
||
|---|---|---|
| .. | ||
| src | ||
| test/corpus | ||
| .gitignore | ||
| grammar.js | ||
| meson.build | ||
| meson_tree_sitter_generate.py | ||
| package.json | ||
| README.md | ||
| tree-sitter.json | ||
rizin-math-parser
A tree-sitter grammar for the Rizin RzNum mathematical expression
language. Used by librz_util to evaluate user-supplied numerical
expressions such as those accepted by the ? and ?v commands.
Regenerating the parser
The parser source files (src/parser.c, src/grammar.json,
src/node-types.json, src/tree_sitter/parser.h) are produced by
tree-sitter generate from grammar.js. They are regenerated by the
build system when the tree-sitter CLI and node are available; the
pre-generated files committed in the repository are used otherwise.
Manual regeneration:
cd subprojects/rizin-math-parser
tree-sitter generate
Running the corpus tests
cd subprojects/rizin-math-parser
tree-sitter test
Supported features
See test/corpus/main.txt for the full reference. Highlights:
- numeric literals in decimal, binary (
0b), ternary (0t), octal (0o) and hexadecimal (0x) bases, with an optional fractional part and[eEpP]exponent (whose digits are required) - C-style integer suffixes (
u,l,U,L,f,F) - SI / IEC byte units (
KiB,KB,MiB,MB,GiB,GB,TiB,TB,PiB,PB,EiB,EB) - arithmetic
+ - * / %and themodkeyword - exponentiation
**and thelogkeyword - bitwise
& | ^ ~, logical not!, shifts<< >>and rotations<<< >>> - comparisons
< <= > >=and equality== != - unary
+and-as operators (not part of the number literal) - function calls
name(arg1, arg2, ...) - variables, including Rizin-style names with dots (e.g.
sym.func.1000055d4) and Unicode identifiers - Rizin special variables as a dedicated
special_variablerule ($$,$$$,$F,$S,$SS, ...) - address with explicit width and endianness:
0x1234:le32/0xDEADBEEF:be64/0x1000:8 - string-as-bytes literal:
"ABC" letand plain assignment to a variable- pre-increment
++xand pre-decrement--x
Reserved words
let is always reserved (always produces a parse error in variable
position). mod, log, le and be are reserved within their
positional contexts (modulo, logarithm, address suffix); bare uses
parse as variables at the grammar level and are rejected by the
evaluator.