Numbers#
Beancount supports arithmetic expressions almost wherever numbers are accepted.
Construction#
NumberExpr supports construction with from_value:
expr = models.NumberExpr.from_value(decimal.Decimal(42))
assert isinstance(expr, models.NumberExpr)
Value access#
NumberExpr supports value reading and writing. Note that value writing essentially replaces the whole expression.
expr = p.parse('1+2*3', models.NumberExpr)
_print_model(expr)
print(expr.value)
expr.value = 8
_print_model(expr)
print(expr.value)
1+2*3
7
8
8
Arithmetic#
The following arithmetic operations are supported on NumberExpr:
+,-,*,/,//+=,-=,*=,/=,//=+,-(unary)
The operands may be int, decimal.Decimal, or NumberExpr.
expr = p.parse('1*2+3', models.NumberExpr)
expr *= 4
expr += 5
expr = -expr
_print_model(expr)
print(expr.value)
-((1*2+3) * 4 + 5)
-25
Ambiguity#
One confusing issue with beancount arithmetic expressions is about custom where multiple of them can appear consecutively. For example, what does the following custom encode?
2000-01-01 custom "foo" 1 2 -3
Somewhat surprisingly:
$ echo '2000-01-01 custom "foo" 1 2 -3' | bean-report /dev/stdin print
2000-01-01 custom "foo" 1 -1
If we want the arguments to be [1, 2, -3], it must be represented as 2000-01-01 "foo" 1 2 (-3):
$ echo '2000-01-01 custom "foo" 1 2 (-3)' | bean-report /dev/stdin print
2000-01-01 custom "foo" 1 2 -3
autobean-refactor automatically disambiguates custom when constructed with Custom.from_children or Custom.from_value:
custom = models.Custom.from_value(
date=datetime.date(2000, 1, 1),
type='foo',
values=map(decimal.Decimal, [1, 2, -3]))
_print_model(custom)
2000-01-01 custom "foo" 1 2 (-3)
However, that doesn’t cover all the cases, you may still accidentally create ambiguity when adding, removing, or modifying arguments, in which case you may manually disambiguate with NumberExpr.wrap_with_parenthesis.
custom = p.parse('2000-01-01 custom "foo" 1 2', models.Custom)
custom.values[1] = decimal.Decimal('-2')
_print_model(custom)
custom.raw_values[1].wrap_with_parenthesis()
_print_model(custom)
2000-01-01 custom "foo" 1 -2
2000-01-01 custom "foo" 1 (-2)