// Test binary expressions. // Ref: false --- // Test adding content. // Ref: true #([*Hello* ] + [world!]) --- // Test math operators. // Test plus and minus. #for v in (1, 3.14, 12pt, 45deg, 90%, 13% + 10pt, 6.3fr) { // Test plus. test(+v, v) // Test minus. test(-v, -1 * v) test(--v, v) // Test combination. test(-++ --v, -v) } #test(-(4 + 2), 6-12) // Addition. #test(2 + 4, 6) #test("a" + "b", "ab") #test("a" + if false { "b" }, "a") #test("a" + if true { "b" }, "ab") #test(13 * "a" + "bbbbbb", "aaaaaaaaaaaaabbbbbb") #test((1, 2) + (3, 4), (1, 2, 3, 4)) #test((a: 1) + (b: 2, c: 3), (a: 1, b: 2, c: 3)) --- // Error: 3-26 value is too large #(9223372036854775807 + 1) --- // Subtraction. #test(1-4, 3*-1) #test(4cm - 2cm, 2cm) #test(1e+2-1e-2, 99.99) // Multiplication. #test(2 * 4, 8) // Division. #test(12pt/.4, 30pt) #test(7 / 2, 3.5) // Combination. #test(3-4 * 5 < -10, true) #test({ let x; x = 1 + 4*5 >= 21 and { x = "a"; x + "b" == "ab" }; x }, true) // With block. #test(if true { 1 } + 2, 3) // Mathematical identities. #let nums = ( 1, 3.14, 12pt, 3em, 12pt + 3em, 45deg, 90%, 13% + 10pt, 5% + 1em + 3pt, 2.3fr, ) #for v in nums { // Test plus and minus. test(v + v - v, v) test(v - v - v, -v) // Test plus/minus and multiplication. test(v - v, 0 * v) test(v + v, 2 * v) // Integer addition does not give a float. if type(v) != int { test(v + v, 2.0 * v) } if type(v) != relative and ("pt" not in repr(v) or "em" not in repr(v)) { test(v / v, 1.0) } } // Make sure length, ratio and relative length // - can all be added to / subtracted from each other, // - multiplied with integers and floats, // - divided by integers and floats. #let dims = (10pt, 1em, 10pt + 1em, 30%, 50% + 3cm, 40% + 2em + 1cm) #for a in dims { for b in dims { test(type(a + b), type(a - b)) } for b in (7, 3.14) { test(type(a * b), type(a)) test(type(b * a), type(a)) test(type(a / b), type(a)) } } // Test division of different numeric types with zero components. #for a in (0pt, 0em, 0%) { for b in (10pt, 10em, 10%) { test((2 * b) / b, 2) test((a + b * 2) / b, 2) test(b / (b * 2 + a), 0.5) } } --- // Test numbers with alternative bases. #test(0x10, 16) #test(0b1101, 13) #test(0xA + 0xa, 0x14) --- // Error: 2-7 invalid binary number: 0b123 #0b123 --- // Error: 2-8 invalid hexadecimal number: 0x123z #0x123z --- // Test that multiplying infinite numbers by certain units does not crash. #(float("inf") * 1pt) #(float("inf") * 1em) #(float("inf") * (1pt + 1em)) --- // Test that trying to produce a NaN scalar (such as in lengths) does not crash. #let infpt = float("inf") * 1pt #test(infpt - infpt, 0pt) #test(infpt + (-infpt), 0pt) // TODO: this result is surprising #test(infpt / float("inf"), 0pt) --- // Test boolean operators. // Test not. #test(not true, false) #test(not false, true) // And. #test(false and false, false) #test(false and true, false) #test(true and false, false) #test(true and true, true) // Or. #test(false or false, false) #test(false or true, true) #test(true or false, true) #test(true or true, true) // Short-circuiting. #test(false and dont-care, false) #test(true or dont-care, true) --- // Test equality operators. // Most things compare by value. #test(1 == "hi", false) #test(1 == 1.0, true) #test(30% == 30% + 0cm, true) #test(1in == 0% + 72pt, true) #test(30% == 30% + 1cm, false) #test("ab" == "a" + "b", true) #test(() == (1,), false) #test((1, 2, 3) == (1, 2.0) + (3,), true) #test((:) == (a: 1), false) #test((a: 2 - 1.0, b: 2) == (b: 2, a: 1), true) #test("a" != "a", false) // Functions compare by identity. #test(test == test, true) #test((() => {}) == (() => {}), false) // Content compares field by field. #let t = [a] #test(t == t, true) #test([] == [], true) #test([a] == [a], true) #test(grid[a] == grid[a], true) #test(grid[a] == grid[b], false) --- // Test comparison operators. #test(13 * 3 < 14 * 4, true) #test(5 < 10, true) #test(5 > 5, false) #test(5 <= 5, true) #test(5 <= 4, false) #test(45deg < 1rad, true) #test(10% < 20%, true) #test(50% < 40% + 0pt, false) #test(40% + 0pt < 50% + 0pt, true) #test(1em < 2em, true) --- // Test assignment operators. #let x = 0 #(x = 10) #test(x, 10) #(x -= 5) #test(x, 5) #(x += 1) #test(x, 6) #(x *= x) #test(x, 36) #(x /= 2.0) #test(x, 18.0) #(x = "some") #test(x, "some") #(x += "thing") #test(x, "something") --- // Test destructuring assignments. #let a = none #let b = none #let c = none #((a,) = (1,)) #test(a, 1) #((_, a, b, _) = (1, 2, 3, 4)) #test(a, 2) #test(b, 3) #((a, b, ..c) = (1, 2, 3, 4, 5, 6)) #test(a, 1) #test(b, 2) #test(c, (3, 4, 5, 6)) #((a: a, b, x: c) = (a: 1, b: 2, x: 3)) #test(a, 1) #test(b, 2) #test(c, 3) #let a = (1, 2) #((a: a.at(0), b) = (a: 3, b: 4)) #test(a, (3, 2)) #test(b, 4) #let a = (1, 2) #((a.at(0), b) = (3, 4)) #test(a, (3, 2)) #test(b, 4) #((a, ..b) = (1, 2, 3, 4)) #test(a, 1) #test(b, (2, 3, 4)) #let a = (1, 2) #((b, ..a.at(0)) = (1, 2, 3, 4)) #test(a, ((2, 3, 4), 2)) #test(b, 1) --- // Error: 3-6 cannot mutate a constant: box #(box = 1) --- // Test `in` operator. #test("hi" in "worship", true) #test("hi" in ("we", "hi", "bye"), true) #test("Hey" in "abHeyCd", true) #test("Hey" in "abheyCd", false) #test(5 in range(10), true) #test(12 in range(10), false) #test("" in (), false) #test("key" in (key: "value"), true) #test("value" in (key: "value"), false) #test("Hey" not in "abheyCd", true) #test("a" not /* fun comment? */ in "abc", false) --- // Error: 10 expected keyword `in` #("a" not) --- // Test `with` method. // Apply positional arguments. #let add(x, y) = x + y #test(add.with(2)(3), 5) #test(add.with(2).with(3)(), 5) #test((add.with(2))(4), 6) #test((add.with(2).with(3))(), 5) // Make sure that named arguments are overridable. #let inc(x, y: 1) = x + y #test(inc(1), 2) #let inc2 = inc.with(y: 2) #test(inc2(2), 4) #test(inc2(2, y: 4), 6)