While loops 🔁
@ -118,6 +118,7 @@ impl Eval for Expr {
|
||||
Self::Binary(v) => v.eval(ctx),
|
||||
Self::Let(v) => v.eval(ctx),
|
||||
Self::If(v) => v.eval(ctx),
|
||||
Self::While(v) => v.eval(ctx),
|
||||
Self::For(v) => v.eval(ctx),
|
||||
}
|
||||
}
|
||||
@ -403,24 +404,56 @@ impl Eval for ExprIf {
|
||||
|
||||
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
|
||||
let condition = self.condition.eval(ctx);
|
||||
|
||||
if let Value::Bool(boolean) = condition {
|
||||
return if boolean {
|
||||
if let Value::Bool(condition) = condition {
|
||||
if condition {
|
||||
self.if_body.eval(ctx)
|
||||
} else if let Some(expr) = &self.else_body {
|
||||
expr.eval(ctx)
|
||||
} else {
|
||||
Value::None
|
||||
};
|
||||
} else if condition != Value::Error {
|
||||
ctx.diag(error!(
|
||||
self.condition.span(),
|
||||
"expected boolean, found {}",
|
||||
condition.type_name(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if condition != Value::Error {
|
||||
ctx.diag(error!(
|
||||
self.condition.span(),
|
||||
"expected boolean, found {}",
|
||||
condition.type_name(),
|
||||
));
|
||||
}
|
||||
Value::Error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value::Error
|
||||
impl Eval for ExprWhile {
|
||||
type Output = Value;
|
||||
|
||||
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
|
||||
let mut output = vec![];
|
||||
loop {
|
||||
let condition = self.condition.eval(ctx);
|
||||
if let Value::Bool(condition) = condition {
|
||||
if condition {
|
||||
match self.body.eval(ctx) {
|
||||
Value::Template(v) => output.extend(v),
|
||||
Value::Str(v) => output.push(TemplateNode::Str(v)),
|
||||
Value::Error => return Value::Error,
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
return Value::Template(output);
|
||||
}
|
||||
} else {
|
||||
if condition != Value::Error {
|
||||
ctx.diag(error!(
|
||||
self.condition.span(),
|
||||
"expected boolean, found {}",
|
||||
condition.type_name(),
|
||||
));
|
||||
}
|
||||
return Value::Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -438,7 +471,8 @@ impl Eval for ExprFor {
|
||||
$(ctx.scopes.def_mut($binding.as_str(), $value);)*
|
||||
|
||||
match self.body.eval(ctx) {
|
||||
Value::Template(new) => output.extend(new),
|
||||
Value::Template(v) => output.extend(v),
|
||||
Value::Str(v) => output.push(TemplateNode::Str(v)),
|
||||
Value::Error => {
|
||||
ctx.scopes.pop();
|
||||
return Value::Error;
|
||||
|
@ -586,7 +586,11 @@ primitive! { Color: "color", Value::Color }
|
||||
primitive! { String: "string", Value::Str }
|
||||
primitive! { ValueArray: "array", Value::Array }
|
||||
primitive! { ValueDict: "dictionary", Value::Dict }
|
||||
primitive! { ValueTemplate: "template", Value::Template }
|
||||
primitive! {
|
||||
ValueTemplate: "template",
|
||||
Value::Template,
|
||||
Value::Str(v) => vec![TemplateNode::Str(v)],
|
||||
}
|
||||
primitive! { ValueFunc: "function", Value::Func }
|
||||
primitive! { ValueArgs: "arguments", Value::Args }
|
||||
|
||||
|
@ -71,7 +71,7 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
|
||||
Token::UnicodeEscape(t) => Node::Text(unicode_escape(p, t)),
|
||||
|
||||
// Hashtag + keyword / identifier.
|
||||
Token::Ident(_) | Token::Let | Token::If | Token::For => {
|
||||
Token::Ident(_) | Token::Let | Token::If | Token::While | Token::For => {
|
||||
*at_start = false;
|
||||
let stmt = token == Token::Let;
|
||||
let group = if stmt { Group::Stmt } else { Group::Expr };
|
||||
@ -191,6 +191,7 @@ fn primary(p: &mut Parser) -> Option<Expr> {
|
||||
// Keywords.
|
||||
Some(Token::Let) => expr_let(p),
|
||||
Some(Token::If) => expr_if(p),
|
||||
Some(Token::While) => expr_while(p),
|
||||
Some(Token::For) => expr_for(p),
|
||||
|
||||
// Structures.
|
||||
@ -382,6 +383,25 @@ fn expr_if(p: &mut Parser) -> Option<Expr> {
|
||||
expr_if
|
||||
}
|
||||
|
||||
/// Parse a while expresion.
|
||||
fn expr_while(p: &mut Parser) -> Option<Expr> {
|
||||
let start = p.start();
|
||||
p.assert(Token::While);
|
||||
|
||||
let mut expr_while = None;
|
||||
if let Some(condition) = expr(p) {
|
||||
if let Some(body) = body(p) {
|
||||
expr_while = Some(Expr::While(ExprWhile {
|
||||
span: p.span(start),
|
||||
condition: Box::new(condition),
|
||||
body: Box::new(body),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
expr_while
|
||||
}
|
||||
|
||||
/// Parse a for expression.
|
||||
fn expr_for(p: &mut Parser) -> Option<Expr> {
|
||||
let start = p.start();
|
||||
|
@ -221,6 +221,7 @@ impl Pretty for Expr {
|
||||
Self::Call(v) => v.pretty(p),
|
||||
Self::Let(v) => v.pretty(p),
|
||||
Self::If(v) => v.pretty(p),
|
||||
Self::While(v) => v.pretty(p),
|
||||
Self::For(v) => v.pretty(p),
|
||||
}
|
||||
}
|
||||
@ -413,6 +414,15 @@ impl Pretty for ExprIf {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pretty for ExprWhile {
|
||||
fn pretty(&self, p: &mut Printer) {
|
||||
p.push_str("while ");
|
||||
self.condition.pretty(p);
|
||||
p.push(' ');
|
||||
self.body.pretty(p);
|
||||
}
|
||||
}
|
||||
|
||||
impl Pretty for ExprFor {
|
||||
fn pretty(&self, p: &mut Printer) {
|
||||
p.push_str("for ");
|
||||
@ -718,9 +728,10 @@ mod tests {
|
||||
|
||||
// Keywords.
|
||||
roundtrip("#let x = 1 + 2");
|
||||
test_parse("#if x [y] #else [z]", "#if x [y] else [z]");
|
||||
roundtrip("#while x {y}");
|
||||
roundtrip("#for x in y {z}");
|
||||
roundtrip("#for k, x in y {z}");
|
||||
test_parse("#if x [y] #else [z]", "#if x [y] else [z]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -19,19 +19,21 @@ pub enum Expr {
|
||||
Template(ExprTemplate),
|
||||
/// A grouped expression: `(1 + 2)`.
|
||||
Group(ExprGroup),
|
||||
/// A block expression: `{ #let x = 1; x + 2 }`.
|
||||
/// A block expression: `{ let x = 1; x + 2 }`.
|
||||
Block(ExprBlock),
|
||||
/// A unary operation: `-x`.
|
||||
Unary(ExprUnary),
|
||||
/// A binary operation: `a + b`.
|
||||
Binary(ExprBinary),
|
||||
/// An invocation of a function: `foo(...)`, `#[foo ...]`.
|
||||
/// An invocation of a function: `foo(...)`.
|
||||
Call(ExprCall),
|
||||
/// A let expression: `#let x = 1`.
|
||||
/// A let expression: `let x = 1`.
|
||||
Let(ExprLet),
|
||||
/// An if expression: `#if x { y } #else { z }`.
|
||||
/// An if expression: `if x { y } else { z }`.
|
||||
If(ExprIf),
|
||||
/// A for expression: `#for x #in y { z }`.
|
||||
/// A while expression: `while x { y }`.
|
||||
While(ExprWhile),
|
||||
/// A for expression: `for x in y { z }`.
|
||||
For(ExprFor),
|
||||
}
|
||||
|
||||
@ -51,6 +53,7 @@ impl Expr {
|
||||
Self::Call(v) => v.span,
|
||||
Self::Let(v) => v.span,
|
||||
Self::If(v) => v.span,
|
||||
Self::While(v) => v.span,
|
||||
Self::For(v) => v.span,
|
||||
}
|
||||
}
|
||||
@ -62,6 +65,7 @@ impl Expr {
|
||||
| Expr::Call(_)
|
||||
| Expr::Let(_)
|
||||
| Expr::If(_)
|
||||
| Expr::While(_)
|
||||
| Expr::For(_)
|
||||
)
|
||||
}
|
||||
@ -154,7 +158,7 @@ pub struct ExprGroup {
|
||||
pub expr: Box<Expr>,
|
||||
}
|
||||
|
||||
/// A block expression: `{ #let x = 1; x + 2 }`.
|
||||
/// A block expression: `{ let x = 1; x + 2 }`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprBlock {
|
||||
/// The source code location.
|
||||
@ -365,7 +369,7 @@ pub enum Associativity {
|
||||
Right,
|
||||
}
|
||||
|
||||
/// An invocation of a function: `foo(...)`, `#[foo ...]`.
|
||||
/// An invocation of a function: `foo(...)`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprCall {
|
||||
/// The source code location.
|
||||
@ -407,7 +411,7 @@ impl ExprArg {
|
||||
}
|
||||
}
|
||||
|
||||
/// A let expression: `#let x = 1`.
|
||||
/// A let expression: `let x = 1`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprLet {
|
||||
/// The source code location.
|
||||
@ -418,7 +422,7 @@ pub struct ExprLet {
|
||||
pub init: Option<Box<Expr>>,
|
||||
}
|
||||
|
||||
/// An if expression: `#if x { y } #else { z }`.
|
||||
/// An if expression: `if x { y } else { z }`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprIf {
|
||||
/// The source code location.
|
||||
@ -431,7 +435,18 @@ pub struct ExprIf {
|
||||
pub else_body: Option<Box<Expr>>,
|
||||
}
|
||||
|
||||
/// A for expression: `#for x #in y { z }`.
|
||||
/// A while expression: `while x { y }`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprWhile {
|
||||
/// The source code location.
|
||||
pub span: Span,
|
||||
/// The condition which selects whether to evaluate the body.
|
||||
pub condition: Box<Expr>,
|
||||
/// The expression to evaluate while the condition is true.
|
||||
pub body: Box<Expr>,
|
||||
}
|
||||
|
||||
/// A for expression: `for x in y { z }`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprFor {
|
||||
/// The source code location.
|
||||
@ -447,9 +462,9 @@ pub struct ExprFor {
|
||||
/// A pattern in a for loop.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ForPattern {
|
||||
/// A value pattern: `#for v #in array`.
|
||||
/// A value pattern: `for v in array`.
|
||||
Value(Ident),
|
||||
/// A key-value pattern: `#for k, v #in dict`.
|
||||
/// A key-value pattern: `for k, v in dict`.
|
||||
KeyValue(Ident, Ident),
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,7 @@ visit! {
|
||||
Expr::Call(e) => v.visit_call(e),
|
||||
Expr::Let(e) => v.visit_let(e),
|
||||
Expr::If(e) => v.visit_if(e),
|
||||
Expr::While(e) => v.visit_while(e),
|
||||
Expr::For(e) => v.visit_for(e),
|
||||
}
|
||||
}
|
||||
@ -132,6 +133,11 @@ visit! {
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_while(v, node: &ExprWhile) {
|
||||
v.visit_expr(&node.condition);
|
||||
v.visit_expr(&node.body);
|
||||
}
|
||||
|
||||
fn visit_for(v, node: &ExprFor) {
|
||||
v.visit_expr(&node.iter);
|
||||
v.visit_expr(&node.body);
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.6 KiB |
BIN
tests/ref/control/invalid.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 364 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
tests/ref/control/while.png
Normal file
After Width: | Height: | Size: 838 B |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 5.0 KiB |
@ -1,32 +0,0 @@
|
||||
// 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 {}
|
@ -1,20 +0,0 @@
|
||||
// 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])
|
||||
} + "]"
|
||||
}
|
@ -1,11 +1,10 @@
|
||||
// Test for loops.
|
||||
// Ref: false
|
||||
|
||||
---
|
||||
// Empty array.
|
||||
#for x in () [Nope]
|
||||
|
||||
// Array.
|
||||
|
||||
#for x in () {}
|
||||
|
||||
#let sum = 0
|
||||
#for x in (1, 2, 3, 4, 5) {
|
||||
sum += x
|
||||
@ -13,14 +12,12 @@
|
||||
|
||||
#test(sum, 15)
|
||||
|
||||
---
|
||||
// Dictionary.
|
||||
// Ref: true
|
||||
(\ #for k, v in (name: "Typst", age: 2) [
|
||||
#h(0.5cm) {k}: {v}, \
|
||||
])
|
||||
// Dictionary is not traversed in insertion order.
|
||||
// Should output `age: 1, name: Typst,`.
|
||||
#for k, v in (name: "Typst", age: 2) [
|
||||
{k}: {v}, \
|
||||
]
|
||||
|
||||
---
|
||||
// String.
|
||||
{
|
||||
let out = ""
|
||||
@ -36,6 +33,33 @@
|
||||
}
|
||||
|
||||
---
|
||||
// Block body.
|
||||
// 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])
|
||||
} + "]"
|
||||
}
|
||||
|
||||
// Template body.
|
||||
// Should output `234`.
|
||||
#for v in (1, 2, 3, 4, 5, 6, 7) [#if v >= 2 and v <= 5 { repr(v) }]
|
||||
|
||||
---
|
||||
// Value of for loops.
|
||||
// Ref: false
|
||||
#test(type(for v in () {}), "template")
|
||||
#test(type(for v in () []), "template")
|
||||
|
||||
---
|
||||
// Error: 14-19 unknown variable
|
||||
#let error = error
|
||||
|
||||
// Uniterable expression.
|
||||
// Error: 11-15 cannot loop over boolean
|
||||
#for v in true {}
|
||||
@ -44,9 +68,7 @@
|
||||
// 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) {
|
||||
// A single error stops iteration.
|
||||
#test(error, for v in (1, 2, 3) {
|
||||
if v < 2 [Ok] else {error}
|
||||
}
|
||||
#test(result, error)
|
||||
})
|
||||
|
@ -1,28 +0,0 @@
|
||||
// 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
|
@ -1,21 +0,0 @@
|
||||
// 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)
|
||||
}
|
@ -3,35 +3,61 @@
|
||||
---
|
||||
// Test condition evaluation.
|
||||
#if 1 < 2 [
|
||||
Ok.
|
||||
One.
|
||||
]
|
||||
|
||||
#if true == false [
|
||||
Bad, but we {dont-care}!
|
||||
{Bad}, but we {dont-care}!
|
||||
]
|
||||
|
||||
---
|
||||
// Brace in condition.
|
||||
// Braced condition.
|
||||
#if {true} [
|
||||
Ok.
|
||||
One.
|
||||
]
|
||||
|
||||
// Template in condition.
|
||||
#if [] != none [
|
||||
Two.
|
||||
]
|
||||
|
||||
// Multi-line condition with parens.
|
||||
#if (
|
||||
1 + 1
|
||||
== 1
|
||||
) {
|
||||
nope
|
||||
} #else {
|
||||
"Ok."
|
||||
) [
|
||||
Nope.
|
||||
] #else {
|
||||
"Three."
|
||||
}
|
||||
|
||||
// Multiline.
|
||||
#if false [
|
||||
Bad.
|
||||
] #else {
|
||||
let pt = "."
|
||||
"Ok" + pt
|
||||
let point = "."
|
||||
"Four" + point
|
||||
}
|
||||
|
||||
---
|
||||
// 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)
|
||||
}
|
||||
|
||||
---
|
||||
|
100
tests/typ/control/invalid.typ
Normal file
@ -0,0 +1,100 @@
|
||||
// Test invalid control syntax.
|
||||
|
||||
---
|
||||
// Error: 5 expected identifier
|
||||
#let
|
||||
|
||||
// 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
|
||||
|
||||
---
|
||||
// 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
|
||||
|
||||
---
|
||||
// Error: 7 expected expression
|
||||
#while
|
||||
|
||||
// Error: 7 expected expression
|
||||
{while}
|
||||
|
||||
// Error: 9 expected body
|
||||
#while x
|
||||
|
||||
// Should output `x`.
|
||||
// Error: 7 expected expression
|
||||
#while
|
||||
x {}
|
||||
|
||||
// Should output `something`.
|
||||
// Error: 9 expected body
|
||||
#while x something
|
||||
|
||||
---
|
||||
// Error: 5 expected identifier
|
||||
#for
|
||||
|
||||
// Error: 5 expected identifier
|
||||
{for}
|
||||
|
||||
// Error: 7 expected keyword `in`
|
||||
#for v
|
||||
|
||||
// Error: 10 expected expression
|
||||
#for v in
|
||||
|
||||
// Error: 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 {}
|
@ -1,20 +0,0 @@
|
||||
// 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
|
@ -1,28 +0,0 @@
|
||||
// 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))
|
@ -1,7 +1,8 @@
|
||||
// Test let bindings.
|
||||
// Ref: false
|
||||
|
||||
---
|
||||
// Ref: false
|
||||
|
||||
// Automatically initialized with none.
|
||||
#let x
|
||||
#test(x, none)
|
||||
@ -9,3 +10,32 @@
|
||||
// Manually initialized with one.
|
||||
#let x = 1
|
||||
#test(x, 1)
|
||||
|
||||
---
|
||||
// Termination.
|
||||
|
||||
// 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))
|
||||
|
46
tests/typ/control/while.typ
Normal file
@ -0,0 +1,46 @@
|
||||
// Test while expressions.
|
||||
|
||||
---
|
||||
// Should output `2 4 6 8 10`.
|
||||
#let i = 0
|
||||
#while i < 10 [
|
||||
{ i += 2 }
|
||||
#i
|
||||
]
|
||||
|
||||
// Should output `Hi`.
|
||||
#let iter = true
|
||||
#while iter {
|
||||
iter = false
|
||||
"Hi."
|
||||
}
|
||||
|
||||
#while false {
|
||||
dont-care
|
||||
}
|
||||
|
||||
---
|
||||
// Value of while loops.
|
||||
// Ref: false
|
||||
#test(type(while false {}), "template")
|
||||
#test(type(while false []), "template")
|
||||
|
||||
---
|
||||
// Error: 14-19 unknown variable
|
||||
#let error = error
|
||||
|
||||
// Condition must be boolean.
|
||||
// Error: 8-14 expected boolean, found template
|
||||
#while [nope] [nope]
|
||||
|
||||
// Make sure that we don't complain twice.
|
||||
// Error: 8-15 unknown variable
|
||||
#while nothing {}
|
||||
|
||||
// A single error stops iteration.
|
||||
#let i = 0
|
||||
#test(error, while i < 10 {
|
||||
i += 1
|
||||
if i < 5 [nope] else { error }
|
||||
})
|
||||
#test(i, 5)
|
@ -19,9 +19,17 @@ A #if true{"B"} C \
|
||||
A#if false [] #else [B]C \
|
||||
A#if true [B] #else [] C \
|
||||
|
||||
---
|
||||
// Spacing around while loop.
|
||||
|
||||
#let c = true; A#while c [{c = false}B]C \
|
||||
#let c = true; A#while c [{c = false}B] C \
|
||||
#let c = true; A #while c { c = false; "B" }C \
|
||||
#let c = true; A #while c { c = false; "B" } C \
|
||||
|
||||
---
|
||||
// Spacing around for loop.
|
||||
|
||||
A#for _ in (none,) [B]C \
|
||||
A#for _ in (none,) [B] C \
|
||||
A #for _ in (none,) [B]C \
|
||||
A #for _ in (none,) {"B"}C \
|
||||
|
@ -92,9 +92,9 @@ function getWebviewContent(pngSrc, refSrc, stdout, stderr) {
|
||||
<style>
|
||||
body, html {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
img {
|
||||
width: 80%;
|
||||
@ -102,7 +102,10 @@ function getWebviewContent(pngSrc, refSrc, stdout, stderr) {
|
||||
object-fit: contain;
|
||||
}
|
||||
pre {
|
||||
display: inline-block;
|
||||
font-family: var(--vscode-editor-font-family);
|
||||
text-align: left;
|
||||
width: 80%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|