New syntax 💎
- Everything everywhere! - Blocks with curly braces: {} - Templates with brackets: [] - Function templates with hashtag: `#[f]` - Headings with equals sign: `= Introduction`
This commit is contained in:
parent
ac24075469
commit
89eb8bae49
@ -8,7 +8,7 @@ use super::{Args, Eval, EvalContext};
|
||||
use crate::color::Color;
|
||||
use crate::geom::{Angle, Length, Linear, Relative};
|
||||
use crate::pretty::{pretty, Pretty, Printer};
|
||||
use crate::syntax::{Spanned, Tree, WithSpan};
|
||||
use crate::syntax::{pretty_template, Spanned, Tree, WithSpan};
|
||||
|
||||
/// A computational value.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@ -121,11 +121,7 @@ impl Pretty for Value {
|
||||
Value::Str(v) => write!(p, "{:?}", v).unwrap(),
|
||||
Value::Array(v) => v.pretty(p),
|
||||
Value::Dict(v) => v.pretty(p),
|
||||
Value::Template(v) => {
|
||||
p.push_str("[");
|
||||
v.pretty(p);
|
||||
p.push_str("]");
|
||||
}
|
||||
Value::Template(v) => pretty_template(v, p),
|
||||
Value::Func(v) => v.pretty(p),
|
||||
Value::Any(v) => v.pretty(p),
|
||||
Value::Error => p.push_str("(error)"),
|
||||
@ -537,8 +533,8 @@ mod tests {
|
||||
// Dictionary.
|
||||
let mut dict = BTreeMap::new();
|
||||
dict.insert("one".into(), Value::Int(1));
|
||||
dict.insert("two".into(), Value::Template(parse("[f]").output));
|
||||
dict.insert("two".into(), Value::Template(parse("#[f]").output));
|
||||
test_pretty(BTreeMap::new(), "(:)");
|
||||
test_pretty(dict, "(one: 1, two: [[f]])");
|
||||
test_pretty(dict, "(one: 1, two: #[f])");
|
||||
}
|
||||
}
|
||||
|
116
src/parse/mod.rs
116
src/parse/mod.rs
@ -45,38 +45,33 @@ fn tree(p: &mut Parser) -> Tree {
|
||||
fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
|
||||
let token = p.peek()?;
|
||||
let node = match token {
|
||||
// Bracket call.
|
||||
Token::LeftBracket => {
|
||||
return Some(Node::Expr(bracket_call(p)?));
|
||||
// Whitespace.
|
||||
Token::Space(newlines) => {
|
||||
*at_start |= newlines > 0;
|
||||
if newlines < 2 { Node::Space } else { Node::Parbreak }
|
||||
}
|
||||
|
||||
// Code block.
|
||||
Token::LeftBrace => {
|
||||
return Some(Node::Expr(block(p, false)?));
|
||||
}
|
||||
// Text.
|
||||
Token::Text(text) => Node::Text(text.into()),
|
||||
|
||||
// Markup.
|
||||
Token::Star => Node::Strong,
|
||||
Token::Underscore => Node::Emph,
|
||||
Token::Tilde => Node::Text("\u{00A0}".into()),
|
||||
Token::Hash => {
|
||||
Token::Eq => {
|
||||
if *at_start {
|
||||
return Some(Node::Heading(heading(p)));
|
||||
} else {
|
||||
Node::Text(p.get(p.peek_span()).into())
|
||||
}
|
||||
}
|
||||
Token::Tilde => Node::Text("\u{00A0}".into()),
|
||||
Token::Backslash => Node::Linebreak,
|
||||
Token::Space(newlines) => {
|
||||
*at_start |= newlines > 0;
|
||||
if newlines < 2 { Node::Space } else { Node::Parbreak }
|
||||
}
|
||||
Token::Text(text) => Node::Text(text.into()),
|
||||
Token::Raw(t) => Node::Raw(raw(p, t)),
|
||||
Token::UnicodeEscape(t) => Node::Text(unicode_escape(p, t)),
|
||||
|
||||
// Keywords.
|
||||
Token::Let | Token::If | Token::For => {
|
||||
*at_start = false;
|
||||
let stmt = token == Token::Let;
|
||||
let group = if stmt { Group::Stmt } else { Group::Expr };
|
||||
|
||||
@ -92,6 +87,24 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
|
||||
return expr.map(Node::Expr);
|
||||
}
|
||||
|
||||
// Block.
|
||||
Token::LeftBrace => {
|
||||
*at_start = false;
|
||||
return Some(Node::Expr(block(p, false)?));
|
||||
}
|
||||
|
||||
// Template.
|
||||
Token::LeftBracket => {
|
||||
*at_start = false;
|
||||
return Some(Node::Expr(template(p)));
|
||||
}
|
||||
|
||||
// Function template.
|
||||
Token::HashBracket => {
|
||||
*at_start = false;
|
||||
return Some(Node::Expr(bracket_call(p)?));
|
||||
}
|
||||
|
||||
// Comments.
|
||||
Token::LineComment(_) | Token::BlockComment(_) => {
|
||||
p.eat();
|
||||
@ -99,6 +112,7 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
|
||||
}
|
||||
|
||||
_ => {
|
||||
*at_start = false;
|
||||
p.unexpected();
|
||||
return None;
|
||||
}
|
||||
@ -109,12 +123,12 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
|
||||
|
||||
/// Parse a heading.
|
||||
fn heading(p: &mut Parser) -> NodeHeading {
|
||||
// Count hashtags.
|
||||
// Count depth.
|
||||
let mut level = p.span(|p| {
|
||||
p.assert(Token::Hash);
|
||||
p.assert(&[Token::Eq]);
|
||||
|
||||
let mut level = 0u8;
|
||||
while p.eat_if(Token::Hash) {
|
||||
while p.eat_if(Token::Eq) {
|
||||
level = level.saturating_add(1);
|
||||
}
|
||||
level
|
||||
@ -278,33 +292,6 @@ fn expr_with(p: &mut Parser, min_prec: usize) -> Option<Expr> {
|
||||
/// Parse a primary expression.
|
||||
fn primary(p: &mut Parser) -> Option<Expr> {
|
||||
let expr = match p.peek() {
|
||||
// Template.
|
||||
Some(Token::LeftBracket) => {
|
||||
return Some(template(p));
|
||||
}
|
||||
|
||||
// Nested block.
|
||||
Some(Token::LeftBrace) => {
|
||||
return block(p, true);
|
||||
}
|
||||
|
||||
// Dictionary or just a parenthesized expression.
|
||||
Some(Token::LeftParen) => {
|
||||
return Some(parenthesized(p));
|
||||
}
|
||||
|
||||
// Function or just ident.
|
||||
Some(Token::Ident(id)) => {
|
||||
p.eat();
|
||||
let ident = Ident(id.into());
|
||||
if p.peek() == Some(Token::LeftParen) {
|
||||
let name = ident.with_span(p.peek_span());
|
||||
return Some(paren_call(p, name));
|
||||
} else {
|
||||
return Some(Expr::Ident(ident));
|
||||
}
|
||||
}
|
||||
|
||||
// Basic values.
|
||||
Some(Token::None) => Expr::None,
|
||||
Some(Token::Bool(b)) => Expr::Bool(b),
|
||||
@ -316,12 +303,45 @@ fn primary(p: &mut Parser) -> Option<Expr> {
|
||||
Some(Token::Color(color)) => Expr::Color(color),
|
||||
Some(Token::Str(token)) => Expr::Str(string(p, token)),
|
||||
|
||||
// Function or identifier.
|
||||
Some(Token::Ident(id)) => {
|
||||
p.eat();
|
||||
let ident = Ident(id.into());
|
||||
if p.peek() == Some(Token::LeftParen) {
|
||||
let name = ident.with_span(p.peek_span());
|
||||
return Some(paren_call(p, name));
|
||||
} else {
|
||||
return Some(Expr::Ident(ident));
|
||||
}
|
||||
}
|
||||
|
||||
// Keywords.
|
||||
Some(Token::Let) => return expr_let(p),
|
||||
Some(Token::If) => return expr_if(p),
|
||||
Some(Token::For) => return expr_for(p),
|
||||
|
||||
// No value.
|
||||
// Block.
|
||||
Some(Token::LeftBrace) => {
|
||||
return block(p, true);
|
||||
}
|
||||
|
||||
// Template.
|
||||
Some(Token::LeftBracket) => {
|
||||
return Some(template(p));
|
||||
}
|
||||
|
||||
// Function template.
|
||||
Some(Token::HashBracket) => {
|
||||
let call = p.span_if(bracket_call)?.map(Node::Expr);
|
||||
return Some(Expr::Template(vec![call]));
|
||||
}
|
||||
|
||||
// Array, dictionary or parenthesized expression.
|
||||
Some(Token::LeftParen) => {
|
||||
return Some(parenthesized(p));
|
||||
}
|
||||
|
||||
// Nothing.
|
||||
_ => {
|
||||
p.expected("expression");
|
||||
return None;
|
||||
@ -380,7 +400,7 @@ fn string(p: &mut Parser, token: TokenStr) -> String {
|
||||
|
||||
/// Parse a let expression.
|
||||
fn expr_let(p: &mut Parser) -> Option<Expr> {
|
||||
p.assert(Token::Let);
|
||||
p.assert(&[Token::Let]);
|
||||
|
||||
let mut expr_let = None;
|
||||
if let Some(pat) = p.span_if(ident) {
|
||||
@ -397,7 +417,7 @@ fn expr_let(p: &mut Parser) -> Option<Expr> {
|
||||
|
||||
/// Parse an if expresion.
|
||||
fn expr_if(p: &mut Parser) -> Option<Expr> {
|
||||
p.assert(Token::If);
|
||||
p.assert(&[Token::If]);
|
||||
|
||||
let mut expr_if = None;
|
||||
if let Some(condition) = p.span_if(expr) {
|
||||
@ -420,7 +440,7 @@ fn expr_if(p: &mut Parser) -> Option<Expr> {
|
||||
|
||||
/// Parse a for expression.
|
||||
fn expr_for(p: &mut Parser) -> Option<Expr> {
|
||||
p.assert(Token::For);
|
||||
p.assert(&[Token::For]);
|
||||
|
||||
let mut expr_for = None;
|
||||
if let Some(pat) = p.span_if(for_pattern) {
|
||||
|
@ -105,9 +105,9 @@ impl<'s> Parser<'s> {
|
||||
self.repeek();
|
||||
|
||||
match group {
|
||||
Group::Paren => self.assert(Token::LeftParen),
|
||||
Group::Bracket => self.assert(Token::LeftBracket),
|
||||
Group::Brace => self.assert(Token::LeftBrace),
|
||||
Group::Paren => self.assert(&[Token::LeftParen]),
|
||||
Group::Bracket => self.assert(&[Token::HashBracket, Token::LeftBracket]),
|
||||
Group::Brace => self.assert(&[Token::LeftBrace]),
|
||||
Group::Subheader => {}
|
||||
Group::Stmt => {}
|
||||
Group::Expr => {}
|
||||
@ -210,10 +210,10 @@ impl<'s> Parser<'s> {
|
||||
eaten
|
||||
}
|
||||
|
||||
/// Consume the next token, debug-asserting that it is the given one.
|
||||
pub fn assert(&mut self, t: Token) {
|
||||
/// Consume the next token, debug-asserting that it is one of the given ones.
|
||||
pub fn assert(&mut self, ts: &[Token]) {
|
||||
let next = self.eat();
|
||||
debug_assert_eq!(next, Some(t));
|
||||
debug_assert!(next.map_or(false, |n| ts.contains(&n)));
|
||||
}
|
||||
|
||||
/// Skip whitespace and comment tokens.
|
||||
|
@ -67,12 +67,15 @@ impl<'s> Iterator for Tokens<'s> {
|
||||
loop {
|
||||
// Common elements.
|
||||
return Some(match c {
|
||||
// Functions, blocks and terminators.
|
||||
// Blocks and templates.
|
||||
'[' => Token::LeftBracket,
|
||||
']' => Token::RightBracket,
|
||||
'{' => Token::LeftBrace,
|
||||
'}' => Token::RightBrace,
|
||||
|
||||
// Keywords, function templates, colors.
|
||||
'#' => self.hash(start),
|
||||
|
||||
// Whitespace.
|
||||
c if c.is_whitespace() => self.whitespace(c),
|
||||
|
||||
@ -90,8 +93,8 @@ impl<'s> Iterator for Tokens<'s> {
|
||||
// Markup.
|
||||
'*' => Token::Star,
|
||||
'_' => Token::Underscore,
|
||||
'=' => Token::Eq,
|
||||
'~' => Token::Tilde,
|
||||
'#' => self.hash(start),
|
||||
'`' => self.raw(),
|
||||
'$' => self.math(),
|
||||
'\\' => self.backslash(),
|
||||
@ -140,8 +143,7 @@ impl<'s> Iterator for Tokens<'s> {
|
||||
self.number(start, c)
|
||||
}
|
||||
|
||||
// Hex values and strings.
|
||||
'#' => self.hex(start),
|
||||
// Strings.
|
||||
'"' => self.string(),
|
||||
|
||||
_ => Token::Invalid(self.s.eaten_from(start)),
|
||||
@ -151,6 +153,27 @@ impl<'s> Iterator for Tokens<'s> {
|
||||
}
|
||||
|
||||
impl<'s> Tokens<'s> {
|
||||
fn hash(&mut self, start: usize) -> Token<'s> {
|
||||
if self.s.eat_if('[') {
|
||||
return Token::HashBracket;
|
||||
}
|
||||
|
||||
self.s.eat_while(is_id_continue);
|
||||
let read = self.s.eaten_from(start);
|
||||
|
||||
if let Some(keyword) = keyword(read) {
|
||||
return keyword;
|
||||
}
|
||||
|
||||
if self.mode == TokenMode::Code {
|
||||
if let Ok(color) = RgbaColor::from_str(read) {
|
||||
return Token::Color(color);
|
||||
}
|
||||
}
|
||||
|
||||
Token::Invalid(read)
|
||||
}
|
||||
|
||||
fn whitespace(&mut self, first: char) -> Token<'s> {
|
||||
// Fast path for just a single space
|
||||
if first == ' ' && !self.s.check(|c| c.is_whitespace()) {
|
||||
@ -182,10 +205,10 @@ impl<'s> Tokens<'s> {
|
||||
c if c.is_whitespace() => true,
|
||||
// Comments.
|
||||
'/' if self.s.check(|c| c == '/' || c == '*') => true,
|
||||
// Parenthesis.
|
||||
'[' | ']' | '{' | '}' => true,
|
||||
// Parenthesis and hashtag.
|
||||
'[' | ']' | '{' | '}' | '#' => true,
|
||||
// Markup.
|
||||
'*' | '_' | '#' | '~' | '`' | '$' => true,
|
||||
'*' | '_' | '=' | '~' | '`' | '$' => true,
|
||||
// Escaping.
|
||||
'\\' => true,
|
||||
_ => false,
|
||||
@ -198,21 +221,6 @@ impl<'s> Tokens<'s> {
|
||||
Token::Text(self.s.eaten_from(start))
|
||||
}
|
||||
|
||||
fn hash(&mut self, start: usize) -> Token<'s> {
|
||||
if self.s.check(is_id_start) {
|
||||
self.s.eat();
|
||||
self.s.eat_while(is_id_continue);
|
||||
let read = self.s.eaten_from(start);
|
||||
if let Some(keyword) = keyword(read) {
|
||||
keyword
|
||||
} else {
|
||||
Token::Invalid(read)
|
||||
}
|
||||
} else {
|
||||
Token::Hash
|
||||
}
|
||||
}
|
||||
|
||||
fn raw(&mut self) -> Token<'s> {
|
||||
let mut backticks = 1;
|
||||
while self.s.eat_if('`') {
|
||||
@ -276,10 +284,10 @@ impl<'s> Tokens<'s> {
|
||||
match c {
|
||||
// Backslash and comments.
|
||||
'\\' | '/' |
|
||||
// Parenthesis.
|
||||
'[' | ']' | '{' | '}' |
|
||||
// Parenthesis and hashtag.
|
||||
'[' | ']' | '{' | '}' | '#' |
|
||||
// Markup.
|
||||
'*' | '_' | '#' | '~' | '`' | '$' => {
|
||||
'*' | '_' | '=' | '~' | '`' | '$' => {
|
||||
let start = self.s.index();
|
||||
self.s.eat_assert(c);
|
||||
Token::Text(&self.s.eaten_from(start))
|
||||
@ -367,18 +375,6 @@ impl<'s> Tokens<'s> {
|
||||
}
|
||||
}
|
||||
|
||||
fn hex(&mut self, start: usize) -> Token<'s> {
|
||||
self.s.eat_while(is_id_continue);
|
||||
let read = self.s.eaten_from(start);
|
||||
if let Some(keyword) = keyword(read) {
|
||||
keyword
|
||||
} else if let Ok(color) = RgbaColor::from_str(read) {
|
||||
Token::Color(color)
|
||||
} else {
|
||||
Token::Invalid(read)
|
||||
}
|
||||
}
|
||||
|
||||
fn string(&mut self) -> Token<'s> {
|
||||
let mut escaped = false;
|
||||
Token::Str(TokenStr {
|
||||
@ -596,8 +592,8 @@ mod tests {
|
||||
// Test markup tokens.
|
||||
t!(Markup[" a1"]: "*" => Star);
|
||||
t!(Markup: "_" => Underscore);
|
||||
t!(Markup[""]: "###" => Hash, Hash, Hash);
|
||||
t!(Markup["a1/"]: "# " => Hash, Space(0));
|
||||
t!(Markup[""]: "===" => Eq, Eq, Eq);
|
||||
t!(Markup["a1/"]: "= " => Eq, Space(0));
|
||||
t!(Markup: "~" => Tilde);
|
||||
t!(Markup[" "]: r"\" => Backslash);
|
||||
}
|
||||
@ -655,10 +651,9 @@ mod tests {
|
||||
];
|
||||
|
||||
for &(s, t) in &both {
|
||||
t!(Code[" "]: format!("#{}", s) => t);
|
||||
t!(Markup[" "]: format!("#{}", s) => t);
|
||||
t!(Markup[" "]: format!("#{0}#{0}", s) => t, t);
|
||||
t!(Markup[" /"]: format!("# {}", s) => Hash, Space(0), Text(s));
|
||||
t!(Both[" "]: format!("#{}", s) => t);
|
||||
t!(Both[" "]: format!("#{0}#{0}", s) => t, t);
|
||||
t!(Markup[" /"]: format!("# {}", s) => Token::Invalid("#"), Space(0), Text(s));
|
||||
}
|
||||
|
||||
let code = [
|
||||
@ -713,7 +708,7 @@ mod tests {
|
||||
|
||||
// Test code symbols in text.
|
||||
t!(Markup[" /"]: "a():\"b" => Text("a():\"b"));
|
||||
t!(Markup[" /"]: ";:,=|/+-" => Text(";:,=|/+-"));
|
||||
t!(Markup[" /"]: ";:,|/+-" => Text(";:,|/+-"));
|
||||
|
||||
// Test text ends.
|
||||
t!(Markup[""]: "hello " => Text("hello"), Space(0));
|
||||
@ -765,17 +760,17 @@ mod tests {
|
||||
t!(Markup: r"\}" => Text("}"));
|
||||
t!(Markup: r"\*" => Text("*"));
|
||||
t!(Markup: r"\_" => Text("_"));
|
||||
t!(Markup: r"\#" => Text("#"));
|
||||
t!(Markup: r"\=" => Text("="));
|
||||
t!(Markup: r"\~" => Text("~"));
|
||||
t!(Markup: r"\`" => Text("`"));
|
||||
t!(Markup: r"\$" => Text("$"));
|
||||
t!(Markup: r"\#" => Text("#"));
|
||||
|
||||
// Test unescapable symbols.
|
||||
t!(Markup[" /"]: r"\a" => Text(r"\"), Text("a"));
|
||||
t!(Markup[" /"]: r"\u" => Text(r"\"), Text("u"));
|
||||
t!(Markup[" /"]: r"\1" => Text(r"\"), Text("1"));
|
||||
t!(Markup[" /"]: r"\:" => Text(r"\"), Text(":"));
|
||||
t!(Markup[" /"]: r"\=" => Text(r"\"), Text("="));
|
||||
t!(Markup[" /"]: r#"\""# => Text(r"\"), Text("\""));
|
||||
|
||||
// Test basic unicode escapes.
|
||||
@ -947,7 +942,7 @@ mod tests {
|
||||
t!(Code: "1%%" => Percent(1.0), Invalid("%"));
|
||||
|
||||
// Test invalid keyword.
|
||||
t!(Markup[" /"]: "#-" => Hash, Text("-"));
|
||||
t!(Markup[" /"]: "#-" => Invalid("#-"));
|
||||
t!(Markup[" /"]: "#do" => Invalid("#do"));
|
||||
t!(Code[" /"]: r"#letter" => Invalid(r"#letter"));
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ pub enum Expr {
|
||||
Unary(ExprUnary),
|
||||
/// A binary operation: `a + b`.
|
||||
Binary(ExprBinary),
|
||||
/// An invocation of a function: `foo(...)`, `[foo ...]`.
|
||||
/// An invocation of a function: `foo(...)`, `#[foo ...]`.
|
||||
Call(ExprCall),
|
||||
/// A let expression: `#let x = 1`.
|
||||
Let(ExprLet),
|
||||
@ -75,11 +75,7 @@ impl Pretty for Expr {
|
||||
Self::Str(v) => write!(p, "{:?}", &v).unwrap(),
|
||||
Self::Array(v) => v.pretty(p),
|
||||
Self::Dict(v) => v.pretty(p),
|
||||
Self::Template(v) => {
|
||||
p.push_str("[");
|
||||
v.pretty(p);
|
||||
p.push_str("]");
|
||||
}
|
||||
Self::Template(v) => pretty_template(v, p),
|
||||
Self::Group(v) => {
|
||||
p.push_str("(");
|
||||
v.v.pretty(p);
|
||||
@ -146,6 +142,17 @@ impl Pretty for Named {
|
||||
/// A template expression: `[*Hi* there!]`.
|
||||
pub type ExprTemplate = Tree;
|
||||
|
||||
/// Pretty print a template.
|
||||
pub fn pretty_template(template: &ExprTemplate, p: &mut Printer) {
|
||||
if let [Spanned { v: Node::Expr(Expr::Call(call)), .. }] = template.as_slice() {
|
||||
pretty_func_template(call, p, false)
|
||||
} else {
|
||||
p.push_str("[");
|
||||
template.pretty(p);
|
||||
p.push_str("]");
|
||||
}
|
||||
}
|
||||
|
||||
/// A grouped expression: `(1 + 2)`.
|
||||
pub type ExprGroup = SpanBox<Expr>;
|
||||
|
||||
@ -400,7 +407,7 @@ pub enum Associativity {
|
||||
Right,
|
||||
}
|
||||
|
||||
/// An invocation of a function: `foo(...)`, `[foo ...]`.
|
||||
/// An invocation of a function: `foo(...)`, `#[foo ...]`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprCall {
|
||||
/// The callee of the function.
|
||||
@ -418,12 +425,12 @@ impl Pretty for ExprCall {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pretty print a bracketed function call, with body or chaining when possible.
|
||||
pub fn pretty_bracket_call(call: &ExprCall, p: &mut Printer, chained: bool) {
|
||||
/// Pretty print a function template, with body or chaining when possible.
|
||||
pub fn pretty_func_template(call: &ExprCall, p: &mut Printer, chained: bool) {
|
||||
if chained {
|
||||
p.push_str(" | ");
|
||||
} else {
|
||||
p.push_str("[");
|
||||
p.push_str("#[");
|
||||
}
|
||||
|
||||
// Function name.
|
||||
@ -431,7 +438,7 @@ pub fn pretty_bracket_call(call: &ExprCall, p: &mut Printer, chained: bool) {
|
||||
|
||||
// Find out whether this can be written with a body or as a chain.
|
||||
//
|
||||
// Example: Transforms "[v [Hi]]" => "[v][Hi]".
|
||||
// Example: Transforms "#[v [Hi]]" => "#[v][Hi]".
|
||||
if let [head @ .., Argument::Pos(Spanned { v: Expr::Template(template), .. })] =
|
||||
call.args.v.as_slice()
|
||||
{
|
||||
@ -443,9 +450,9 @@ pub fn pretty_bracket_call(call: &ExprCall, p: &mut Printer, chained: bool) {
|
||||
|
||||
// Find out whether this can written as a chain.
|
||||
//
|
||||
// Example: Transforms "[v][[f]]" => "[v | f]".
|
||||
// Example: Transforms "#[v][[f]]" => "#[v | f]".
|
||||
if let [Spanned { v: Node::Expr(Expr::Call(call)), .. }] = template.as_slice() {
|
||||
return pretty_bracket_call(call, p, true);
|
||||
return pretty_func_template(call, p, true);
|
||||
} else {
|
||||
p.push_str("][");
|
||||
template.pretty(p);
|
||||
|
@ -60,7 +60,7 @@ mod tests {
|
||||
roundtrip("hi");
|
||||
|
||||
// Heading.
|
||||
roundtrip("# *Ok*");
|
||||
roundtrip("= *Ok*");
|
||||
|
||||
// Raw.
|
||||
roundtrip("`lang 1`");
|
||||
@ -94,9 +94,9 @@ mod tests {
|
||||
roundtrip("{(a: 1, b: 2)}");
|
||||
|
||||
// Templates.
|
||||
roundtrip("{[]}");
|
||||
roundtrip("{[*Ok*]}");
|
||||
roundtrip("{[[f]]}");
|
||||
roundtrip("[]");
|
||||
roundtrip("[*Ok*]");
|
||||
roundtrip("{[f]}");
|
||||
|
||||
// Groups.
|
||||
roundtrip("{(1)}");
|
||||
@ -105,6 +105,7 @@ mod tests {
|
||||
roundtrip("{}");
|
||||
roundtrip("{1}");
|
||||
roundtrip("{ #let x = 1; x += 2; x + 1 }");
|
||||
roundtrip("[{}]");
|
||||
|
||||
// Operators.
|
||||
roundtrip("{-x}");
|
||||
@ -116,14 +117,14 @@ mod tests {
|
||||
roundtrip("{v(1)}");
|
||||
roundtrip("{v(a: 1, b)}");
|
||||
|
||||
// Bracket calls.
|
||||
roundtrip("[v]");
|
||||
roundtrip("[v 1]");
|
||||
roundtrip("[v 1, 2][*Ok*]");
|
||||
roundtrip("[v 1 | f 2]");
|
||||
roundtrip("{[[v]]}");
|
||||
test("[v 1, [[f 2]]]", "[v 1 | f 2]");
|
||||
test("[v 1, 2][[f 3]]", "[v 1, 2 | f 3]");
|
||||
// Function templates.
|
||||
roundtrip("#[v]");
|
||||
roundtrip("#[v 1]");
|
||||
roundtrip("#[v 1, 2][*Ok*]");
|
||||
roundtrip("#[v 1 | f 2]");
|
||||
roundtrip("{#[v]}");
|
||||
test("#[v 1, #[f 2]]", "#[v 1 | f 2]");
|
||||
test("#[v 1, 2][#[f 3]]", "#[v 1, 2 | f 3]");
|
||||
|
||||
// Keywords.
|
||||
roundtrip("#let x = 1 + 2");
|
||||
|
@ -36,8 +36,8 @@ impl Pretty for Node {
|
||||
Self::Raw(raw) => raw.pretty(p),
|
||||
Self::Expr(expr) => {
|
||||
if let Expr::Call(call) = expr {
|
||||
// Format bracket calls appropriately.
|
||||
pretty_bracket_call(call, p, false)
|
||||
// Format function templates appropriately.
|
||||
pretty_func_template(call, p, false)
|
||||
} else {
|
||||
expr.pretty(p);
|
||||
}
|
||||
@ -58,7 +58,7 @@ pub struct NodeHeading {
|
||||
impl Pretty for NodeHeading {
|
||||
fn pretty(&self, p: &mut Printer) {
|
||||
for _ in 0 ..= self.level.v {
|
||||
p.push_str("#");
|
||||
p.push_str("=");
|
||||
}
|
||||
self.contents.pretty(p);
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ use crate::geom::{AngularUnit, LengthUnit};
|
||||
pub enum Token<'s> {
|
||||
/// A left square bracket: `[`.
|
||||
LeftBracket,
|
||||
/// A hashtag followed by a left square bracket: `#[`.
|
||||
HashBracket,
|
||||
/// A right square bracket: `]`.
|
||||
RightBracket,
|
||||
/// A left curly brace: `{`.
|
||||
@ -20,8 +22,8 @@ pub enum Token<'s> {
|
||||
Star,
|
||||
/// An underscore: `_`.
|
||||
Underscore,
|
||||
/// A hashtag: `#`.
|
||||
Hash,
|
||||
/// A single equals sign: `=`.
|
||||
Eq,
|
||||
/// A tilde: `~`.
|
||||
Tilde,
|
||||
/// A backslash followed by nothing or whitespace: `\`.
|
||||
@ -40,8 +42,6 @@ pub enum Token<'s> {
|
||||
Hyph,
|
||||
/// A slash: `/`.
|
||||
Slash,
|
||||
/// A single equals sign: `=`.
|
||||
Eq,
|
||||
/// Two equals signs: `==`.
|
||||
EqEq,
|
||||
/// An exclamation mark followed by an equals sign: `!=`.
|
||||
@ -191,6 +191,7 @@ impl<'s> Token<'s> {
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::LeftBracket => "opening bracket",
|
||||
Self::HashBracket => "start of function template",
|
||||
Self::RightBracket => "closing bracket",
|
||||
Self::LeftBrace => "opening brace",
|
||||
Self::RightBrace => "closing brace",
|
||||
@ -198,7 +199,6 @@ impl<'s> Token<'s> {
|
||||
Self::RightParen => "closing paren",
|
||||
Self::Star => "star",
|
||||
Self::Underscore => "underscore",
|
||||
Self::Hash => "hashtag",
|
||||
Self::Tilde => "tilde",
|
||||
Self::Backslash => "backslash",
|
||||
Self::Comma => "comma",
|
||||
|
@ -1,23 +1,23 @@
|
||||
[page width: 450pt, height: 300pt, margins: 1cm]
|
||||
#[page width: 450pt, height: 300pt, margins: 1cm]
|
||||
|
||||
[box][
|
||||
#[box][
|
||||
*Technische Universität Berlin* \
|
||||
*Fakultät II, Institut for Mathematik* \
|
||||
Sekretariat MA \
|
||||
Dr. Max Mustermann \
|
||||
Ola Nordmann, John Doe
|
||||
]
|
||||
[align right | box][*WiSe 2019/2020* \ Woche 3]
|
||||
#[align right | box][*WiSe 2019/2020* \ Woche 3]
|
||||
|
||||
[v 6mm]
|
||||
#[v 6mm]
|
||||
|
||||
[align center][
|
||||
#### 3. Übungsblatt Computerorientierte Mathematik II [v 2mm]
|
||||
*Abgabe: 03.05.2019* (bis 10:10 Uhr in MA 001) [v 2mm]
|
||||
#[align center][
|
||||
==== 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)]
|
||||
*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
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Binary file not shown.
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.2 KiB |
@ -1,25 +1,25 @@
|
||||
// Basic call, whitespace insignificant.
|
||||
[f], [ f ], [
|
||||
#[f], #[ f ], #[
|
||||
f
|
||||
]
|
||||
|
||||
[f bold]
|
||||
#[f bold]
|
||||
|
||||
[f 1,]
|
||||
#[f 1,]
|
||||
|
||||
[f a:2]
|
||||
#[f a:2]
|
||||
|
||||
[f 1, a: (3, 4), 2, b: "5"]
|
||||
#[f 1, a: (3, 4), 2, b: "5"]
|
||||
|
||||
---
|
||||
// Body and no body.
|
||||
[f][[f]]
|
||||
#[f][#[f]]
|
||||
|
||||
// Lots of potential bodies.
|
||||
[f][f][f]
|
||||
#[f][f]#[f]
|
||||
|
||||
// Multi-paragraph body.
|
||||
[box][
|
||||
#[box][
|
||||
First
|
||||
|
||||
Second
|
||||
@ -27,81 +27,81 @@
|
||||
|
||||
---
|
||||
// Chained.
|
||||
[f | f]
|
||||
#[f | f]
|
||||
|
||||
// Multi-chain.
|
||||
[f|f|f]
|
||||
#[f|f|f]
|
||||
|
||||
// With body.
|
||||
// Error: 1:6-1:7 expected identifier, found integer
|
||||
[f | 1 | box][💕]
|
||||
// Error: 1:7-1:8 expected identifier, found integer
|
||||
#[f | 1 | box][💕]
|
||||
|
||||
// Error: 2:2-2:2 expected identifier
|
||||
// Error: 1:3-1:3 expected identifier
|
||||
[||f true]
|
||||
// Error: 2:3-2:3 expected identifier
|
||||
// Error: 1:4-1:4 expected identifier
|
||||
#[||f true]
|
||||
|
||||
// Error: 1:6-1:6 expected identifier
|
||||
[f 1|]
|
||||
// Error: 1:7-1:7 expected identifier
|
||||
#[f 1|]
|
||||
|
||||
// Error: 2:2-2:2 expected identifier
|
||||
// Error: 1:3-1:3 expected identifier
|
||||
[|][Nope]
|
||||
// Error: 2:3-2:3 expected identifier
|
||||
// Error: 1:4-1:4 expected identifier
|
||||
#[|][Nope]
|
||||
|
||||
// Error: 2:5-2:5 expected closing paren
|
||||
// Error: 1:8-1:9 expected expression, found closing paren
|
||||
[f (|f )]
|
||||
// Error: 2:6-2:6 expected closing paren
|
||||
// Error: 1:9-1:10 expected expression, found closing paren
|
||||
#[f (|f )]
|
||||
|
||||
// With actual functions.
|
||||
[box width: 1cm | image "res/rhino.png"]
|
||||
#[box width: 1cm | image "res/rhino.png"]
|
||||
|
||||
---
|
||||
// Error: 1:4-1:6 expected expression, found end of block comment
|
||||
[f */]
|
||||
// Error: 1:5-1:7 expected expression, found end of block comment
|
||||
#[f */]
|
||||
|
||||
// Error: 1:7-1:8 expected expression, found colon
|
||||
[f a:1:]
|
||||
// Error: 1:8-1:9 expected expression, found colon
|
||||
#[f a:1:]
|
||||
|
||||
// Error: 1:5-1:5 expected comma
|
||||
[f 1 2]
|
||||
// Error: 1:6-1:6 expected comma
|
||||
#[f 1 2]
|
||||
|
||||
// Error: 2:4-2:5 expected identifier
|
||||
// Error: 1:6-1:6 expected expression
|
||||
[f 1:]
|
||||
// Error: 2:5-2:6 expected identifier
|
||||
// Error: 1:7-1:7 expected expression
|
||||
#[f 1:]
|
||||
|
||||
// Error: 1:4-1:5 expected identifier
|
||||
[f 1:2]
|
||||
// Error: 1:5-1:6 expected identifier
|
||||
#[f 1:2]
|
||||
|
||||
// Error: 1:4-1:7 expected identifier
|
||||
[f (x):1]
|
||||
// Error: 1:5-1:8 expected identifier
|
||||
#[f (x):1]
|
||||
|
||||
---
|
||||
// Ref: false
|
||||
// Error: 2:2-2:3 expected function, found string
|
||||
// Error: 2:3-2:4 expected function, found string
|
||||
#let x = "string"
|
||||
[x]
|
||||
#[x]
|
||||
|
||||
// Error: 1:2-1:3 expected identifier, found invalid token
|
||||
[# 1]
|
||||
// Error: 1:3-1:4 expected identifier, found invalid token
|
||||
#[# 1]
|
||||
|
||||
// Error: 4:1-4:1 expected identifier
|
||||
// Error: 3:1-3:1 expected closing bracket
|
||||
[
|
||||
#[
|
||||
|
||||
---
|
||||
// Ref: false
|
||||
// Error: 2:2-2:3 expected identifier, found closing paren
|
||||
// Error: 2:3-2:4 expected identifier, found closing paren
|
||||
// Error: 3:1-3:1 expected closing bracket
|
||||
[)
|
||||
#[)
|
||||
|
||||
---
|
||||
// Error: 3:1-3:1 expected closing bracket
|
||||
[f [*]
|
||||
#[f [*]
|
||||
|
||||
---
|
||||
// Error: 3:1-3:1 expected closing bracket
|
||||
[f][`a]`
|
||||
#[f][`a]`
|
||||
|
||||
---
|
||||
// Error: 3:1-3:1 expected quote
|
||||
// Error: 2:1-2:1 expected closing bracket
|
||||
[f "]
|
||||
#[f "]
|
||||
|
@ -8,7 +8,7 @@ C/*
|
||||
*/D
|
||||
|
||||
// Works in headers.
|
||||
[f /*1*/ a: "b" //
|
||||
#[f /*1*/ a: "b" //
|
||||
, 1]
|
||||
|
||||
// End should not appear without start.
|
||||
|
@ -5,7 +5,7 @@ _Emph_ and *strong*!
|
||||
Pa_rtl_y emphasized or str*ength*ened.
|
||||
|
||||
// Scoped to body.
|
||||
[box][*Sco_ped] to body.
|
||||
#[box][*Sco_ped] to body.
|
||||
|
||||
// Unterminated is fine.
|
||||
_The End
|
||||
|
@ -7,75 +7,75 @@
|
||||
#let error = +""
|
||||
|
||||
// Paren call.
|
||||
[test f(1), "f(1)"]
|
||||
[test type(1), "integer"]
|
||||
#[test f(1), "f(1)"]
|
||||
#[test type(1), "integer"]
|
||||
|
||||
// Unary operations.
|
||||
[test +1, 1]
|
||||
[test -1, 1-2]
|
||||
[test --1, 1]
|
||||
#[test +1, 1]
|
||||
#[test -1, 1-2]
|
||||
#[test --1, 1]
|
||||
|
||||
// Math operations.
|
||||
[test "a" + "b", "ab"]
|
||||
[test 1-4, 3*-1]
|
||||
[test a * b, 8]
|
||||
[test 12pt/.4, 30pt]
|
||||
[test 1e+2-1e-2, 99.99]
|
||||
#[test "a" + "b", "ab"]
|
||||
#[test 1-4, 3*-1]
|
||||
#[test a * b, 8]
|
||||
#[test 12pt/.4, 30pt]
|
||||
#[test 1e+2-1e-2, 99.99]
|
||||
|
||||
// Associativity.
|
||||
[test 1+2+3, 6]
|
||||
[test 1/2*3, 1.5]
|
||||
#[test 1+2+3, 6]
|
||||
#[test 1/2*3, 1.5]
|
||||
|
||||
// Precedence.
|
||||
[test 1+2*-3, -5]
|
||||
#[test 1+2*-3, -5]
|
||||
|
||||
// Short-circuiting logical operators.
|
||||
[test not "a" == "b", true]
|
||||
[test not 7 < 4 and 10 == 10, true]
|
||||
[test 3 < 2 or 4 < 5, true]
|
||||
[test false and false or true, true]
|
||||
#[test not "a" == "b", true]
|
||||
#[test not 7 < 4 and 10 == 10, true]
|
||||
#[test 3 < 2 or 4 < 5, true]
|
||||
#[test false and false or true, true]
|
||||
|
||||
// Right-hand side not even evaluated.
|
||||
[test false and dont-care, false]
|
||||
[test true or dont-care, true]
|
||||
#[test false and dont-care, false]
|
||||
#[test true or dont-care, true]
|
||||
|
||||
// Equality and inequality.
|
||||
[test "ab" == "a" + "b", true]
|
||||
[test [*Hi*] == [*Hi*], true]
|
||||
[test "a" != "a", false]
|
||||
[test [*] != [_], true]
|
||||
[test (1, 2, 3) == (1, 2) + (3,), true]
|
||||
[test () == (1,), false]
|
||||
[test (a: 1, b: 2) == (b: 2, a: 1), true]
|
||||
[test (:) == (a: 1), false]
|
||||
[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 [*Hi*] == [*Hi*], true]
|
||||
#[test "a" != "a", false]
|
||||
#[test [*] != [_], true]
|
||||
#[test (1, 2, 3) == (1, 2) + (3,), true]
|
||||
#[test () == (1,), false]
|
||||
#[test (a: 1, b: 2) == (b: 2, a: 1), true]
|
||||
#[test (:) == (a: 1), false]
|
||||
#[test 1 == "hi", false]
|
||||
#[test 1 == 1.0, true]
|
||||
#[test 30% == 30% + 0cm, true]
|
||||
#[test 1in == 0% + 72pt, true]
|
||||
#[test 30% == 30% + 1cm, false]
|
||||
|
||||
// Comparisons.
|
||||
[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 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]
|
||||
|
||||
// Assignment.
|
||||
#let x = "some"
|
||||
#let y = "some"
|
||||
[test (x = y = "") == none and x == none and y == "", true]
|
||||
#[test (x = y = "") == none and x == none and y == "", true]
|
||||
|
||||
// Modify-assign 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"]
|
||||
{ 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"]
|
||||
|
||||
// Error: 1:3-1:4 unknown variable
|
||||
{ z = 1 }
|
||||
@ -94,21 +94,21 @@
|
||||
#let box = ""; { box = "hi" }
|
||||
|
||||
// Parentheses.
|
||||
[test (a), 2]
|
||||
[test (2), 2]
|
||||
[test (1+2)*3, 9]
|
||||
#[test (a), 2]
|
||||
#[test (2), 2]
|
||||
#[test (1+2)*3, 9]
|
||||
|
||||
// Error: 1:3-1:3 expected expression
|
||||
{-}
|
||||
|
||||
// Error: 1:10-1:10 expected expression
|
||||
[test {1+}, 1]
|
||||
// Error: 1:11-1:11 expected expression
|
||||
#[test {1+}, 1]
|
||||
|
||||
// Error: 1:10-1:10 expected expression
|
||||
[test {2*}, 2]
|
||||
// Error: 1:11-1:11 expected expression
|
||||
#[test {2*}, 2]
|
||||
|
||||
// Error: 1:7-1:16 cannot apply '-' to boolean
|
||||
[test -not true, error]
|
||||
// Error: 1:8-1:17 cannot apply '-' to boolean
|
||||
#[test -not true, error]
|
||||
|
||||
// Error: 1:2-1:8 cannot apply 'not' to array
|
||||
{not ()}
|
||||
|
@ -1,48 +1,41 @@
|
||||
// Test different numbers of hashtags.
|
||||
|
||||
// Valid levels.
|
||||
# One
|
||||
### Three
|
||||
###### Six
|
||||
= One
|
||||
=== Three
|
||||
====== Six
|
||||
|
||||
// Too many hashtags.
|
||||
// Warning: 1:1-1:8 should not exceed depth 6
|
||||
####### Seven
|
||||
======= Seven
|
||||
|
||||
---
|
||||
// Test heading vs. no heading.
|
||||
|
||||
// Parsed as headings if at start of the context.
|
||||
/**/ # Heading
|
||||
{[## Heading]}
|
||||
[box][### Heading]
|
||||
/**/ = Heading
|
||||
{[== Heading]}
|
||||
#[box][=== Heading]
|
||||
|
||||
// Not at the start of the context.
|
||||
Text with # hashtag
|
||||
Text = with=sign
|
||||
|
||||
// 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
|
||||
\= No heading
|
||||
|
||||
---
|
||||
// Heading continuation over linebreak.
|
||||
|
||||
// Code blocks continue heading.
|
||||
# This {
|
||||
= This {
|
||||
"continues"
|
||||
}
|
||||
|
||||
// Function call continues heading.
|
||||
# [box][
|
||||
= #[box][
|
||||
This,
|
||||
] too
|
||||
|
||||
// Without some kind of block, headings end at a line break.
|
||||
# This
|
||||
= This
|
||||
not
|
||||
|
@ -52,8 +52,7 @@ a#if x b#if (x)c
|
||||
a#if true [b] #else c
|
||||
|
||||
// Lone else.
|
||||
// Error: 2:1-2:6 unexpected keyword `#else`
|
||||
// Error: 1:8-1:8 expected identifier
|
||||
// Error: 1:1-1:6 unexpected keyword `#else`
|
||||
#else []
|
||||
|
||||
// Condition must be boolean. If it isn't, neither branch is evaluated.
|
||||
|
@ -1,10 +1,10 @@
|
||||
// Automatically initialized with `none`.
|
||||
#let x
|
||||
[test x, none]
|
||||
#[test x, none]
|
||||
|
||||
// Initialized with `1`.
|
||||
#let y = 1
|
||||
[test y, 1]
|
||||
#[test y, 1]
|
||||
|
||||
// Initialize with template, not terminated by semicolon in template.
|
||||
#let v = [Hello; there]
|
||||
@ -15,10 +15,10 @@
|
||||
2,
|
||||
3,
|
||||
)
|
||||
[test x, (1, 2, 3)]
|
||||
#[test x, (1, 2, 3)]
|
||||
|
||||
// Multiple bindings in one line.
|
||||
#let x = "a"; #let y = "b"; [test x + y, "ab"]
|
||||
#let x = "a"; #let y = "b"; #[test x + y, "ab"]
|
||||
|
||||
// Invalid name.
|
||||
// Error: 1:6-1:7 expected identifier, found integer
|
||||
@ -43,24 +43,24 @@ The Fi#let;rst
|
||||
|
||||
// Terminated with just a line break.
|
||||
#let v = "a"
|
||||
The Second [test v, "a"]
|
||||
The Second #[test v, "a"]
|
||||
|
||||
// Terminated with semicolon + line break.
|
||||
#let v = "a";
|
||||
The Third [test v, "a"]
|
||||
The Third #[test v, "a"]
|
||||
|
||||
// Terminated with just a semicolon.
|
||||
The#let v = "a"; Fourth [test v, "a"]
|
||||
The#let v = "a"; Fourth #[test v, "a"]
|
||||
|
||||
// Terminated by semicolon even though we are in a paren group.
|
||||
// Error: 2:25-2:25 expected expression
|
||||
// Error: 1:25-1:25 expected closing paren
|
||||
The#let array = (1, 2 + ;Fifth [test array, (1, 2)]
|
||||
The#let array = (1, 2 + ;Fifth #[test array, (1, 2)]
|
||||
|
||||
// Not terminated.
|
||||
// Error: 1:16-1:16 expected semicolon or line break
|
||||
The#let v = "a"Sixth [test v, "a"]
|
||||
The#let v = "a"Sixth #[test v, "a"]
|
||||
|
||||
// Not terminated.
|
||||
// Error: 1:16-1:16 expected semicolon or line break
|
||||
The#let v = "a" [test v, "a"] Seventh
|
||||
The#let v = "a" #[test v, "a"] Seventh
|
||||
|
@ -1,4 +1,4 @@
|
||||
[font 8pt]
|
||||
#[font 8pt]
|
||||
|
||||
// Typst syntax inside.
|
||||
`#let x = 1``[f 1]`
|
||||
|
@ -37,4 +37,4 @@
|
||||
{"a\n[]\"\u{1F680}string"} \
|
||||
|
||||
// Templates.
|
||||
{[*{"Hi"} [f 1]*]}
|
||||
{[*{"Hi"} #[f 1]*]}
|
||||
|
@ -1,37 +1,37 @@
|
||||
// Test configuring font properties.
|
||||
|
||||
[font "PT Sans", 10pt]
|
||||
#[font "PT Sans", 10pt]
|
||||
|
||||
// Set same font size in three different ways.
|
||||
[font 20pt][A]
|
||||
[font 200%][A]
|
||||
[font 15pt + 50%][A]
|
||||
#[font 20pt][A]
|
||||
#[font 200%][A]
|
||||
#[font 15pt + 50%][A]
|
||||
|
||||
// Do nothing.
|
||||
[font][Normal]
|
||||
#[font][Normal]
|
||||
|
||||
// Set style (is available).
|
||||
[font style: italic][Italic]
|
||||
#[font style: italic][Italic]
|
||||
|
||||
// Set weight (is available).
|
||||
[font weight: bold][Bold]
|
||||
#[font weight: bold][Bold]
|
||||
|
||||
// Set stretch (not available, matching closest).
|
||||
[font stretch: ultra-condensed][Condensed]
|
||||
#[font stretch: ultra-condensed][Condensed]
|
||||
|
||||
// Error: 1:7-1:12 unexpected argument
|
||||
[font false]
|
||||
// Error: 1:8-1:13 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]
|
||||
// Error: 3:15-3:19 expected font style, found font weight
|
||||
// Error: 2:29-2:35 expected font weight, found string
|
||||
// Error: 1:44-1:45 expected font family or array of font families, found integer
|
||||
#[font style: bold, weight: "thin", serif: 0]
|
||||
|
||||
// Warning: 1:15-1:19 should be between 100 and 900
|
||||
[font weight: 2700]
|
||||
// Warning: 1:16-1:20 should be between 100 and 900
|
||||
#[font weight: 2700]
|
||||
|
||||
// Error: 1:7-1:27 unexpected argument
|
||||
[font something: "invalid"]
|
||||
// Error: 1:8-1:28 unexpected argument
|
||||
#[font something: "invalid"]
|
||||
|
||||
---
|
||||
// Test font fallback and class definitions.
|
||||
@ -40,17 +40,17 @@
|
||||
Emoji: 🏀
|
||||
|
||||
// CMU Serif + Noto Emoji.
|
||||
[font "CMU Serif", "Noto Emoji"][
|
||||
#[font "CMU Serif", "Noto Emoji"][
|
||||
Emoji: 🏀
|
||||
]
|
||||
|
||||
// Class definitions.
|
||||
[font serif: ("CMU Serif", "Latin Modern Math", "Noto Emoji")]
|
||||
[font serif][
|
||||
#[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)]
|
||||
#[font sans-serif: "Noto Emoji"]
|
||||
#[font sans-serif: ("Archivo", sans-serif)]
|
||||
New sans-serif. 🚀
|
||||
|
@ -1,22 +1,22 @@
|
||||
// Ends paragraphs.
|
||||
Tightly [v -5pt] packed
|
||||
Tightly #[v -5pt] packed
|
||||
|
||||
// Eating up soft spacing.
|
||||
Inv [h 0pt] isible
|
||||
Inv #[h 0pt] isible
|
||||
|
||||
// Multiple spacings in a row.
|
||||
Add [h 10pt] [h 10pt] up
|
||||
Add #[h 10pt] #[h 10pt] up
|
||||
|
||||
// Relative to font size.
|
||||
Relative [h 100%] spacing
|
||||
Relative #[h 100%] spacing
|
||||
|
||||
// Missing spacing.
|
||||
// Error: 1:11-1:11 missing argument: spacing
|
||||
Totally [h] ignored
|
||||
// Error: 1:12-1:12 missing argument: spacing
|
||||
Totally #[h] ignored
|
||||
|
||||
// Swapped axes.
|
||||
[page main-dir: rtl, cross-dir: ttb, height: 80pt][
|
||||
1 [h 1cm] 2
|
||||
#[page main-dir: rtl, cross-dir: ttb, height: 80pt][
|
||||
1 #[h 1cm] 2
|
||||
|
||||
3 [v 1cm] 4 [v -1cm] 5
|
||||
3 #[v 1cm] 4 #[v -1cm] 5
|
||||
]
|
||||
|
@ -1,39 +1,39 @@
|
||||
// Test loading different image formats.
|
||||
|
||||
// Load an RGBA PNG image.
|
||||
[image "res/rhino.png"]
|
||||
[pagebreak]
|
||||
#[image "res/rhino.png"]
|
||||
#[pagebreak]
|
||||
|
||||
// Load an RGB JPEG image.
|
||||
[image "res/tiger.jpg"]
|
||||
#[image "res/tiger.jpg"]
|
||||
|
||||
// Error: 1:8-1:29 failed to load image
|
||||
[image "path/does/not/exist"]
|
||||
// Error: 1:9-1:30 failed to load image
|
||||
#[image "path/does/not/exist"]
|
||||
|
||||
// Error: 1:8-1:29 failed to load image
|
||||
[image "typ/image-error.typ"]
|
||||
// Error: 1:9-1:30 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"]
|
||||
#[image "res/rhino.png"]
|
||||
|
||||
// Fit to height of page.
|
||||
[page height: 40pt][
|
||||
[image "res/rhino.png"]
|
||||
#[page height: 40pt][
|
||||
#[image "res/rhino.png"]
|
||||
]
|
||||
|
||||
// Set width explicitly.
|
||||
[image "res/rhino.png", width: 50pt]
|
||||
#[image "res/rhino.png", width: 50pt]
|
||||
|
||||
// Set height explicitly.
|
||||
[image "res/rhino.png", height: 50pt]
|
||||
#[image "res/rhino.png", height: 50pt]
|
||||
|
||||
// Set width and height explicitly and force stretching.
|
||||
[image "res/rhino.png", width: 25pt, height: 50pt]
|
||||
#[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]
|
||||
#[align bottom, right][
|
||||
#[image "res/tiger.jpg", width: 60pt]
|
||||
]
|
||||
|
@ -1,54 +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]
|
||||
#[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]
|
||||
#[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]
|
||||
#[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]
|
||||
#[page margins: 0pt, left: 20pt][Overriden]
|
||||
|
||||
// Error: 1:7-1:18 unknown variable
|
||||
[page nonexistant]
|
||||
// Error: 1:8-1:19 unknown variable
|
||||
#[page nonexistant]
|
||||
|
||||
// Error: 1:17-1:20 aligned axis
|
||||
[page main-dir: ltr]
|
||||
// Error: 1:18-1:21 aligned axis
|
||||
#[page main-dir: ltr]
|
||||
|
||||
// Flipped predefined paper.
|
||||
[page "a11", flip: true][Flipped A11]
|
||||
#[page "a11", flip: true][Flipped A11]
|
||||
|
||||
// Flipped custom page size.
|
||||
[page width: 40pt, height: 120pt]
|
||||
[page flip: true]
|
||||
#[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]
|
||||
#[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 height: 50pt]
|
||||
|
||||
[page][First]
|
||||
[page][Second]
|
||||
[pagebreak]
|
||||
#[page][First]
|
||||
#[page][Second]
|
||||
#[pagebreak]
|
||||
Fourth
|
||||
[page][]
|
||||
#[page][]
|
||||
Sixth
|
||||
[page][Seventh and last]
|
||||
#[page][Seventh and last]
|
||||
|
@ -1,3 +1,3 @@
|
||||
First of two
|
||||
[pagebreak]
|
||||
[page height: 40pt]
|
||||
#[pagebreak]
|
||||
#[page height: 40pt]
|
||||
|
@ -1,17 +1,17 @@
|
||||
// Check the output.
|
||||
[rgb 0.0, 0.3, 0.7]
|
||||
#[rgb 0.0, 0.3, 0.7]
|
||||
|
||||
// Alpha channel.
|
||||
[rgb 1.0, 0.0, 0.0, 0.5]
|
||||
#[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]
|
||||
// Warning: 2:7-2:10 should be between 0.0 and 1.0
|
||||
// Warning: 1:12-1:16 should be between 0.0 and 1.0
|
||||
#[rgb -30, 15.5, 0.5]
|
||||
|
||||
// Error: 1:6-1:10 missing argument: blue component
|
||||
[rgb 0, 1]
|
||||
// Error: 1:7-1:11 missing argument: blue component
|
||||
#[rgb 0, 1]
|
||||
|
||||
// Error: 3:5-3:5 missing argument: red component
|
||||
// Error: 2:5-2:5 missing argument: green component
|
||||
// Error: 1:5-1:5 missing argument: blue component
|
||||
[rgb]
|
||||
// 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]
|
||||
|
Loading…
Reference in New Issue
Block a user