summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-01-17 13:53:22 +0100
committerLaurenz <laurmaedje@gmail.com>2021-01-17 13:53:22 +0100
commit29be90bf95f2ea10c435e7b02f8c26626b956417 (patch)
treeb6186120a443664c0d016730725b27f7c550eb78
parentcc5f14193c25200e254e64cc594b15e84da280f9 (diff)
Assertions with [eq] + better tests 🩺
-rw-r--r--src/eval/call.rs28
-rw-r--r--src/parse/mod.rs10
-rw-r--r--src/parse/parser.rs85
-rw-r--r--tests/lang/ref/arrays.pngbin3479 -> 3609 bytes
-rw-r--r--tests/lang/ref/basics.pngbin5051 -> 0 bytes
-rw-r--r--tests/lang/ref/blocks.pngbin1363 -> 799 bytes
-rw-r--r--tests/lang/ref/emph-strong.pngbin0 -> 4117 bytes
-rw-r--r--tests/lang/ref/expressions.pngbin2351 -> 0 bytes
-rw-r--r--tests/lang/ref/headings.pngbin7850 -> 7985 bytes
-rw-r--r--tests/lang/ref/let.pngbin1493 -> 1563 bytes
-rw-r--r--tests/lang/ref/linebreaks.pngbin0 -> 4161 bytes
-rw-r--r--tests/lang/ref/nbsp.pngbin0 -> 1854 bytes
-rw-r--r--tests/lang/ref/raw.pngbin4212 -> 4071 bytes
-rw-r--r--tests/lang/ref/text.pngbin0 -> 731 bytes
-rw-r--r--tests/lang/ref/values.pngbin8531 -> 8651 bytes
-rw-r--r--tests/lang/typ/arrays.typ17
-rw-r--r--tests/lang/typ/basics.typ14
-rw-r--r--tests/lang/typ/blocks.typ19
-rw-r--r--tests/lang/typ/comments.typ9
-rw-r--r--tests/lang/typ/dictionaries.typ2
-rw-r--r--tests/lang/typ/emph-strong.typ11
-rw-r--r--tests/lang/typ/escaping.typ2
-rw-r--r--tests/lang/typ/expressions.typ42
-rw-r--r--tests/lang/typ/headings.typ22
-rw-r--r--tests/lang/typ/let.typ61
-rw-r--r--tests/lang/typ/linebreaks.typ19
-rw-r--r--tests/lang/typ/nbsp.typ2
-rw-r--r--tests/lang/typ/raw.typ21
-rw-r--r--tests/lang/typ/text.typ1
-rw-r--r--tests/lang/typ/values.typ28
-rw-r--r--tests/typeset.rs26
31 files changed, 284 insertions, 135 deletions
diff --git a/src/eval/call.rs b/src/eval/call.rs
index 8e75f17c..5b0628a8 100644
--- a/src/eval/call.rs
+++ b/src/eval/call.rs
@@ -73,7 +73,7 @@ impl Args {
where
T: Cast<Spanned<Value>>,
{
- self.pos.iter_mut().find_map(move |slot| try_cast(ctx, slot))
+ (0 .. self.pos.len()).find_map(move |i| try_cast(ctx, &mut self.pos, i))
}
/// Find and remove the first convertible positional argument, producing an
@@ -97,7 +97,16 @@ impl Args {
where
T: Cast<Spanned<Value>>,
{
- self.pos.iter_mut().filter_map(move |slot| try_cast(ctx, slot))
+ let mut i = 0;
+ std::iter::from_fn(move || {
+ while i < self.pos.len() {
+ if let Some(val) = try_cast(ctx, &mut self.pos, i) {
+ return Some(val);
+ }
+ i += 1;
+ }
+ None
+ })
}
/// Convert and remove the value for the given named argument, producing an
@@ -163,17 +172,26 @@ where
/// Try to cast the value in the slot into `T`, putting it back if the
/// conversion fails.
-fn try_cast<T>(ctx: &mut EvalContext, slot: &mut Spanned<Value>) -> Option<T>
+fn try_cast<T>(
+ ctx: &mut EvalContext,
+ vec: &mut Vec<Spanned<Value>>,
+ i: usize,
+) -> Option<T>
where
T: Cast<Spanned<Value>>,
{
// Replace with error placeholder when conversion works since error values
// are ignored when generating "unexpected argument" errors.
- let value = std::mem::replace(slot, Spanned::zero(Value::Error));
+ let slot = &mut vec[i];
+ let value = std::mem::replace(slot, Spanned::zero(Value::None));
let span = value.span;
match T::cast(value) {
- CastResult::Ok(t) => Some(t),
+ CastResult::Ok(t) => {
+ vec.remove(i);
+ Some(t)
+ }
CastResult::Warn(t, m) => {
+ vec.remove(i);
ctx.diag(warning!(span, "{}", m));
Some(t)
}
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index b6836d38..7c92185d 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -117,7 +117,7 @@ fn heading(p: &mut Parser) -> NodeHeading {
// Parse the heading contents.
let mut contents = vec![];
- while p.check(|t| !matches!(t, Token::Space(n) if n > 0)) {
+ while p.check(|t| !matches!(t, Token::Space(n) if n >= 1)) {
if let Some(node) = p.span_if(|p| node(p, &mut false)) {
contents.push(node);
}
@@ -388,8 +388,8 @@ fn string(p: &mut Parser, token: TokenStr) -> String {
/// Parse a let expresion.
fn expr_let(p: &mut Parser) -> Option<Expr> {
p.push_mode(TokenMode::Code);
- p.start_group(Group::Terminated);
p.eat_assert(Token::Let);
+ p.start_group(Group::Expr);
let pat = p.span_if(ident);
let mut rhs = None;
@@ -404,11 +404,11 @@ fn expr_let(p: &mut Parser) -> Option<Expr> {
p.diag_expected("identifier");
}
- while !p.eof() {
- p.diag_unexpected();
+ p.pop_mode();
+ if !p.eof() {
+ p.diag_expected("semicolon or line break");
}
- p.pop_mode();
p.end_group();
pat.map(|pat| Expr::Let(ExprLet { pat, expr: rhs }))
}
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index 2b5fe720..8c27c8f7 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -112,16 +112,14 @@ impl<'s> Parser<'s> {
/// # Panics
/// This panics if the next token does not start the given group.
pub fn start_group(&mut self, group: Group) {
+ self.groups.push(group);
match group {
Group::Paren => self.eat_assert(Token::LeftParen),
Group::Bracket => self.eat_assert(Token::LeftBracket),
Group::Brace => self.eat_assert(Token::LeftBrace),
- Group::Subheader => {}
- Group::Terminated => {}
+ Group::Expr => self.repeek(),
+ Group::Subheader => self.repeek(),
}
-
- self.groups.push(group);
- self.repeek();
}
/// Ends the parsing of a group and returns the span of the whole group.
@@ -129,26 +127,21 @@ impl<'s> Parser<'s> {
/// # Panics
/// This panics if no group was started.
pub fn end_group(&mut self) {
- // Check that we are indeed at the end of the group.
- debug_assert_eq!(self.peek(), None, "unfinished group");
-
let group = self.groups.pop().expect("no started group");
self.repeek();
- let end = match group {
- Group::Paren => Some(Token::RightParen),
- Group::Bracket => Some(Token::RightBracket),
- Group::Brace => Some(Token::RightBrace),
- Group::Subheader => None,
- Group::Terminated => Some(Token::Semicolon),
+ let (end, required) = match group {
+ Group::Paren => (Token::RightParen, true),
+ Group::Bracket => (Token::RightBracket, true),
+ Group::Brace => (Token::RightBrace, true),
+ Group::Expr => (Token::Semicolon, false),
+ Group::Subheader => return,
};
- if let Some(token) = end {
- if self.next == Some(token) {
- self.bump();
- } else {
- self.diag(error!(self.next_start, "expected {}", token.name()));
- }
+ if self.next == Some(end) {
+ self.bump();
+ } else if required {
+ self.diag(error!(self.next_start, "expected {}", end.name()));
}
}
@@ -169,7 +162,7 @@ impl<'s> Parser<'s> {
where
F: FnOnce(&mut Self) -> Option<T>,
{
- self.span(|p| f(p)).transpose()
+ self.span(f).transpose()
}
/// Consume the next token.
@@ -269,17 +262,21 @@ impl<'s> Parser<'s> {
match self.tokens.mode() {
TokenMode::Markup => {}
- TokenMode::Code => {
- while matches!(
- self.next,
- Some(Token::Space(_)) |
- Some(Token::LineComment(_)) |
- Some(Token::BlockComment(_))
- ) {
- self.next_start = self.tokens.pos();
- self.next = self.tokens.next();
+ TokenMode::Code => loop {
+ match self.next {
+ Some(Token::Space(n)) => {
+ if n >= 1 && self.groups.last() == Some(&Group::Expr) {
+ break;
+ }
+ }
+ Some(Token::LineComment(_)) => {}
+ Some(Token::BlockComment(_)) => {}
+ _ => break,
}
- }
+
+ self.next_start = self.tokens.pos();
+ self.next = self.tokens.next();
+ },
}
self.repeek();
@@ -287,16 +284,22 @@ impl<'s> Parser<'s> {
fn repeek(&mut self) {
self.peeked = self.next;
- if self.groups.contains(&match self.next {
- Some(Token::RightParen) => Group::Paren,
- Some(Token::RightBracket) => Group::Bracket,
- Some(Token::RightBrace) => Group::Brace,
- Some(Token::Pipe) => Group::Subheader,
- Some(Token::Semicolon) => Group::Terminated,
+ let token = match self.next {
+ Some(token) => token,
+ None => return,
+ };
+
+ match token {
+ Token::RightParen if self.groups.contains(&Group::Paren) => {}
+ Token::RightBracket if self.groups.contains(&Group::Bracket) => {}
+ Token::RightBrace if self.groups.contains(&Group::Brace) => {}
+ Token::Semicolon if self.groups.contains(&Group::Expr) => {}
+ Token::Space(n) if n >= 1 && self.groups.last() == Some(&Group::Expr) => {}
+ Token::Pipe if self.groups.contains(&Group::Subheader) => {}
_ => return,
- }) {
- self.peeked = None;
}
+
+ self.peeked = None;
}
}
@@ -316,9 +319,9 @@ pub enum Group {
Bracket,
/// A curly-braced group: `{...}`.
Brace,
+ /// A group ended by a semicolon or a line break: `;`, `\n`.
+ Expr,
/// A group ended by a chained subheader or a closing bracket:
/// `... >>`, `...]`.
Subheader,
- /// A group ended by a semicolon: `;`.
- Terminated,
}
diff --git a/tests/lang/ref/arrays.png b/tests/lang/ref/arrays.png
index 2d70a3f6..36845b5f 100644
--- a/tests/lang/ref/arrays.png
+++ b/tests/lang/ref/arrays.png
Binary files differ
diff --git a/tests/lang/ref/basics.png b/tests/lang/ref/basics.png
deleted file mode 100644
index 3c3dd7a0..00000000
--- a/tests/lang/ref/basics.png
+++ /dev/null
Binary files differ
diff --git a/tests/lang/ref/blocks.png b/tests/lang/ref/blocks.png
index ca826c1b..22a5722a 100644
--- a/tests/lang/ref/blocks.png
+++ b/tests/lang/ref/blocks.png
Binary files differ
diff --git a/tests/lang/ref/emph-strong.png b/tests/lang/ref/emph-strong.png
new file mode 100644
index 00000000..36392f8c
--- /dev/null
+++ b/tests/lang/ref/emph-strong.png
Binary files differ
diff --git a/tests/lang/ref/expressions.png b/tests/lang/ref/expressions.png
deleted file mode 100644
index 309c32f1..00000000
--- a/tests/lang/ref/expressions.png
+++ /dev/null
Binary files differ
diff --git a/tests/lang/ref/headings.png b/tests/lang/ref/headings.png
index 1f374cce..c8d83165 100644
--- a/tests/lang/ref/headings.png
+++ b/tests/lang/ref/headings.png
Binary files differ
diff --git a/tests/lang/ref/let.png b/tests/lang/ref/let.png
index be9d8397..d9833391 100644
--- a/tests/lang/ref/let.png
+++ b/tests/lang/ref/let.png
Binary files differ
diff --git a/tests/lang/ref/linebreaks.png b/tests/lang/ref/linebreaks.png
new file mode 100644
index 00000000..5268ed64
--- /dev/null
+++ b/tests/lang/ref/linebreaks.png
Binary files differ
diff --git a/tests/lang/ref/nbsp.png b/tests/lang/ref/nbsp.png
new file mode 100644
index 00000000..537a70eb
--- /dev/null
+++ b/tests/lang/ref/nbsp.png
Binary files differ
diff --git a/tests/lang/ref/raw.png b/tests/lang/ref/raw.png
index f88194c0..8b68922a 100644
--- a/tests/lang/ref/raw.png
+++ b/tests/lang/ref/raw.png
Binary files differ
diff --git a/tests/lang/ref/text.png b/tests/lang/ref/text.png
new file mode 100644
index 00000000..54479229
--- /dev/null
+++ b/tests/lang/ref/text.png
Binary files differ
diff --git a/tests/lang/ref/values.png b/tests/lang/ref/values.png
index 762ad64e..d4411cc8 100644
--- a/tests/lang/ref/values.png
+++ b/tests/lang/ref/values.png
Binary files differ
diff --git a/tests/lang/typ/arrays.typ b/tests/lang/typ/arrays.typ
index 0b8fb866..3c290e50 100644
--- a/tests/lang/typ/arrays.typ
+++ b/tests/lang/typ/arrays.typ
@@ -1,6 +1,9 @@
// Empty.
{()}
+// Not an array, just a parenthesized expression.
+{(1)}
+
// One item and trailing comma.
{(-1,)}
@@ -13,21 +16,31 @@
, #003
,)}
+// Missing closing paren.
// Error: 1:3-1:3 expected closing paren
{(}
-// Error: 2:4-2:6 expected expression, found end of block comment
-// Error: 1:4-1:4 expected comma
+// Not an array.
+// Error: 1:2-1:3 expected expression, found closing paren
+{)}
+
+// Missing comma and bad expression.
+// Error: 2:4-2:4 expected comma
+// Error: 1:4-1:6 expected expression, found end of block comment
{(1*/2)}
+// Bad expression.
// Error: 1:6-1:8 expected expression, found invalid token
{(1, 1u 2)}
+// Leading comma is not an expression.
// Error: 1:3-1:4 expected expression, found comma
{(,1)}
+// Missing expression makes named pair incomplete, making this an empty array.
// Error: 1:5-1:5 expected expression
{(a:)}
+// Named pair after this is already identified as an array.
// Error: 1:6-1:10 expected expression, found named pair
{(1, b: 2)}
diff --git a/tests/lang/typ/basics.typ b/tests/lang/typ/basics.typ
deleted file mode 100644
index 9220e376..00000000
--- a/tests/lang/typ/basics.typ
+++ /dev/null
@@ -1,14 +0,0 @@
-Hello 🌏!
-
-_Emph_ and *strong*!
-
-The non-breaking~space does not work.
-
-// Directly after word.
-Line\ Break
-
-// Spaces around.
-Line \ Break
-
-// Directly before word does not work.
-No \Break
diff --git a/tests/lang/typ/blocks.typ b/tests/lang/typ/blocks.typ
index cadd30dd..faef0987 100644
--- a/tests/lang/typ/blocks.typ
+++ b/tests/lang/typ/blocks.typ
@@ -1,21 +1,26 @@
+// Basic expression.
{1}
-// Function calls.
-{f(1)}
-{[[f 1]]}
-
// Error: 1:2-1:2 expected expression
{}
+// Bad expression.
// Error: 1:2-1:4 expected expression, found invalid token
{1u}
+// Two expressions are not allowed.
+// Error: 1:4-1:5 unexpected integer
+{2 3}
+
+// Missing closing brace in nested block.
// Error: 1:5-1:5 expected closing brace
{({1) + 2}
-// Error: 1:12-1:12 expected closing bracket
-{[*] + [ok*}
+// Missing closing bracket in template expression.
+// Error: 1:11-1:11 expected closing bracket
+{[_] + [4_}
+// Opening brace is ignored after one expression is parsed.
// Error: 2:4-2:5 unexpected hex value
// Error: 1:5-1:6 unexpected opening brace
-{1 #{} _end_
+{5 #{}*.*
diff --git a/tests/lang/typ/comments.typ b/tests/lang/typ/comments.typ
index c5b04967..5118a05e 100644
--- a/tests/lang/typ/comments.typ
+++ b/tests/lang/typ/comments.typ
@@ -1,18 +1,19 @@
-// Test whether line comment acts as spacing.
+// Line comment acts as spacing.
A// you
B
-// Test whether block comment acts as spacing.
+// Block comment does not act as spacing.
C/*
/* */
*/D
-// Test in expressions.
+// Works in headers.
[f /*1*/ a: "b" //
, 1]
+// End should not appear without start.
// Error: 1:7-1:9 unexpected end of block comment
/* */ */
-// Unterminated block comment is okay.
+// Unterminated is okay.
/*
diff --git a/tests/lang/typ/dictionaries.typ b/tests/lang/typ/dictionaries.typ
index c729b92c..63d9db9e 100644
--- a/tests/lang/typ/dictionaries.typ
+++ b/tests/lang/typ/dictionaries.typ
@@ -4,9 +4,11 @@
// Two pairs.
{(one: 1, two: 2)}
+// Simple expression after this is already identified as a dictionary.
// Error: 1:9-1:10 expected named pair, found expression
{(a: 1, b)}
+// Identified as dictionary by initial colon.
// Error: 4:4-4:5 expected named pair, found expression
// Error: 3:5-3:5 expected comma
// Error: 2:12-2:16 expected identifier
diff --git a/tests/lang/typ/emph-strong.typ b/tests/lang/typ/emph-strong.typ
new file mode 100644
index 00000000..dcd17337
--- /dev/null
+++ b/tests/lang/typ/emph-strong.typ
@@ -0,0 +1,11 @@
+// Basic.
+_Emph_ and *strong*!
+
+// Inside of words.
+Pa_rtl_y emphasized or str*ength*ened.
+
+// Scoped to body.
+[box][*Sco_ped] to body.
+
+// Unterminated is fine.
+_The End
diff --git a/tests/lang/typ/escaping.typ b/tests/lang/typ/escaping.typ
index cb663612..abf124ce 100644
--- a/tests/lang/typ/escaping.typ
+++ b/tests/lang/typ/escaping.typ
@@ -18,8 +18,10 @@
// Escaped escape sequence.
\u{41} vs. \\u\{41\}
+// Unicode codepoint does not exist.
// Error: 1:1-1:11 invalid unicode escape sequence
\u{FFFFFF}
+// Unterminated.
// Error: 1:6-1:6 expected closing brace
\u{41*Bold*
diff --git a/tests/lang/typ/expressions.typ b/tests/lang/typ/expressions.typ
index 01725289..74150c4b 100644
--- a/tests/lang/typ/expressions.typ
+++ b/tests/lang/typ/expressions.typ
@@ -1,37 +1,43 @@
+// Ref: false
+
#let a = 2;
#let b = 4;
+// Paren call.
+[eq f(1), "f(1)"]
+[eq type(1), "integer"]
+
// Unary operations.
-{+1}
-{-1}
-{--1}
+[eq +1, 1]
+[eq -1, 1-2]
+[eq --1, 1]
// Binary operations.
-{"a"+"b"}
-{1-2}
-{a * b}
-{12pt/.4}
+[eq "a" + "b", "ab"]
+[eq 1-4, 3*-1]
+[eq a * b, 8]
+[eq 12pt/.4, 30pt]
// Associativity.
-{1+2+3}
-{1/2*3}
+[eq 1+2+3, 6]
+[eq 1/2*3, 1.5]
// Precedence.
-{1+2*-3}
+[eq 1+2*-3, -5]
// Parentheses.
-{(a)}
-{(2)}
-{(1+2)*3}
+[eq (a), 2]
+[eq (2), 2]
+[eq (1+2)*3, 9]
// Confusion with floating-point literal.
-{1e+2-1e-2}
+[eq 1e+2-1e-2, 99.99]
// Error: 1:3-1:3 expected expression
{-}
-// Error: 1:4-1:4 expected expression
-{1+}
+// Error: 1:8-1:8 expected expression
+[eq {1+}, 1]
-// Error: 1:4-1:4 expected expression
-{2*}
+// Error: 1:8-1:8 expected expression
+[eq {2*}, 2]
diff --git a/tests/lang/typ/headings.typ b/tests/lang/typ/headings.typ
index f62f6534..765d0377 100644
--- a/tests/lang/typ/headings.typ
+++ b/tests/lang/typ/headings.typ
@@ -1,38 +1,48 @@
// Test different numbers of hashtags.
+// Valid levels.
# One
### Three
###### Six
+// Too many hashtags.
// Warning: 1:1-1:8 section depth should not exceed 6
####### Seven
---
// Test heading vs. no heading.
+// Parsed as headings if at start of the context.
/**/ # Heading
{[## Heading]}
[box][### Heading]
-\# No heading
-
+// Not at the start of the context.
Text with # hashtag
-Nr#1
+// Escaped.
+\# No heading
+// Keyword.
// Error: 1:1-1:6 unexpected invalid token
#nope
+// Not parsed as a keyword, but neither as a heading.
+Nr#1
+
---
// Heading continuation over linebreak.
+// Code blocks continue heading.
# This {
- "works"
+ "continues"
}
+// Function call continues heading.
# [box][
- This
+ This,
] too
+// Without some kind of block, headings end at a line break.
# This
-does not
+not
diff --git a/tests/lang/typ/let.typ b/tests/lang/typ/let.typ
index c7bba747..a966f243 100644
--- a/tests/lang/typ/let.typ
+++ b/tests/lang/typ/let.typ
@@ -1,20 +1,53 @@
+// Ref: false
+
// Automatically initialized with `none`.
-#let x;
-{(x,)}
+#let x
+[eq x, none]
-// Can start with underscore.
-#let _y=1;
-{_y}
+// Initialized with `1`.
+#let y = 1;
+[eq y, 1]
-// Multiline.
-#let z = "world"
- + " 🌏"; Hello, {z}!
+// Multiple bindings in one line.
+#let x = "a"; #let y = "b"; [eq x + y, "ab"]
+// No name.
// Error: 1:6-1:7 expected identifier, found integer
-#let 1;
+#let 1
+
+---
+// Terminated with just a line break.
+#let v = "a"
+First
+[eq v, "a"]
+
+// Terminated with just a semicolon.
+#let v = "a"; Second
+[eq v, "a"]
+
+// Terminated with semicolon + line break.
+#let v = "a";
+Third
+[eq v, "a"]
+
+// Terminated by semicolon even though we are in a paren group.
+// Error: 2:22-2:22 expected expression
+// Error: 1:22-1:22 expected closing paren
+#let array = (1, 2 + ;Fourth
+[eq array, (1, 2)]
+
+// Not terminated.
+// Error: 1:14-1:20 expected semicolon or line break, found identifier
+#let v = "a" Unseen Fifth
+[eq v, "a"]
+
+// Not terminated by semicolon in template.
+#let v = [Hello; there]
-// Error: 4:1-4:3 unexpected identifier
-// Error: 3:4-3:9 unexpected identifier
-// Error: 3:1-3:1 expected semicolon
-#let x = ""
-Hi there
+// Not terminated by line break due to parens.
+#let x = (
+ 1,
+ 2,
+ 3,
+)
+[eq x, (1, 2, 3)]
diff --git a/tests/lang/typ/linebreaks.typ b/tests/lang/typ/linebreaks.typ
new file mode 100644
index 00000000..ee2f453a
--- /dev/null
+++ b/tests/lang/typ/linebreaks.typ
@@ -0,0 +1,19 @@
+// Leading line break.
+\ Leading
+
+// Directly after word.
+Line\ Break
+
+// Spaces around.
+Line \ Break
+
+// Directly before word does not work.
+No \Break
+
+// Trailing before paragraph break.
+Paragraph 1 \
+
+Paragraph 2
+
+// Trailing before end of document.
+Paragraph 3 \
diff --git a/tests/lang/typ/nbsp.typ b/tests/lang/typ/nbsp.typ
new file mode 100644
index 00000000..b2f09950
--- /dev/null
+++ b/tests/lang/typ/nbsp.typ
@@ -0,0 +1,2 @@
+// Parsed correctly, but not actually doing anything at the moment.
+The non-breaking~space does not work.
diff --git a/tests/lang/typ/raw.typ b/tests/lang/typ/raw.typ
index 22eda139..2c0890ba 100644
--- a/tests/lang/typ/raw.typ
+++ b/tests/lang/typ/raw.typ
@@ -1,23 +1,28 @@
-The keyword ``rust let``.
+[font 8pt]
+
+// Typst syntax inside.
+`#let x = 1``[f 1]`
-`#let x = 1`
-`[f 1]`
+// Space between "rust" and "let" is trimmed.
+The keyword ``rust let``.
----
-[font 6pt]
+// Trimming depends on number backticks.
+<` untrimmed `> \
+<`` trimmed ``>
+// Multiline trimming.
``py
import this
def say_hi():
- print("Hello World!")
+ print("Hi!")
``
----
+// Lots of backticks inside.
````
```backticks```
````
----
+// Unterminated.
// Error: 2:1-2:1 expected backtick(s)
`endless
diff --git a/tests/lang/typ/text.typ b/tests/lang/typ/text.typ
new file mode 100644
index 00000000..9c1e7977
--- /dev/null
+++ b/tests/lang/typ/text.typ
@@ -0,0 +1 @@
+Hello 🌏!
diff --git a/tests/lang/typ/values.typ b/tests/lang/typ/values.typ
index ce41fc43..51d0bd17 100644
--- a/tests/lang/typ/values.typ
+++ b/tests/lang/typ/values.typ
@@ -1,13 +1,24 @@
+// Test representation of values in the document.
+
#let name = "Typst";
#let ke-bab = "Kebab!";
#let α = "Alpha";
+// Variables.
{name} \
{ke-bab} \
{α} \
+
+// Error: 1:1-1:4 unknown variable
+{_}
+
+// Literal values.
{none} (empty) \
{true} \
{false} \
+
+// Numerical values.
+{1} \
{1.0e-4} \
{3.15} \
{1e-10} \
@@ -17,17 +28,16 @@
{12e1pt} \
{2.5rad} \
{45deg} \
-{"hi"} \
-{"a\n[]\"\u{1F680}string"} \
-{#f7a20500} \
-{[*{"Hi"} [f 1]*]} \
-{{1}}
-// Error: 1:1-1:4 unknown variable
-{_} \
+// Colors.
+{#f7a20500} \
// Error: 1:2-1:5 invalid color
{#a5}
-// Error: 1:2-1:4 expected expression, found invalid token
-{1u}
+// Strings and escaping.
+{"hi"} \
+{"a\n[]\"\u{1F680}string"} \
+
+// Templates.
+{[*{"Hi"} [f 1]*]}
diff --git a/tests/typeset.rs b/tests/typeset.rs
index 10e6231e..fd5f4c81 100644
--- a/tests/typeset.rs
+++ b/tests/typeset.rs
@@ -1,7 +1,9 @@
+use std::cell::RefCell;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::path::Path;
+use std::rc::Rc;
use fontdock::fs::FsIndex;
use image::{GenericImageView, Rgba};
@@ -180,7 +182,8 @@ fn test_part(i: usize, src: &str, env: &mut Env) -> (bool, Vec<Frame>) {
let (compare_ref, ref_diags) = parse_metadata(src, &map);
let mut scope = library::new();
- register_helpers(&mut scope);
+ let panicked = Rc::new(RefCell::new(false));
+ register_helpers(&mut scope, Rc::clone(&panicked));
// We want to have "unbounded" pages, so we allow them to be infinitely
// large and fit them to match their content.
@@ -201,6 +204,10 @@ fn test_part(i: usize, src: &str, env: &mut Env) -> (bool, Vec<Frame>) {
diags.sort_by_key(|d| d.span);
let mut ok = true;
+ if *panicked.borrow() {
+ ok = false;
+ }
+
if diags != ref_diags {
println!(" Subtest {} does not match expected diagnostics. ❌", i);
ok = false;
@@ -259,7 +266,7 @@ fn parse_metadata(src: &str, map: &LineMap) -> (bool, SpanVec<Diag>) {
(compare_ref, diags)
}
-fn register_helpers(scope: &mut Scope) {
+fn register_helpers(scope: &mut Scope, panicked: Rc<RefCell<bool>>) {
pub fn f(_: &mut EvalContext, args: &mut Args) -> Value {
let (array, dict) = args.drain();
let iter = array
@@ -281,7 +288,22 @@ fn register_helpers(scope: &mut Scope) {
Value::Str(p.finish())
}
+ let eq = move |ctx: &mut EvalContext, args: &mut Args| -> Value {
+ let lhs = args.require::<Value>(ctx, "left-hand side");
+ let rhs = args.require::<Value>(ctx, "right-hand side");
+ if lhs != rhs {
+ *panicked.borrow_mut() = true;
+ println!(" Assertion failed ❌");
+ println!(" left: {:?}", lhs);
+ println!(" right: {:?}", rhs);
+ Value::Str(format!("(panic)"))
+ } else {
+ Value::None
+ }
+ };
+
scope.set("f", ValueFunc::new("f", f));
+ scope.set("eq", ValueFunc::new("eq", eq));
}
fn print_diag(diag: &Spanned<Diag>, map: &LineMap) {