summaryrefslogtreecommitdiff
path: root/tests/typ
diff options
context:
space:
mode:
Diffstat (limited to 'tests/typ')
-rw-r--r--tests/typ/comment.typ23
-rw-r--r--tests/typ/control/for-invalid.typ32
-rw-r--r--tests/typ/control/for-pattern.typ30
-rw-r--r--tests/typ/control/for-value.typ20
-rw-r--r--tests/typ/control/for.typ52
-rw-r--r--tests/typ/control/if-invalid.typ28
-rw-r--r--tests/typ/control/if-value.typ21
-rw-r--r--tests/typ/control/if.typ45
-rw-r--r--tests/typ/control/let-invalid.typ20
-rw-r--r--tests/typ/control/let-terminated.typ28
-rw-r--r--tests/typ/control/let.typ11
-rw-r--r--tests/typ/empty.typ0
-rw-r--r--tests/typ/expr/array.typ43
-rw-r--r--tests/typ/expr/assoc.typ18
-rw-r--r--tests/typ/expr/block-invalid.typ37
-rw-r--r--tests/typ/expr/block-scoping.typ35
-rw-r--r--tests/typ/expr/block.typ38
-rw-r--r--tests/typ/expr/call-invalid.typ44
-rw-r--r--tests/typ/expr/call.typ40
-rw-r--r--tests/typ/expr/dict.typ20
-rw-r--r--tests/typ/expr/ops-invalid.typ59
-rw-r--r--tests/typ/expr/ops.typ150
-rw-r--r--tests/typ/expr/prec.typ30
-rw-r--r--tests/typ/full/coma.typ50
-rw-r--r--tests/typ/library/box.typ20
-rw-r--r--tests/typ/library/font.typ56
-rw-r--r--tests/typ/library/hv.typ22
-rw-r--r--tests/typ/library/image.typ35
-rw-r--r--tests/typ/library/page.typ53
-rw-r--r--tests/typ/library/pagebreak.typ3
-rw-r--r--tests/typ/library/rgb.typ17
-rw-r--r--tests/typ/markup/emph.typ14
-rw-r--r--tests/typ/markup/escape.typ30
-rw-r--r--tests/typ/markup/heading.typ44
-rw-r--r--tests/typ/markup/linebreak.typ23
-rw-r--r--tests/typ/markup/nbsp.typ5
-rw-r--r--tests/typ/markup/raw.typ51
-rw-r--r--tests/typ/markup/strong.typ14
-rw-r--r--tests/typ/repr.typ50
-rw-r--r--tests/typ/spacing.typ27
-rw-r--r--tests/typ/text.typ8
41 files changed, 1346 insertions, 0 deletions
diff --git a/tests/typ/comment.typ b/tests/typ/comment.typ
new file mode 100644
index 00000000..25180211
--- /dev/null
+++ b/tests/typ/comment.typ
@@ -0,0 +1,23 @@
+// Test line and block comments.
+
+---
+// Line comment acts as spacing.
+A// you
+B
+
+// Block comment does not act as spacing.
+C/*
+ /* */
+*/D
+
+// Works in code.
+#test(type(/*1*/ 1) //
+, "integer")
+
+---
+// End should not appear without start.
+// Error: 1:7-1:9 unexpected end of block comment
+/* */ */
+
+// Unterminated is okay.
+/*
diff --git a/tests/typ/control/for-invalid.typ b/tests/typ/control/for-invalid.typ
new file mode 100644
index 00000000..c8bdebdd
--- /dev/null
+++ b/tests/typ/control/for-invalid.typ
@@ -0,0 +1,32 @@
+// Test invalid for loop syntax.
+
+---
+// Error: 5-5 expected identifier
+#for
+
+// Error: 7-7 expected keyword `in`
+#for v
+
+// Error: 10-10 expected expression
+#for v in
+
+// Error: 15-15 expected body
+#for v in iter
+
+---
+// Should output `v in iter`.
+// Error: 5 expected identifier
+#for
+v in iter {}
+
+// Should output `A thing`.
+// Error: 7-10 expected identifier, found string
+A#for "v" thing.
+
+// Should output `in iter`.
+// Error: 6-9 expected identifier, found string
+#for "v" in iter {}
+
+// Should output `+ b in iter`.
+// Error: 7 expected keyword `in`
+#for a + b in iter {}
diff --git a/tests/typ/control/for-pattern.typ b/tests/typ/control/for-pattern.typ
new file mode 100644
index 00000000..38253b33
--- /dev/null
+++ b/tests/typ/control/for-pattern.typ
@@ -0,0 +1,30 @@
+// Test for loop patterns.
+// Ref: false
+
+---
+#let out = ()
+
+// Values of array.
+#for v in (1, 2, 3) {
+ out += (v,)
+}
+
+// Values of dictionary.
+#for v in (a: 4, b: 5) {
+ out += (v,)
+}
+
+// Keys and values of dictionary.
+#for k, v in (a: 6, b: 7) {
+ out += (k,)
+ out += (v,)
+}
+
+#test(out, (1, 2, 3, 4, 5, "a", 6, "b", 7))
+
+---
+// Keys and values of array.
+// Error: 6-10 mismatched pattern
+#for k, v in (-1, -2, -3) {
+ dont-care
+}
diff --git a/tests/typ/control/for-value.typ b/tests/typ/control/for-value.typ
new file mode 100644
index 00000000..3ab80716
--- /dev/null
+++ b/tests/typ/control/for-value.typ
@@ -0,0 +1,20 @@
+// Test return value of for loops.
+
+---
+// Template body yields template.
+// Should output `234`.
+#for v in (1, 2, 3, 4) [#if v >= 2 [{v}]]
+
+---
+// Block body yields template.
+// Should output `[1st, 2nd, 3rd, 4th, 5th, 6th]`.
+{
+ "[" + for v in (1, 2, 3, 4, 5, 6) {
+ (if v > 1 [, ]
+ + [{v}]
+ + if v == 1 [st]
+ + if v == 2 [nd]
+ + if v == 3 [rd]
+ + if v >= 4 [th])
+ } + "]"
+}
diff --git a/tests/typ/control/for.typ b/tests/typ/control/for.typ
new file mode 100644
index 00000000..294345b5
--- /dev/null
+++ b/tests/typ/control/for.typ
@@ -0,0 +1,52 @@
+// Test for loops.
+// Ref: false
+
+---
+// Array.
+
+#for x in () {}
+
+#let sum = 0
+#for x in (1, 2, 3, 4, 5) {
+ sum += x
+}
+
+#test(sum, 15)
+
+---
+// Dictionary.
+// Ref: true
+(\ #for k, v in (name: "Typst", age: 2) [
+ #h(0.5cm) {k}: {v}, \
+])
+
+---
+// String.
+{
+ let out = ""
+ let first = true
+ for c in "abc" {
+ if not first {
+ out += ", "
+ }
+ first = false
+ out += c
+ }
+ test(out, "a, b, c")
+}
+
+---
+// Uniterable expression.
+// Error: 11-15 cannot loop over boolean
+#for v in true {}
+
+// Make sure that we don't complain twice.
+// Error: 11-18 cannot add integer and string
+#for v in 1 + "2" {}
+
+// Error: 14-17 cannot apply '-' to string
+#let error = -""
+#let result = for v in (1, 2, 3) {
+ if v < 2 [Ok] else {error}
+}
+#test(result, error)
diff --git a/tests/typ/control/if-invalid.typ b/tests/typ/control/if-invalid.typ
new file mode 100644
index 00000000..6d2deab1
--- /dev/null
+++ b/tests/typ/control/if-invalid.typ
@@ -0,0 +1,28 @@
+// Test invalid if syntax.
+
+---
+// Error: 4 expected expression
+#if
+
+// Error: 4 expected expression
+{if}
+
+// Error: 6 expected body
+#if x
+
+// Error: 1-6 unexpected keyword `else`
+#else {}
+
+---
+// Should output `x`.
+// Error: 4 expected expression
+#if
+x {}
+
+// Should output `something`.
+// Error: 6 expected body
+#if x something
+
+// Should output `A thing.`
+// Error: 20 expected body
+A#if false {} #else thing
diff --git a/tests/typ/control/if-value.typ b/tests/typ/control/if-value.typ
new file mode 100644
index 00000000..d7124255
--- /dev/null
+++ b/tests/typ/control/if-value.typ
@@ -0,0 +1,21 @@
+// Test return value of if expressions.
+// Ref: false
+
+---
+{
+ let x = 1
+ let y = 2
+ let z
+
+ // Returns if branch.
+ z = if x < y { "ok" }
+ test(z, "ok")
+
+ // Returns else branch.
+ z = if x > y { "bad" } else { "ok" }
+ test(z, "ok")
+
+ // Missing else evaluates to none.
+ z = if x > y { "bad" }
+ test(z, none)
+}
diff --git a/tests/typ/control/if.typ b/tests/typ/control/if.typ
new file mode 100644
index 00000000..4ed6b649
--- /dev/null
+++ b/tests/typ/control/if.typ
@@ -0,0 +1,45 @@
+// Test if-else expressions.
+
+---
+// Test condition evaluation.
+#if 1 < 2 [
+ Ok.
+]
+
+#if true == false [
+ Bad, but we {dont-care}!
+]
+
+---
+// Brace in condition.
+#if {true} [
+ Ok.
+]
+
+// Multi-line condition with parens.
+#if (
+ 1 + 1
+ == 1
+) {
+ nope
+} #else {
+ "Ok."
+}
+
+// Multiline.
+#if false [
+ Bad.
+] #else {
+ let pt = "."
+ "Ok" + pt
+}
+
+---
+// Condition must be boolean.
+// If it isn't, neither branch is evaluated.
+// Error: 5-14 expected boolean, found string
+#if "a" + "b" { nope } #else { nope }
+
+// Make sure that we don't complain twice.
+// Error: 5-12 cannot add integer and string
+#if 1 + "2" {}
diff --git a/tests/typ/control/let-invalid.typ b/tests/typ/control/let-invalid.typ
new file mode 100644
index 00000000..f29353ed
--- /dev/null
+++ b/tests/typ/control/let-invalid.typ
@@ -0,0 +1,20 @@
+// Test invalid let binding syntax.
+
+---
+// Error: 5 expected identifier
+#let
+
+// Error: 6-9 expected identifier, found string
+#let "v"
+
+// Should output `1`.
+// Error: 7 expected semicolon or line break
+#let v 1
+
+// Error: 9 expected expression
+#let v =
+
+---
+// Should output `= 1`.
+// Error: 6-9 expected identifier, found string
+#let "v" = 1
diff --git a/tests/typ/control/let-terminated.typ b/tests/typ/control/let-terminated.typ
new file mode 100644
index 00000000..623265e0
--- /dev/null
+++ b/tests/typ/control/let-terminated.typ
@@ -0,0 +1,28 @@
+// Test termination of let statements.
+
+---
+// Terminated by line break.
+#let v1 = 1
+One
+
+// Terminated by semicolon.
+#let v2 = 2; Two
+
+// Terminated by semicolon and line break.
+#let v3 = 3;
+Three
+
+// Terminated because expression ends.
+// Error: 12 expected semicolon or line break
+#let v4 = 4 Four
+
+// Terminated by semicolon even though we are in a paren group.
+// Error: 2:19 expected expression
+// Error: 1:19 expected closing paren
+#let v5 = (1, 2 + ; Five
+
+#test(v1, 1)
+#test(v2, 2)
+#test(v3, 3)
+#test(v4, 4)
+#test(v5, (1, 2))
diff --git a/tests/typ/control/let.typ b/tests/typ/control/let.typ
new file mode 100644
index 00000000..e609d3a9
--- /dev/null
+++ b/tests/typ/control/let.typ
@@ -0,0 +1,11 @@
+// Test let bindings.
+// Ref: false
+
+---
+// Automatically initialized with none.
+#let x
+#test(x, none)
+
+// Manually initialized with one.
+#let x = 1
+#test(x, 1)
diff --git a/tests/typ/empty.typ b/tests/typ/empty.typ
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/typ/empty.typ
diff --git a/tests/typ/expr/array.typ b/tests/typ/expr/array.typ
new file mode 100644
index 00000000..c9383501
--- /dev/null
+++ b/tests/typ/expr/array.typ
@@ -0,0 +1,43 @@
+// Test arrays.
+
+---
+// Empty.
+{()}
+
+// Not an array, just a parenthesized expression.
+{(1)}
+
+// One item and trailing comma.
+{(-1,)}
+
+// No trailing comma.
+{(true, false)}
+
+// Multiple lines and items and trailing comma.
+{("1"
+ , #002
+ ,)}
+
+// Error: 3 expected closing paren
+{(}
+
+// Error: 2-3 expected expression, found closing paren
+{)}
+
+// Error: 2:4 expected comma
+// Error: 1:4-1:6 expected expression, found end of block comment
+{(1*/2)}
+
+// Error: 6-8 expected expression, found invalid token
+{(1, 1u 2)}
+
+// Error: 3-4 expected expression, found comma
+{(,1)}
+
+// Missing expression makes named pair incomplete, making this an empty array.
+// Error: 5 expected expression
+{(a:)}
+
+// Named pair after this is already identified as an array.
+// Error: 6-10 expected expression, found named pair
+{(1, b: 2)}
diff --git a/tests/typ/expr/assoc.typ b/tests/typ/expr/assoc.typ
new file mode 100644
index 00000000..19c56951
--- /dev/null
+++ b/tests/typ/expr/assoc.typ
@@ -0,0 +1,18 @@
+// Test operator associativity.
+// Ref: false
+
+---
+// Math operators are left-associative.
+#test(10 / 2 / 2 == (10 / 2) / 2, true)
+#test(10 / 2 / 2 == 10 / (2 / 2), false)
+#test(1 / 2 * 3, 1.5)
+
+---
+// Assignment is right-associative.
+{
+ let x = 1
+ let y = 2
+ x = y = "ok"
+ test(x, none)
+ test(y, "ok")
+}
diff --git a/tests/typ/expr/block-invalid.typ b/tests/typ/expr/block-invalid.typ
new file mode 100644
index 00000000..d98bf06b
--- /dev/null
+++ b/tests/typ/expr/block-invalid.typ
@@ -0,0 +1,37 @@
+// Test invalid code block syntax.
+
+---
+// Multiple unseparated expressions in one line.
+
+// Error: 2-4 expected expression, found invalid token
+{1u}
+
+// Should output `1`.
+// Error: 3 expected semicolon or line break
+{0 1}
+
+// Should output `2`.
+// Error: 2:12 expected semicolon or line break
+// Error: 1:22 expected semicolon or line break
+{let x = -1 let y = 3 x + y}
+
+// Should output `3`.
+{
+ // Error: 9-12 expected identifier, found string
+ for "v"
+
+ // Error: 10 expected keyword `in`
+ for v let z = 1 + 2
+
+ z
+}
+
+---
+// Ref: false
+// Error: 3:1 expected closing brace
+{
+
+---
+// Ref: false
+// Error: 1-2 unexpected closing brace
+}
diff --git a/tests/typ/expr/block-scoping.typ b/tests/typ/expr/block-scoping.typ
new file mode 100644
index 00000000..7bb98969
--- /dev/null
+++ b/tests/typ/expr/block-scoping.typ
@@ -0,0 +1,35 @@
+// Test scoping with blocks.
+// Ref: false
+
+---
+// Block in template does not create a scope.
+{ let x = 1 }
+#test(x, 1)
+
+---
+// Block in expression does create a scope.
+#let a = {
+ let b = 1
+ b
+}
+
+#test(a, 1)
+
+// Error: 2-3 unknown variable
+{b}
+
+---
+// Multiple nested scopes.
+{
+ let a = "a1"
+ {
+ let a = "a2"
+ {
+ test(a, "a2")
+ let a = "a3"
+ test(a, "a3")
+ }
+ test(a, "a2")
+ }
+ test(a, "a1")
+}
diff --git a/tests/typ/expr/block.typ b/tests/typ/expr/block.typ
new file mode 100644
index 00000000..196e6c14
--- /dev/null
+++ b/tests/typ/expr/block.typ
@@ -0,0 +1,38 @@
+// Test code blocks.
+
+---
+All none
+
+// Nothing evaluates to none.
+{}
+
+// Let evaluates to none.
+{ let v = 0 }
+
+// Trailing none evaluates to none.
+{
+ type("")
+ none
+}
+
+---
+// Evaluates to single expression.
+{ "Hello" }
+
+// Evaluates to trailing expression.
+{ let x = "Hel"; x + "lo" }
+
+// Evaluates to concatenation of for loop bodies.
+{
+ let parts = ("Hel", "lo")
+ for s in parts [{s}]
+}
+
+---
+// Works the same way in code environment.
+// Ref: false
+#test(3, {
+ let x = 1
+ let y = 2
+ x + y
+})
diff --git a/tests/typ/expr/call-invalid.typ b/tests/typ/expr/call-invalid.typ
new file mode 100644
index 00000000..0ed5246f
--- /dev/null
+++ b/tests/typ/expr/call-invalid.typ
@@ -0,0 +1,44 @@
+// Test invalid function calls.
+
+---
+// Error: 1-2 unexpected invalid token
+#
+
+---
+// Error: 7-8 expected expression, found colon
+#args(:)
+
+// Error: 10-12 expected expression, found end of block comment
+#args(a:1*/)
+
+// Error: 8 expected comma
+#args(1 2)
+
+// Error: 2:7-2:8 expected identifier
+// Error: 1:9 expected expression
+#args(1:)
+
+// Error: 7-8 expected identifier
+#args(1:2)
+
+// Error: 7-10 expected identifier
+{args((x):1)}
+
+---
+#let x = "string"
+
+// Error: 1-3 expected function, found string
+#x()
+
+---
+// Error: 3:1 expected closing bracket
+#args[`a]`
+
+---
+// Error: 7 expected closing paren
+{args(}
+
+---
+// Error: 3:1 expected quote
+// Error: 2:1 expected closing paren
+#args("]
diff --git a/tests/typ/expr/call.typ b/tests/typ/expr/call.typ
new file mode 100644
index 00000000..213d5554
--- /dev/null
+++ b/tests/typ/expr/call.typ
@@ -0,0 +1,40 @@
+// Test function calls.
+
+---
+// One argument.
+#args(bold)
+
+// One argument and trailing comma.
+#args(1,)
+
+// One named argument.
+#args(a:2)
+
+// Mixed arguments.
+{args(1, b: "2", 3)}
+
+---
+// Different forms of template arguments.
+// Ref: true
+
+#let a = "a"
+
+#args[a] \
+#args(a) \
+#args(a, [b]) \
+#args(a)[b] \
+
+// Template can be argument or body depending on whitespace.
+#if "template" == type[b] [Sure ]
+#if "template" == type [Nope.] #else [thing.]
+
+// Should output `<function args> (Okay.)`.
+#args (Okay.)
+
+---
+// Call function assigned to variable.
+#let alias = type
+#test(alias(alias), "function")
+
+// Library function `font` returns template.
+#test(type(font(12pt)), "template")
diff --git a/tests/typ/expr/dict.typ b/tests/typ/expr/dict.typ
new file mode 100644
index 00000000..655a3299
--- /dev/null
+++ b/tests/typ/expr/dict.typ
@@ -0,0 +1,20 @@
+// Test dictionaries.
+
+---
+// Empty
+{(:)}
+
+// Two pairs.
+{(a1: 1, a2: 2)}
+
+---
+// Simple expression after already being identified as a dictionary.
+// Error: 9-10 expected named pair, found expression
+{(a: 1, b)}
+
+// Identified as dictionary due to initial colon.
+// Error: 4:4-4:5 expected named pair, found expression
+// Error: 3:5 expected comma
+// Error: 2:12-2:16 expected identifier
+// Error: 1:17-1:18 expected expression, found colon
+{(:1 b:[], true::)}
diff --git a/tests/typ/expr/ops-invalid.typ b/tests/typ/expr/ops-invalid.typ
new file mode 100644
index 00000000..f760ae31
--- /dev/null
+++ b/tests/typ/expr/ops-invalid.typ
@@ -0,0 +1,59 @@
+// Test invalid expressions.
+// Ref: false
+
+---
+// Missing expressions.
+
+// Error: 3 expected expression
+{-}
+
+// Error: 10 expected expression
+#test({1+}, 1)
+
+// Error: 10 expected expression
+#test({2*}, 2)
+
+---
+// Mismatched types.
+
+// Error: 2-12 cannot apply '+' to template
+{+([] + [])}
+
+// Error: 2-5 cannot apply '-' to string
+{-""}
+
+// Error: 2-8 cannot apply 'not' to array
+{not ()}
+
+// Error: 1:2-1:12 cannot apply '<=' to relative and relative
+{30% <= 40%}
+
+// Special messages for +, -, * and /.
+// Error: 4:03-4:10 cannot add integer and string
+// Error: 3:12-3:19 cannot subtract integer from relative
+// Error: 2:21-2:29 cannot multiply integer with boolean
+// Error: 1:31-1:39 cannot divide integer by length
+{(1 + "2", 40% - 1, 2 * true, 3 / 12pt)}
+
+// Error: 14-22 cannot apply '+=' to integer and string
+{ let x = 1; x += "2" }
+
+---
+// Bad left-hand sides of assignment.
+
+// Error: 3-6 cannot assign to this expression
+{ (x) = "" }
+
+// Error: 3-8 cannot assign to this expression
+{ 1 + 2 += 3 }
+
+// Error: 3-4 unknown variable
+{ z = 1 }
+
+// Error: 3-6 cannot assign to a constant
+{ box = "hi" }
+
+// Works if we define box beforehand
+// (since then it doesn't resolve to the standard library version anymore).
+#let box = ""
+{ box = "hi" }
diff --git a/tests/typ/expr/ops.typ b/tests/typ/expr/ops.typ
new file mode 100644
index 00000000..2390b7b4
--- /dev/null
+++ b/tests/typ/expr/ops.typ
@@ -0,0 +1,150 @@
+// Test binary expressions.
+// Ref: false
+
+---
+// Test template addition.
+// Ref: true
+{[*Hello ] + [world!*]}
+
+---
+// Test math operators.
+
+// Test plus and minus.
+#for v in (1, 3.14, 12pt, 45deg, 90%, 13% + 10pt) {
+ // 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((1, 2) + (3, 4), (1, 2, 3, 4))
+#test((a: 1) + (b: 2, c: 3), (a: 1, b: 2, c: 3))
+
+// 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)
+
+// Mathematical identities.
+#let nums = (1, 3.14, 12pt, 45deg, 90%, 13% + 10pt)
+#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) != "integer" {
+ test(v + v, 2.0 * v)
+ }
+
+ // Linears cannot be divided by themselves.
+ if type(v) != "linear" {
+ test(v / v, 1.0)
+ test(v / v == 1, true)
+ }
+}
+
+// Make sure length, relative and linear
+// - can all be added to / subtracted from each other,
+// - multiplied with integers and floats,
+// - divided by floats.
+#let dims = (10pt, 30%, 50% + 3cm)
+#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 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.
+
+#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([*Hi*] == [*Hi*], true)
+
+#test("a" != "a", false)
+#test([*] != [_], true)
+
+---
+// 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 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")
diff --git a/tests/typ/expr/prec.typ b/tests/typ/expr/prec.typ
new file mode 100644
index 00000000..e64e583c
--- /dev/null
+++ b/tests/typ/expr/prec.typ
@@ -0,0 +1,30 @@
+// Test operator precedence.
+// Ref: false
+
+---
+// Multiplication binds stronger than addition.
+#test(1+2*-3, -5)
+
+// Subtraction binds stronger than comparison.
+#test(3 == 5 - 2, true)
+
+// Boolean operations bind stronger than '=='.
+#test("a" == "a" and 2 < 3, true)
+#test(not "b" == "b", false)
+
+// Assignment binds stronger than boolean operations.
+// Error: 2-7 cannot assign to this expression
+{not x = "a"}
+
+---
+// Parentheses override precedence.
+#test((1), 1)
+#test((1+2)*-3, -9)
+
+// Error: 14 expected closing paren
+#test({(1 + 1}, 2)
+
+---
+// Precedence doesn't matter for chained unary operators.
+// Error: 2-11 cannot apply '-' to boolean
+{-not true}
diff --git a/tests/typ/full/coma.typ b/tests/typ/full/coma.typ
new file mode 100644
index 00000000..03f6dcc3
--- /dev/null
+++ b/tests/typ/full/coma.typ
@@ -0,0 +1,50 @@
+// Configuration with `page` and `font` functions.
+#page(width: 450pt, height: 380pt, margins: 1cm)
+#font("CMU Serif")
+
+// There are variables and they can take normal values like strings, ...
+#let city = "Berlin"
+
+// ... but also "template" values. While these contain markup,
+// they are also values and can be summed, stored in arrays etc.
+// There are also more standard control flow structures, like #if and #for.
+#let university = [*Technische Universität {city}*]
+#let faculty = [*Fakultät II, Institut for Mathematik*]
+
+// The `box` function just places content into a rectangular container. When
+// the only argument to a function is a template, the parentheses can be omitted
+// (i.e. `f[a]` is the same as `f([a])`).
+#box[
+ // Backslash adds a forced line break.
+ #university \
+ #faculty \
+ Sekretariat MA \
+ Dr. Max Mustermann \
+ Ola Nordmann, John Doe
+]
+#align(right, box([*WiSe 2019/2020* \ Woche 3]))
+
+// Adds vertical spacing.
+#v(6mm)
+
+// If the last argument to a function is a template, we can also place it behind
+// the parentheses.
+#align(center)[
+ // Markdown-like syntax for headings.
+ ==== 3. Übungsblatt Computerorientierte Mathematik II #v(2mm)
+ *Abgabe: 03.05.2019* (bis 10:10 Uhr in MA 001) #v(2mm)
+ *Alle Antworten sind zu beweisen.*
+]
+
+*1. Aufgabe* #align(right)[(1 + 1 + 2 Punkte)]
+
+Ein _Binärbaum_ ist ein Wurzelbaum, in dem jeder Knoten ≤ 2 Kinder hat.
+Die Tiefe eines Knotens _v_ ist die Länge des eindeutigen Weges von der Wurzel
+zu _v_, und die Höhe von _v_ ist die Länge eines längsten (absteigenden) Weges
+von _v_ zu einem Blatt. Die Höhe des Baumes ist die Höhe der Wurzel.
+
+#v(6mm)
+
+// The `image` function returns a "template" value of the same type as
+// the `[...]` literals.
+#align(center, image("res/graph.png", width: 75%))
diff --git a/tests/typ/library/box.typ b/tests/typ/library/box.typ
new file mode 100644
index 00000000..57763f12
--- /dev/null
+++ b/tests/typ/library/box.typ
@@ -0,0 +1,20 @@
+#page("a7", flip: true)
+
+// Box with fixed width, should have text height.
+#box(width: 2cm, color: #9650D6)[A]
+
+Sometimes there is no box.
+
+// Box with fixed height, should span line.
+#box(height: 2cm, width: 100%, color: #734CED)[B]
+
+// Empty box with fixed width and height.
+#box(width: 6cm, height: 12pt, color: #CB4CED)
+
+// Not visiblem, but creates a gap between the boxes above and below.
+#box(width: 2in, color: #ff0000)
+
+// These are in a row!
+#box(width: 1in, height: 10pt, color: #D6CD67)
+#box(width: 1in, height: 10pt, color: #EDD466)
+#box(width: 1in, height: 10pt, color: #E3BE62)
diff --git a/tests/typ/library/font.typ b/tests/typ/library/font.typ
new file mode 100644
index 00000000..7f13e8a6
--- /dev/null
+++ b/tests/typ/library/font.typ
@@ -0,0 +1,56 @@
+// Test configuring font properties.
+
+#font("PT Sans", 10pt)
+
+// Set same font size in three different ways.
+#font(20pt)[A]
+#font(200%)[A]
+#font(15pt + 50%)[A]
+
+// Do nothing.
+#font[Normal]
+
+// Set style (is available).
+#font(style: italic)[Italic]
+
+// Set weight (is available).
+#font(weight: bold)[Bold]
+
+// Set stretch (not available, matching closest).
+#font(stretch: ultra-condensed)[Condensed]
+
+// Error: 7-12 unexpected argument
+#font(false)
+
+// Error: 3:14-3:18 expected font style, found font weight
+// Error: 2:28-2:34 expected font weight, found string
+// Error: 1:43-1:44 expected font family or array of font families, found integer
+#font(style: bold, weight: "thin", serif: 0)
+
+// Warning: 15-19 should be between 100 and 900
+#font(weight: 2700)
+
+// Error: 7-27 unexpected argument
+#font(something: "invalid")
+
+---
+// Test font fallback and class definitions.
+
+// Source Sans Pro + Segoe UI Emoji.
+Emoji: 🏀
+
+// CMU Serif + Noto Emoji.
+#font("CMU Serif", "Noto Emoji")[
+ Emoji: 🏀
+]
+
+// Class definitions.
+#font(serif: ("CMU Serif", "Latin Modern Math", "Noto Emoji"))
+#font(serif)[
+ Math: ∫ α + β ➗ 3
+]
+
+// Class definition reused.
+#font(sans-serif: "Noto Emoji")
+#font(sans-serif: ("Archivo", sans-serif))
+New sans-serif. 🚀
diff --git a/tests/typ/library/hv.typ b/tests/typ/library/hv.typ
new file mode 100644
index 00000000..154445cf
--- /dev/null
+++ b/tests/typ/library/hv.typ
@@ -0,0 +1,22 @@
+// Ends paragraphs.
+Tightly #v(-5pt) packed
+
+// Eating up soft spacing.
+Inv #h(0pt) isible
+
+// Multiple spacings in a row.
+Add #h(10pt) #h(10pt) up
+
+// Relative to font size.
+Relative #h(100%) spacing
+
+// Missing spacing.
+// Error: 12 missing argument: spacing
+Totally #h() ignored
+
+// Swapped axes.
+#page(main-dir: rtl, cross-dir: ttb, height: 80pt)[
+ 1 #h(1cm) 2
+
+ 3 #v(1cm) 4 #v(-1cm) 5
+]
diff --git a/tests/typ/library/image.typ b/tests/typ/library/image.typ
new file mode 100644
index 00000000..9c3cd4fe
--- /dev/null
+++ b/tests/typ/library/image.typ
@@ -0,0 +1,35 @@
+// Test loading different image formats.
+
+// Load an RGBA PNG image.
+#image("res/rhino.png")
+#pagebreak()
+
+// Load an RGB JPEG image.
+#image("res/tiger.jpg")
+
+// Error: 8-29 failed to load image
+#image("path/does/not/exist")
+
+// Error: 8-29 failed to load image
+#image("typ/image-error.typ")
+
+---
+// Test configuring the size and fitting behaviour of images.
+
+// Fit to width of page.
+#image("res/rhino.png")
+
+// Fit to height of page.
+#page(height: 40pt, image("res/rhino.png"))
+
+// Set width explicitly.
+#image("res/rhino.png", width: 50pt)
+
+// Set height explicitly.
+#image("res/rhino.png", height: 50pt)
+
+// Set width and height explicitly and force stretching.
+#image("res/rhino.png", width: 25pt, height: 50pt)
+
+// Make sure the bounding-box of the image is correct.
+#align(bottom, right, image("res/tiger.jpg", width: 60pt))
diff --git a/tests/typ/library/page.typ b/tests/typ/library/page.typ
new file mode 100644
index 00000000..b7ac0348
--- /dev/null
+++ b/tests/typ/library/page.typ
@@ -0,0 +1,53 @@
+// Test configuring page sizes and margins.
+
+// Set width and height.
+#page(width: 120pt, height: 120pt)
+#page(width: 40pt)[High]
+#page(height: 40pt)[Wide]
+
+// Set all margins at once.
+#page(margins: 30pt)[
+ #align(top, left)[TL]
+ #align(bottom, right)[BR]
+]
+
+// Set individual margins.
+#page(height: 40pt)
+#page(left: 0pt, align(left)[Left])
+#page(right: 0pt, align(right)[Right])
+#page(top: 0pt, align(top)[Top])
+#page(bottom: 0pt, align(bottom)[Bottom])
+
+// Ensure that specific margins override general margins.
+#page(margins: 0pt, left: 20pt)[Overriden]
+
+// Error: 7-18 unknown variable
+#page(nonexistant)
+
+// Error: 17-20 aligned axis
+#page(main-dir: ltr)
+
+// Flipped predefined paper.
+#page("a11", flip: true)[Flipped A11]
+
+// Flipped custom page size.
+#page(width: 40pt, height: 120pt)
+#page(flip: true)
+Wide
+
+// Test changing the layouting directions of pages.
+#page(height: 50pt, main-dir: btt, cross-dir: rtl)
+Right to left!
+
+---
+// Test a combination of pages with bodies and normal content.
+
+#page(height: 50pt)
+
+#page[First]
+#page[Second]
+#pagebreak()
+Fourth
+#page[]
+Sixth
+#page[Seventh and last]
diff --git a/tests/typ/library/pagebreak.typ b/tests/typ/library/pagebreak.typ
new file mode 100644
index 00000000..dc3e5682
--- /dev/null
+++ b/tests/typ/library/pagebreak.typ
@@ -0,0 +1,3 @@
+First of two
+#pagebreak()
+#page(height: 40pt)
diff --git a/tests/typ/library/rgb.typ b/tests/typ/library/rgb.typ
new file mode 100644
index 00000000..b962bbc0
--- /dev/null
+++ b/tests/typ/library/rgb.typ
@@ -0,0 +1,17 @@
+// Check the output.
+#rgb(0.0, 0.3, 0.7)
+
+// Alpha channel.
+#rgb(1.0, 0.0, 0.0, 0.5)
+
+// Warning: 2:6-2:9 should be between 0.0 and 1.0
+// Warning: 1:11-1:15 should be between 0.0 and 1.0
+#rgb(-30, 15.5, 0.5)
+
+// Error: 6-10 missing argument: blue component
+#rgb(0, 1)
+
+// Error: 3:6-3:6 missing argument: red component
+// Error: 2:6-2:6 missing argument: green component
+// Error: 1:6-1:6 missing argument: blue component
+#rgb()
diff --git a/tests/typ/markup/emph.typ b/tests/typ/markup/emph.typ
new file mode 100644
index 00000000..772e15ab
--- /dev/null
+++ b/tests/typ/markup/emph.typ
@@ -0,0 +1,14 @@
+// Test emphasis toggle.
+
+---
+// Basic.
+_Emphasized!_
+
+// Inside of words.
+Partly em_phas_ized.
+
+// Scoped to body.
+#box[_Scoped] to body.
+
+// Unterminated is fine.
+_The End
diff --git a/tests/typ/markup/escape.typ b/tests/typ/markup/escape.typ
new file mode 100644
index 00000000..eeac4997
--- /dev/null
+++ b/tests/typ/markup/escape.typ
@@ -0,0 +1,30 @@
+// Test escape sequences.
+
+---
+// Escapable symbols.
+\\ \/ \[ \] \{ \} \# \* \_ \= \~ \` \$
+
+// No need to escape.
+( ) ; < >
+
+// Unescapable.
+\a \: \; \( \)
+
+// Escaped comments.
+\//
+\/\* \*\/
+\/* \*/ *
+
+// Unicode escape sequence.
+\u{1F3D5} == 🏕
+
+// Escaped escape sequence.
+\u{41} vs. \\u\{41\}
+
+// Unicode codepoint does not exist.
+// Error: 1-11 invalid unicode escape sequence
+\u{FFFFFF}
+
+// Unterminated.
+// Error: 6 expected closing brace
+\u{41*Bold*
diff --git a/tests/typ/markup/heading.typ b/tests/typ/markup/heading.typ
new file mode 100644
index 00000000..8497ec8f
--- /dev/null
+++ b/tests/typ/markup/heading.typ
@@ -0,0 +1,44 @@
+// Test headings.
+
+---
+// Different number of hashtags.
+
+// Valid levels.
+=1
+===2
+======6
+
+// Too many hashtags.
+// Warning: 1-8 should not exceed depth 6
+=======7
+
+---
+// Heading continuation over linebreak.
+
+// Code blocks continue heading.
+= A{
+ "B"
+}
+
+// Function call continues heading.
+= #box[
+ A
+] B
+
+// Without some kind of block, headings end at a line break.
+= A
+B
+
+---
+// Heading vs. no heading.
+
+// Parsed as headings if at start of the context.
+/**/ = Ok
+{[== Ok]}
+#box[=== Ok]
+
+// Not at the start of the context.
+No = heading
+
+// Escaped.
+\= No heading
diff --git a/tests/typ/markup/linebreak.typ b/tests/typ/markup/linebreak.typ
new file mode 100644
index 00000000..e6392992
--- /dev/null
+++ b/tests/typ/markup/linebreak.typ
@@ -0,0 +1,23 @@
+// Test forced line breaks.
+
+---
+// Directly after word.
+Line\ Break
+
+// Spaces around.
+Line \ Break
+
+// Directly before word does not work.
+No \Break
+
+---
+// Leading line break.
+\ Leading
+
+// Trailing before paragraph break.
+Trailing 1 \
+
+Trailing 2
+
+// Trailing before end of document.
+Trailing 3 \
diff --git a/tests/typ/markup/nbsp.typ b/tests/typ/markup/nbsp.typ
new file mode 100644
index 00000000..5af6c84f
--- /dev/null
+++ b/tests/typ/markup/nbsp.typ
@@ -0,0 +1,5 @@
+// Test the non breaking space.
+
+---
+// Parsed correctly, but not actually doing anything at the moment.
+The non-breaking~space does not work.
diff --git a/tests/typ/markup/raw.typ b/tests/typ/markup/raw.typ
new file mode 100644
index 00000000..f5074c8e
--- /dev/null
+++ b/tests/typ/markup/raw.typ
@@ -0,0 +1,51 @@
+// Test raw blocks.
+
+---
+// No extra space.
+`A``B`
+
+---
+// Typst syntax inside.
+`#let x = 1` \
+`#[f 1]`
+
+---
+// Multiline block splits paragraphs.
+
+First
+```
+Second
+```
+Third
+
+---
+// Lots of backticks inside.
+````
+```backticks```
+````
+
+---
+// Trimming.
+
+// Space between "rust" and "let" is trimmed.
+The keyword ```rust let```.
+
+// Trimming depends on number backticks.
+<``> \
+<` untrimmed `> \
+<``` trimmed` ```> \
+<``` trimmed ```> \
+<``` trimmed```>
+
+// Multiline trimming.
+```py
+import this
+
+def hi():
+ print("Hi!")
+```
+
+---
+// Unterminated.
+// Error: 2:1 expected backtick(s)
+`endless
diff --git a/tests/typ/markup/strong.typ b/tests/typ/markup/strong.typ
new file mode 100644
index 00000000..b02a55a5
--- /dev/null
+++ b/tests/typ/markup/strong.typ
@@ -0,0 +1,14 @@
+// Test strong toggle.
+
+---
+// Basic.
+*Strong!*
+
+// Inside of words.
+Partly str*ength*ened.
+
+// Scoped to body.
+#box[*Scoped] to body.
+
+// Unterminated is fine.
+*The End
diff --git a/tests/typ/repr.typ b/tests/typ/repr.typ
new file mode 100644
index 00000000..666db428
--- /dev/null
+++ b/tests/typ/repr.typ
@@ -0,0 +1,50 @@
+// Test representation of values in the document.
+
+---
+// Variables.
+
+#let name = "Typst"
+#let ke-bab = "Kebab!"
+#let α = "Alpha"
+
+{name} \
+{ke-bab} \
+{α} \
+
+// Error: 2-3 unknown variable
+{_}
+
+---
+// Literal values.
+{none} (empty) \
+{true} \
+{false} \
+
+---
+// Numerical values.
+{1} \
+{1.0e-4} \
+{3.15} \
+{1e-10} \
+{50.368%} \
+{0.0000012345pt} \
+{4.5cm} \
+{12e1pt} \
+{2.5rad} \
+{45deg} \
+
+// Not in monospace via repr.
+#repr(45deg)
+
+---
+// Colors.
+{#f7a20500} \
+
+---
+// Strings and escaping.
+{"hi"} \
+{"a\n[]\"\u{1F680}string"} \
+
+---
+// Templates.
+{[*{"H" + "i"} there*]}
diff --git a/tests/typ/spacing.typ b/tests/typ/spacing.typ
new file mode 100644
index 00000000..d44cd84c
--- /dev/null
+++ b/tests/typ/spacing.typ
@@ -0,0 +1,27 @@
+// Test spacing around control flow structures.
+
+---
+// Spacing around let.
+
+// Error: 6 expected identifier
+A#let;B \
+A#let x = 1;B #test(x, 1) \
+A #let x = 2;B #test(x, 2) \
+A#let x = 3; B #test(x, 3) \
+
+---
+// Spacing around if-else.
+
+A#if true[B]C \
+A#if true[B] C \
+A #if true{"B"}C \
+A #if true{"B"} C \
+A#if false [] #else [B]C \
+A#if true [B] #else [] C \
+
+---
+// Spacing around for loop.
+
+A#for _ in (none,) [B]C \
+A#for _ in (none,) [B] C \
+A #for _ in (none,) [B]C \
diff --git a/tests/typ/text.typ b/tests/typ/text.typ
new file mode 100644
index 00000000..d86f4895
--- /dev/null
+++ b/tests/typ/text.typ
@@ -0,0 +1,8 @@
+// Test simple text.
+
+---
+Hello 🌏!
+
+---
+// Some code stuff in text.
+let f() , ; : | + - /= == 12 "string"