llm
Download
101 lines 3.8 KiBTomoModified 2026-09-02 raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
# How operators group: precedence, associativity, and how far a leading `-`
# reaches. The snapshots in test/parse pin down the shape of the parse tree;
# these pin down the values it produces.

test "precedence and associativity"
    assert 1 + 2 * 3 - 4 == 3
    assert 2 ^ 3 ^ 2 == 512
    assert 2 ^ 2 * 3 == 12
    assert 10 - 3 - 2 == 5
    assert 100 / 10 / 2 == 5
    assert 1 + 2 < 3 * 4

# Negation sits between `^` and `*`, the same place it sits in ordinary math
# notation, and a literal is no exception: `-2 ^ 2` is `-(2 ^ 2)`.
test "negation"
    x := 3
    assert -x ^ 2 == -9
    assert -2 ^ 2 == -4
    assert (-2) ^ 2 == 4
    assert -2 ^ 2 ^ 3 == -256
    assert 1 - -x ^ 2 == 10
    assert -x * 2 == -6
    assert -x + 1 == -2
    assert 5 - -x == 8
    assert - -1 == 1
    assert -2.5 ^ 2 == -6.25
    assert -(1.0/3) == -1.0/3
    # The formatter prints these back as written: `-` absorbs the `^` but not
    # the `*`, so only the second needs its parentheses (see test-format).
    assert -x^2 == -9
    assert -(x*2) == -6

# A `-` in front of a number still yields a single negative literal, which is
# what lets it be compiled to a sized type that only holds it with the sign on.
test "negative literals"
    small : Int8 = -128
    assert "$small" == "-128"
    assert [1, -2, 3][-2] == -2
    assert -2.5 + 0.5 == -2.0
    assert -1e3 == -1000.0
    assert -0x10 == -16

# Whether the `-` is written against the digits decides only whether the parser
# folds it into the literal; it doesn't decide what the expression means. These
# are all the literal -2, including where only a literal will do, whether
# compiling straight to a sized type or taking its type from the other operand.
#
# Written as `-(2)` rather than `- 2` because the formatter normalizes the spacing
# away, and test-format checks that formatting every .tm file in the tree leaves
# its parse alone. Both representations parse to the same negation of a literal.
test "a `-` written apart from its digits"
    small : Int8 = -(128)
    assert "$small" == "-128"
    assert Int8(-(2)) == Int8(-2)
    assert Int8(-(-(-2))) == Int8(-2)
    assert Int64(-(5)) == Int64(-5)
    assert Float64(-(2)) == Float64(-2)
    assert Int8(1) + -(2) == Int8(-1)
    assert Int8(5) > -(1)
    assert [10, 20, 30][-(1)]! == 30
    # Not just the same value, but the same *type*, which decides whether the
    # arithmetic wraps. Treating `-(1)` as an ordinary Int rather than a literal
    # escalated this to bignum arithmetic, where it came out 128 instead.
    big := Int8(127)
    assert big - -1 == Int8(-128)
    assert big - -(1) == Int8(-128)

# Representations whose value comes out different if the tree is wrong: Euclidean
# `mod` makes `(-x) mod 3` and `-(x mod 3)` disagree, `.abs()` makes `(-x).abs()`
# and `-(x.abs())` disagree, and a suffix binding looser than the `-` would
# change the rest. The trees these check are in test/parse/negation.tm.
test "negation binds where the parse tree says it does"
    x := 5
    n := -3
    xs := [10, 20]
    y := 2.0
    assert -x mod 3 == 1
    assert -n.abs() == -3
    assert -2.abs() == -2
    assert -xs[1]! == -10
    assert -12..round() == -12
    assert 10 - -3 == 13
    assert 10--3 == 13
    assert -y^-1 == -0.5
    assert y^-2.0 == 0.25
    assert -y^2 == -4

# Groupings whose parentheses carry meaning: dropping them regroups the
# expression. test-format checks that formatting every .tm file leaves its parse
# alone, so writing them here is what puts the formatter's parenthesizing under
# test: it prints parentheses exactly where the operator wouldn't absorb the
# operand back.
test "parentheses that have to survive formatting"
    assert (2^3)^2 == 64
    assert 2^(3^2) == 512
    assert (10 - 3) - 2 == 5
    assert 10 - (3 - 2) == 9
    assert (100/10)/2 == 5
    assert 100/(10/2) == 20
    assert (2 + 3)*4 == 20
    assert -(2*3) == -6