Powers and remainders in the calculator (closes #3)
check / check (push) Successful in 57s

internal/calc gets its own tokenizer and precedence-climbing parser in
place of go/parser, which cannot express a power operator; go/constant
still computes. ^ (also **) binds tighter than * / % and a sign on its
left, and groups to the right. % takes the sign of the divisor and is
exact on decimals. A whole exponent is computed exactly while the
result's numerator and denominator stay within 4096 bits; otherwise,
and for a fractional exponent, in float64. A remainder, or the sign of
a negative base's power, that depends on a value go/constant holds
rounded is refused as too large. The welcome text and the replies
mention the new operators; a negative base with a fractional exponent
gets its own reply.

Model: opus-5-5
This commit is contained in:
clawbot
2026-09-29 00:08:26 +00:00
parent 09a5caa8ab
commit aeb040d156
7 changed files with 522 additions and 96 deletions
+23 -13
View File
@@ -5,9 +5,10 @@ SimpleX Chat network: it accepts every contact request and answers
arithmetic such as `2 + 2` with the result.
Send it `2 + 2` and it replies `4`; send `5 * 5/2` and it replies
`12.5`. It understands decimal numbers, `+ - * /`, unary minus and
parentheses, and computes exactly, so `0.1 + 0.2` is `0.3`. Anything
else gets a short explanation instead of a result.
`12.5`. It understands decimal numbers, `+ - * /`, powers written `2^10`
or `2**10`, remainders written `7 % 3`, signs and parentheses, and
computes exactly, so `0.1 + 0.2` is `0.3`. Anything else gets a short
explanation instead of a result.
## Getting Started
@@ -157,16 +158,25 @@ container.
- **Replies**: for each text message a contact sends in a direct chat,
the bot sends back the result, as a reply quoting the message. Group
messages, files and the bot's own messages are ignored.
- **Arithmetic** (`internal/calc`): the text is parsed as a Go
expression with `go/parser`, and only numbers, `+ - * /`, unary signs
and parentheses are evaluated; anything else in the syntax tree is
refused. `go/constant` computes with exact rationals. Numbers are read
as decimal, so `010` is ten. Input over 256 bytes is refused, so a
message cannot make the bot do unbounded work. Whole numbers below
10<sup>21</sup> are written exactly; other results in the shortest
form that reads back as the same double, in exponent notation from
10<sup>21</sup> up and below 10<sup>-6</sup>. A result beyond the
range of a double is refused as too large.
- **Arithmetic** (`internal/calc`): a small parser of its own reads
numbers, `+ - * / % ^`, signs and parentheses, and refuses anything
else. `go/constant` computes with exact rationals. `^`, also written
`**`, is a power: it binds tighter than `*`, `/`, `%` and a sign on
its left, and groups to the right, so `2^3^2` is `512`, `-2^2` is
`-4`, `(-2)^2` is `4` and `2^-1` is `0.5`. `%` is the remainder and
ranks with `*` and `/`; its result takes the sign of the divisor, as
in Python, so `7 % 3` is `1`, `-7 % 3` is `2` and `7.5 % 2` is `1.5`.
A power with a whole exponent is exact, so `0.1^2` is `0.01`, unless
its numerator and denominator together could pass 4096 bits; that
power, and one with a fractional exponent, is computed as a double, so
`2^0.5` is `1.4142135623730951`. A negative number to a fractional
power is refused, as having no real result. Numbers are read as
decimal, so `010` is ten. Input over 256 bytes is refused and exact
powers are capped, so a message cannot make the bot do unbounded work.
Whole numbers below 10<sup>21</sup> are written exactly; other results
in the shortest form that reads back as the same double, in exponent
notation from 10<sup>21</sup> up and below 10<sup>-6</sup>. A result
beyond the range of a double is refused as too large.
- **Failure is an exit.** If the chat client exits or the connection to
it drops, the bot exits with an error and the container's restart
policy starts both again. `SIGTERM` stops the bot, which stops the