mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Multi-expression blocks 🛍️
This commit is contained in:
parent
aaa48403cd
commit
e847082435
@ -68,7 +68,7 @@ impl Eval for &[Spanned<Node>] {
|
|||||||
|
|
||||||
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
|
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
|
||||||
for node in self {
|
for node in self {
|
||||||
node.as_ref().eval(ctx);
|
node.eval(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,8 +175,8 @@ impl Eval for Spanned<&Expr> {
|
|||||||
Expr::Array(v) => Value::Array(v.with_span(self.span).eval(ctx)),
|
Expr::Array(v) => Value::Array(v.with_span(self.span).eval(ctx)),
|
||||||
Expr::Dict(v) => Value::Dict(v.with_span(self.span).eval(ctx)),
|
Expr::Dict(v) => Value::Dict(v.with_span(self.span).eval(ctx)),
|
||||||
Expr::Template(v) => Value::Template(v.clone()),
|
Expr::Template(v) => Value::Template(v.clone()),
|
||||||
Expr::Group(v) => v.as_ref().eval(ctx),
|
Expr::Group(v) => v.eval(ctx),
|
||||||
Expr::Block(v) => v.as_ref().eval(ctx),
|
Expr::Block(v) => v.with_span(self.span).eval(ctx),
|
||||||
Expr::Call(v) => v.with_span(self.span).eval(ctx),
|
Expr::Call(v) => v.with_span(self.span).eval(ctx),
|
||||||
Expr::Unary(v) => v.with_span(self.span).eval(ctx),
|
Expr::Unary(v) => v.with_span(self.span).eval(ctx),
|
||||||
Expr::Binary(v) => v.with_span(self.span).eval(ctx),
|
Expr::Binary(v) => v.with_span(self.span).eval(ctx),
|
||||||
@ -190,7 +190,7 @@ impl Eval for Spanned<&ExprArray> {
|
|||||||
type Output = ValueArray;
|
type Output = ValueArray;
|
||||||
|
|
||||||
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
|
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
|
||||||
self.v.iter().map(|expr| expr.as_ref().eval(ctx)).collect()
|
self.v.iter().map(|expr| expr.eval(ctx)).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,16 +200,28 @@ impl Eval for Spanned<&ExprDict> {
|
|||||||
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
|
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
|
||||||
self.v
|
self.v
|
||||||
.iter()
|
.iter()
|
||||||
.map(|Named { name, expr }| (name.v.0.clone(), expr.as_ref().eval(ctx)))
|
.map(|Named { name, expr }| (name.v.0.clone(), expr.eval(ctx)))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Eval for Spanned<&ExprBlock> {
|
||||||
|
type Output = Value;
|
||||||
|
|
||||||
|
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
|
||||||
|
let mut output = Value::None;
|
||||||
|
for expr in &self.v.exprs {
|
||||||
|
output = expr.eval(ctx);
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Eval for Spanned<&ExprUnary> {
|
impl Eval for Spanned<&ExprUnary> {
|
||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
|
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
|
||||||
let value = self.v.expr.as_ref().eval(ctx);
|
let value = self.v.expr.eval(ctx);
|
||||||
if value == Value::Error {
|
if value == Value::Error {
|
||||||
return Value::Error;
|
return Value::Error;
|
||||||
}
|
}
|
||||||
@ -327,8 +339,8 @@ impl Eval for Spanned<&ExprLet> {
|
|||||||
type Output = Value;
|
type Output = Value;
|
||||||
|
|
||||||
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
|
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
|
||||||
let value = match &self.v.expr {
|
let value = match &self.v.init {
|
||||||
Some(expr) => expr.as_ref().eval(ctx),
|
Some(expr) => expr.eval(ctx),
|
||||||
None => Value::None,
|
None => Value::None,
|
||||||
};
|
};
|
||||||
ctx.scopes.define(self.v.pat.v.as_str(), value);
|
ctx.scopes.define(self.v.pat.v.as_str(), value);
|
||||||
|
@ -109,10 +109,13 @@ pub fn div(lhs: Value, rhs: Value) -> Value {
|
|||||||
(Float(a), Float(b)) => Float(a / b),
|
(Float(a), Float(b)) => Float(a / b),
|
||||||
(Length(a), Int(b)) => Length(a / b as f64),
|
(Length(a), Int(b)) => Length(a / b as f64),
|
||||||
(Length(a), Float(b)) => Length(a / b),
|
(Length(a), Float(b)) => Length(a / b),
|
||||||
|
(Length(a), Length(b)) => Float(a / b),
|
||||||
(Angle(a), Int(b)) => Angle(a / b as f64),
|
(Angle(a), Int(b)) => Angle(a / b as f64),
|
||||||
(Angle(a), Float(b)) => Angle(a / b),
|
(Angle(a), Float(b)) => Angle(a / b),
|
||||||
|
(Angle(a), Angle(b)) => Float(a / b),
|
||||||
(Relative(a), Int(b)) => Relative(a / b as f64),
|
(Relative(a), Int(b)) => Relative(a / b as f64),
|
||||||
(Relative(a), Float(b)) => Relative(a / b),
|
(Relative(a), Float(b)) => Relative(a / b),
|
||||||
|
(Relative(a), Relative(b)) => Float(a / b),
|
||||||
(Linear(a), Int(b)) => Linear(a / b as f64),
|
(Linear(a), Int(b)) => Linear(a / b as f64),
|
||||||
(Linear(a), Float(b)) => Linear(a / b),
|
(Linear(a), Float(b)) => Linear(a / b),
|
||||||
_ => Error,
|
_ => Error,
|
||||||
|
@ -94,6 +94,14 @@ impl Div<f64> for Relative {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Div for Relative {
|
||||||
|
type Output = f64;
|
||||||
|
|
||||||
|
fn div(self, other: Self) -> f64 {
|
||||||
|
self.0 / other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assign_impl!(Relative += Relative);
|
assign_impl!(Relative += Relative);
|
||||||
assign_impl!(Relative -= Relative);
|
assign_impl!(Relative -= Relative);
|
||||||
assign_impl!(Relative *= f64);
|
assign_impl!(Relative *= f64);
|
||||||
|
141
src/parse/mod.rs
141
src/parse/mod.rs
@ -43,7 +43,8 @@ fn tree(p: &mut Parser) -> Tree {
|
|||||||
|
|
||||||
/// Parse a syntax node.
|
/// Parse a syntax node.
|
||||||
fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
|
fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
|
||||||
let node = match p.peek()? {
|
let token = p.peek()?;
|
||||||
|
let node = match token {
|
||||||
// Bracket call.
|
// Bracket call.
|
||||||
Token::LeftBracket => {
|
Token::LeftBracket => {
|
||||||
return Some(Node::Expr(bracket_call(p)?));
|
return Some(Node::Expr(bracket_call(p)?));
|
||||||
@ -75,8 +76,21 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
|
|||||||
Token::UnicodeEscape(t) => Node::Text(unicode_escape(p, t)),
|
Token::UnicodeEscape(t) => Node::Text(unicode_escape(p, t)),
|
||||||
|
|
||||||
// Keywords.
|
// Keywords.
|
||||||
Token::Let => return Some(Node::Expr(stmt_let(p)?)),
|
Token::Let | Token::If | Token::For => {
|
||||||
Token::If => return Some(Node::Expr(expr_if(p)?)),
|
let stmt = token == Token::Let;
|
||||||
|
let group = if stmt { Group::Stmt } else { Group::Expr };
|
||||||
|
|
||||||
|
p.start_group(group, TokenMode::Code);
|
||||||
|
let expr = primary(p);
|
||||||
|
if stmt && expr.is_some() && !p.eof() {
|
||||||
|
p.expected_at("semicolon or line break", p.last_end());
|
||||||
|
}
|
||||||
|
p.end_group();
|
||||||
|
|
||||||
|
// Uneat spaces we might have eaten eagerly.
|
||||||
|
p.jump(p.last_end());
|
||||||
|
return expr.map(Node::Expr);
|
||||||
|
}
|
||||||
|
|
||||||
// Comments.
|
// Comments.
|
||||||
Token::LineComment(_) | Token::BlockComment(_) => {
|
Token::LineComment(_) | Token::BlockComment(_) => {
|
||||||
@ -97,7 +111,7 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
|
|||||||
fn heading(p: &mut Parser) -> NodeHeading {
|
fn heading(p: &mut Parser) -> NodeHeading {
|
||||||
// Count hashtags.
|
// Count hashtags.
|
||||||
let mut level = p.span(|p| {
|
let mut level = p.span(|p| {
|
||||||
p.eat_assert(Token::Hash);
|
p.assert(Token::Hash);
|
||||||
|
|
||||||
let mut level = 0u8;
|
let mut level = 0u8;
|
||||||
while p.eat_if(Token::Hash) {
|
while p.eat_if(Token::Hash) {
|
||||||
@ -194,10 +208,6 @@ fn bracket_subheader(p: &mut Parser) -> Option<ExprCall> {
|
|||||||
p.start_group(Group::Subheader, TokenMode::Code);
|
p.start_group(Group::Subheader, TokenMode::Code);
|
||||||
|
|
||||||
let name = p.span_if(ident);
|
let name = p.span_if(ident);
|
||||||
if name.is_none() {
|
|
||||||
p.expected("function name");
|
|
||||||
}
|
|
||||||
|
|
||||||
let args = p.span(arguments);
|
let args = p.span(arguments);
|
||||||
p.end_group();
|
p.end_group();
|
||||||
|
|
||||||
@ -215,14 +225,6 @@ fn bracket_body(p: &mut Parser) -> Tree {
|
|||||||
tree
|
tree
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an identifier.
|
|
||||||
fn ident(p: &mut Parser) -> Option<Ident> {
|
|
||||||
p.eat_map(|token| match token {
|
|
||||||
Token::Ident(id) => Some(Ident(id.into())),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse an expression.
|
/// Parse an expression.
|
||||||
fn expr(p: &mut Parser) -> Option<Expr> {
|
fn expr(p: &mut Parser) -> Option<Expr> {
|
||||||
expr_with(p, 0)
|
expr_with(p, 0)
|
||||||
@ -314,6 +316,10 @@ fn primary(p: &mut Parser) -> Option<Expr> {
|
|||||||
Some(Token::Color(color)) => Expr::Color(color),
|
Some(Token::Color(color)) => Expr::Color(color),
|
||||||
Some(Token::Str(token)) => Expr::Str(string(p, token)),
|
Some(Token::Str(token)) => Expr::Str(string(p, token)),
|
||||||
|
|
||||||
|
// Keywords.
|
||||||
|
Some(Token::Let) => return expr_let(p),
|
||||||
|
Some(Token::If) => return expr_if(p),
|
||||||
|
|
||||||
// No value.
|
// No value.
|
||||||
_ => {
|
_ => {
|
||||||
p.expected("expression");
|
p.expected("expression");
|
||||||
@ -335,12 +341,20 @@ fn template(p: &mut Parser) -> Expr {
|
|||||||
/// Parse a block expression: `{...}`.
|
/// Parse a block expression: `{...}`.
|
||||||
fn block(p: &mut Parser) -> Option<Expr> {
|
fn block(p: &mut Parser) -> Option<Expr> {
|
||||||
p.start_group(Group::Brace, TokenMode::Code);
|
p.start_group(Group::Brace, TokenMode::Code);
|
||||||
let expr = p.span_if(expr);
|
let mut exprs = vec![];
|
||||||
while !p.eof() {
|
while !p.eof() {
|
||||||
p.unexpected();
|
p.start_group(Group::Stmt, TokenMode::Code);
|
||||||
|
if let Some(expr) = p.span_if(expr) {
|
||||||
|
exprs.push(expr);
|
||||||
|
if !p.eof() {
|
||||||
|
p.expected_at("semicolon or line break", p.last_end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.end_group();
|
p.end_group();
|
||||||
Some(Expr::Block(Box::new(expr?)))
|
p.skip_white();
|
||||||
|
}
|
||||||
|
p.end_group();
|
||||||
|
Some(Expr::Block(ExprBlock { exprs }))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a parenthesized function call.
|
/// Parse a parenthesized function call.
|
||||||
@ -363,72 +377,69 @@ fn string(p: &mut Parser, token: TokenStr) -> String {
|
|||||||
resolve::resolve_string(token.string)
|
resolve::resolve_string(token.string)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a let statement.
|
/// Parse a let expression.
|
||||||
fn stmt_let(p: &mut Parser) -> Option<Expr> {
|
fn expr_let(p: &mut Parser) -> Option<Expr> {
|
||||||
p.start_group(Group::Stmt, TokenMode::Code);
|
p.assert(Token::Let);
|
||||||
p.eat_assert(Token::Let);
|
|
||||||
|
|
||||||
let pat = match p.span_if(ident) {
|
let mut expr_let = None;
|
||||||
Some(pat) => pat,
|
if let Some(pat) = p.span_if(ident) {
|
||||||
None => {
|
let mut init = None;
|
||||||
p.expected("identifier");
|
if p.eat_if(Token::Eq) {
|
||||||
p.end_group();
|
init = p.span_if(expr);
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let rhs = if p.eat_if(Token::Eq) { p.span_if(expr) } else { None };
|
|
||||||
|
|
||||||
if !p.eof() {
|
|
||||||
p.expected_at("semicolon or line break", p.last_end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p.end_group();
|
expr_let = Some(Expr::Let(ExprLet { pat, init: init.map(Box::new) }))
|
||||||
|
}
|
||||||
|
|
||||||
Some(Expr::Let(ExprLet { pat, expr: rhs.map(Box::new) }))
|
expr_let
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an if expresion.
|
/// Parse an if expresion.
|
||||||
fn expr_if(p: &mut Parser) -> Option<Expr> {
|
fn expr_if(p: &mut Parser) -> Option<Expr> {
|
||||||
p.start_group(Group::Expr, TokenMode::Code);
|
p.assert(Token::If);
|
||||||
p.eat_assert(Token::If);
|
|
||||||
|
|
||||||
let condition = match p.span_if(expr) {
|
let mut expr_if = None;
|
||||||
Some(condition) => Box::new(condition),
|
if let Some(condition) = p.span_if(expr) {
|
||||||
None => {
|
if let Some(if_body) = p.span_if(body) {
|
||||||
p.end_group();
|
let mut else_body = None;
|
||||||
return None;
|
if p.eat_if(Token::Else) {
|
||||||
|
else_body = p.span_if(body);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
p.end_group();
|
expr_if = Some(Expr::If(ExprIf {
|
||||||
|
condition: Box::new(condition),
|
||||||
|
if_body: Box::new(if_body),
|
||||||
|
else_body: else_body.map(Box::new),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let if_body = Box::new(control_body(p)?);
|
expr_if
|
||||||
|
}
|
||||||
|
|
||||||
let start = p.last_end();
|
|
||||||
p.skip_white();
|
|
||||||
|
|
||||||
let else_body = if p.eat_if(Token::Else) {
|
|
||||||
control_body(p).map(Box::new)
|
/// Parse an identifier.
|
||||||
} else {
|
fn ident(p: &mut Parser) -> Option<Ident> {
|
||||||
p.jump(start);
|
match p.peek() {
|
||||||
|
Some(Token::Ident(id)) => {
|
||||||
|
p.eat();
|
||||||
|
Some(Ident(id.into()))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
p.expected("identifier");
|
||||||
None
|
None
|
||||||
};
|
}
|
||||||
|
}
|
||||||
Some(Expr::If(ExprIf { condition, if_body, else_body }))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a control flow body.
|
/// Parse a control flow body.
|
||||||
fn control_body(p: &mut Parser) -> Option<Spanned<Expr>> {
|
fn body(p: &mut Parser) -> Option<Expr> {
|
||||||
let start = p.last_end();
|
|
||||||
p.skip_white();
|
|
||||||
|
|
||||||
match p.peek() {
|
match p.peek() {
|
||||||
Some(Token::LeftBracket) => Some(p.span(template)),
|
Some(Token::LeftBracket) => Some(template(p)),
|
||||||
Some(Token::LeftBrace) => p.span_if(block),
|
Some(Token::LeftBrace) => block(p),
|
||||||
_ => {
|
_ => {
|
||||||
p.expected_at("body", start);
|
p.expected_at("body", p.last_end());
|
||||||
p.jump(start);
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,10 +103,11 @@ impl<'s> Parser<'s> {
|
|||||||
|
|
||||||
self.groups.push(group);
|
self.groups.push(group);
|
||||||
self.repeek();
|
self.repeek();
|
||||||
|
|
||||||
match group {
|
match group {
|
||||||
Group::Paren => self.eat_assert(Token::LeftParen),
|
Group::Paren => self.assert(Token::LeftParen),
|
||||||
Group::Bracket => self.eat_assert(Token::LeftBracket),
|
Group::Bracket => self.assert(Token::LeftBracket),
|
||||||
Group::Brace => self.eat_assert(Token::LeftBrace),
|
Group::Brace => self.assert(Token::LeftBrace),
|
||||||
Group::Subheader => {}
|
Group::Subheader => {}
|
||||||
Group::Stmt => {}
|
Group::Stmt => {}
|
||||||
Group::Expr => {}
|
Group::Expr => {}
|
||||||
@ -199,8 +200,18 @@ impl<'s> Parser<'s> {
|
|||||||
mapped
|
mapped
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consume the next token if it is the given one and produce an error if
|
||||||
|
/// not.
|
||||||
|
pub fn expect(&mut self, t: Token) -> bool {
|
||||||
|
let eaten = self.eat_if(t);
|
||||||
|
if !eaten {
|
||||||
|
self.expected(t.name());
|
||||||
|
}
|
||||||
|
eaten
|
||||||
|
}
|
||||||
|
|
||||||
/// Consume the next token, debug-asserting that it is the given one.
|
/// Consume the next token, debug-asserting that it is the given one.
|
||||||
pub fn eat_assert(&mut self, t: Token) {
|
pub fn assert(&mut self, t: Token) {
|
||||||
let next = self.eat();
|
let next = self.eat();
|
||||||
debug_assert_eq!(next, Some(t));
|
debug_assert_eq!(next, Some(t));
|
||||||
}
|
}
|
||||||
@ -277,6 +288,7 @@ impl<'s> Parser<'s> {
|
|||||||
scanner
|
scanner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Move to the next token, skipping whitespace and comments in code mode.
|
||||||
fn bump(&mut self) {
|
fn bump(&mut self) {
|
||||||
self.last_end = self.tokens.pos();
|
self.last_end = self.tokens.pos();
|
||||||
self.next_start = self.tokens.pos();
|
self.next_start = self.tokens.pos();
|
||||||
@ -286,11 +298,7 @@ impl<'s> Parser<'s> {
|
|||||||
TokenMode::Markup => {}
|
TokenMode::Markup => {}
|
||||||
TokenMode::Code => loop {
|
TokenMode::Code => loop {
|
||||||
match self.next {
|
match self.next {
|
||||||
Some(Token::Space(n)) => {
|
Some(Token::Space(n)) if n < 1 || !self.in_line_group() => {}
|
||||||
if n >= 1 && self.groups.last() == Some(&Group::Stmt) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(Token::LineComment(_)) => {}
|
Some(Token::LineComment(_)) => {}
|
||||||
Some(Token::BlockComment(_)) => {}
|
Some(Token::BlockComment(_)) => {}
|
||||||
_ => break,
|
_ => break,
|
||||||
@ -304,6 +312,7 @@ impl<'s> Parser<'s> {
|
|||||||
self.repeek();
|
self.repeek();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Take another look at the next token to recheck whether it ends a group.
|
||||||
fn repeek(&mut self) {
|
fn repeek(&mut self) {
|
||||||
self.peeked = self.next;
|
self.peeked = self.next;
|
||||||
let token = match self.next {
|
let token = match self.next {
|
||||||
@ -316,13 +325,18 @@ impl<'s> Parser<'s> {
|
|||||||
Token::RightBracket if self.groups.contains(&Group::Bracket) => {}
|
Token::RightBracket if self.groups.contains(&Group::Bracket) => {}
|
||||||
Token::RightBrace if self.groups.contains(&Group::Brace) => {}
|
Token::RightBrace if self.groups.contains(&Group::Brace) => {}
|
||||||
Token::Semicolon if self.groups.contains(&Group::Stmt) => {}
|
Token::Semicolon if self.groups.contains(&Group::Stmt) => {}
|
||||||
Token::Space(n) if n >= 1 && self.groups.last() == Some(&Group::Stmt) => {}
|
Token::Space(n) if n >= 1 && self.in_line_group() => {}
|
||||||
Token::Pipe if self.groups.contains(&Group::Subheader) => {}
|
Token::Pipe if self.groups.contains(&Group::Subheader) => {}
|
||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.peeked = None;
|
self.peeked = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the active group ends at a newline.
|
||||||
|
fn in_line_group(&self) -> bool {
|
||||||
|
matches!(self.groups.last(), Some(&Group::Stmt) | Some(&Group::Expr))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Parser<'_> {
|
impl Debug for Parser<'_> {
|
||||||
|
@ -75,11 +75,7 @@ impl Pretty for Expr {
|
|||||||
v.v.pretty(p);
|
v.v.pretty(p);
|
||||||
p.push_str(")");
|
p.push_str(")");
|
||||||
}
|
}
|
||||||
Self::Block(v) => {
|
Self::Block(v) => v.pretty(p),
|
||||||
p.push_str("{");
|
|
||||||
v.v.pretty(p);
|
|
||||||
p.push_str("}");
|
|
||||||
}
|
|
||||||
Self::Unary(v) => v.pretty(p),
|
Self::Unary(v) => v.pretty(p),
|
||||||
Self::Binary(v) => v.pretty(p),
|
Self::Binary(v) => v.pretty(p),
|
||||||
Self::Call(v) => v.pretty(p),
|
Self::Call(v) => v.pretty(p),
|
||||||
@ -139,10 +135,28 @@ impl Pretty for Named {
|
|||||||
pub type ExprTemplate = Tree;
|
pub type ExprTemplate = Tree;
|
||||||
|
|
||||||
/// A grouped expression: `(1 + 2)`.
|
/// A grouped expression: `(1 + 2)`.
|
||||||
pub type ExprGroup = Box<Spanned<Expr>>;
|
pub type ExprGroup = SpanBox<Expr>;
|
||||||
|
|
||||||
/// A block expression: `{1 + 2}`.
|
/// A block expression: `{1 + 2}`.
|
||||||
pub type ExprBlock = Box<Spanned<Expr>>;
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct ExprBlock {
|
||||||
|
/// The list of expressions contained in the block.
|
||||||
|
pub exprs: SpanVec<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pretty for ExprBlock {
|
||||||
|
fn pretty(&self, p: &mut Printer) {
|
||||||
|
p.push_str("{");
|
||||||
|
if self.exprs.len() > 1 {
|
||||||
|
p.push_str(" ");
|
||||||
|
}
|
||||||
|
p.join(&self.exprs, "; ", |expr, p| expr.v.pretty(p));
|
||||||
|
if self.exprs.len() > 1 {
|
||||||
|
p.push_str(" ");
|
||||||
|
}
|
||||||
|
p.push_str("}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A unary operation: `-x`.
|
/// A unary operation: `-x`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@ -150,7 +164,7 @@ pub struct ExprUnary {
|
|||||||
/// The operator: `-`.
|
/// The operator: `-`.
|
||||||
pub op: Spanned<UnOp>,
|
pub op: Spanned<UnOp>,
|
||||||
/// The expression to operator on: `x`.
|
/// The expression to operator on: `x`.
|
||||||
pub expr: Box<Spanned<Expr>>,
|
pub expr: SpanBox<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprUnary {
|
impl Pretty for ExprUnary {
|
||||||
@ -213,11 +227,11 @@ impl Pretty for UnOp {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprBinary {
|
pub struct ExprBinary {
|
||||||
/// The left-hand side of the operation: `a`.
|
/// The left-hand side of the operation: `a`.
|
||||||
pub lhs: Box<Spanned<Expr>>,
|
pub lhs: SpanBox<Expr>,
|
||||||
/// The operator: `+`.
|
/// The operator: `+`.
|
||||||
pub op: Spanned<BinOp>,
|
pub op: Spanned<BinOp>,
|
||||||
/// The right-hand side of the operation: `b`.
|
/// The right-hand side of the operation: `b`.
|
||||||
pub rhs: Box<Spanned<Expr>>,
|
pub rhs: SpanBox<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprBinary {
|
impl Pretty for ExprBinary {
|
||||||
@ -376,7 +390,7 @@ pub enum Associativity {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprCall {
|
pub struct ExprCall {
|
||||||
/// The callee of the function.
|
/// The callee of the function.
|
||||||
pub callee: Box<Spanned<Expr>>,
|
pub callee: SpanBox<Expr>,
|
||||||
/// The arguments to the function.
|
/// The arguments to the function.
|
||||||
pub args: Spanned<ExprArgs>,
|
pub args: Spanned<ExprArgs>,
|
||||||
}
|
}
|
||||||
@ -466,17 +480,17 @@ impl Pretty for Argument {
|
|||||||
pub struct ExprLet {
|
pub struct ExprLet {
|
||||||
/// The pattern to assign to.
|
/// The pattern to assign to.
|
||||||
pub pat: Spanned<Ident>,
|
pub pat: Spanned<Ident>,
|
||||||
/// The expression to assign to the pattern.
|
/// The expression the pattern is initialized with.
|
||||||
pub expr: Option<Box<Spanned<Expr>>>,
|
pub init: Option<SpanBox<Expr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprLet {
|
impl Pretty for ExprLet {
|
||||||
fn pretty(&self, p: &mut Printer) {
|
fn pretty(&self, p: &mut Printer) {
|
||||||
p.push_str("#let ");
|
p.push_str("#let ");
|
||||||
p.push_str(&self.pat.v);
|
p.push_str(&self.pat.v);
|
||||||
if let Some(expr) = &self.expr {
|
if let Some(init) = &self.init {
|
||||||
p.push_str(" = ");
|
p.push_str(" = ");
|
||||||
expr.v.pretty(p);
|
init.v.pretty(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -485,11 +499,11 @@ impl Pretty for ExprLet {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprIf {
|
pub struct ExprIf {
|
||||||
/// The condition which selects the body to evaluate.
|
/// The condition which selects the body to evaluate.
|
||||||
pub condition: Box<Spanned<Expr>>,
|
pub condition: SpanBox<Expr>,
|
||||||
/// The expression to evaluate if the condition is true.
|
/// The expression to evaluate if the condition is true.
|
||||||
pub if_body: Box<Spanned<Expr>>,
|
pub if_body: SpanBox<Expr>,
|
||||||
/// The expression to evaluate if the condition is false.
|
/// The expression to evaluate if the condition is false.
|
||||||
pub else_body: Option<Box<Spanned<Expr>>>,
|
pub else_body: Option<SpanBox<Expr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for ExprIf {
|
impl Pretty for ExprIf {
|
||||||
@ -520,6 +534,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_pretty_print_expressions() {
|
fn test_pretty_print_expressions() {
|
||||||
// Unary and binary operations.
|
// Unary and binary operations.
|
||||||
|
test_pretty("{}", "{}");
|
||||||
test_pretty("{1 +}", "{1}");
|
test_pretty("{1 +}", "{1}");
|
||||||
test_pretty("{1++1}", "{1 + +1}");
|
test_pretty("{1++1}", "{1 + +1}");
|
||||||
test_pretty("{+-1}", "{+-1}");
|
test_pretty("{+-1}", "{+-1}");
|
||||||
|
@ -34,6 +34,9 @@ impl<T> Offset for SpanVec<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A box of a spanned value of type `T`.
|
||||||
|
pub type SpanBox<T> = Box<Spanned<T>>;
|
||||||
|
|
||||||
/// A value with the span it corresponds to in the source code.
|
/// A value with the span it corresponds to in the source code.
|
||||||
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 640 B After Width: | Height: | Size: 515 B |
Binary file not shown.
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.8 KiB |
@ -1,21 +1,17 @@
|
|||||||
|
// Empty.
|
||||||
|
{}
|
||||||
|
|
||||||
// Basic expression.
|
// Basic expression.
|
||||||
{1}
|
{1}
|
||||||
|
|
||||||
// Error: 1:2-1:2 expected expression
|
|
||||||
{}
|
|
||||||
|
|
||||||
// Bad expression.
|
// Bad expression.
|
||||||
// Error: 1:2-1:4 expected expression, found invalid token
|
// Error: 1:2-1:4 expected expression, found invalid token
|
||||||
{1u}
|
{1u}
|
||||||
|
|
||||||
// Two expressions are not allowed.
|
|
||||||
// Error: 1:4-1:5 unexpected integer
|
|
||||||
{2 3}
|
|
||||||
|
|
||||||
// Missing closing brace in nested block.
|
// Missing closing brace in nested block.
|
||||||
// Error: 1:5-1:5 expected closing brace
|
// Error: 1:5-1:5 expected closing brace
|
||||||
{({1) + 2}
|
{({1) + 1}
|
||||||
|
|
||||||
// Missing closing bracket in template expression.
|
// Missing closing bracket in template expression.
|
||||||
// Error: 1:11-1:11 expected closing bracket
|
// Error: 1:11-1:11 expected closing bracket
|
||||||
{[_] + [4_}
|
{[_] + [3_}
|
||||||
|
@ -33,18 +33,18 @@
|
|||||||
[f|f|f]
|
[f|f|f]
|
||||||
|
|
||||||
// With body.
|
// With body.
|
||||||
// Error: 1:6-1:7 expected function name, found integer
|
// Error: 1:6-1:7 expected identifier, found integer
|
||||||
[f | 1 | box][💕]
|
[f | 1 | box][💕]
|
||||||
|
|
||||||
// Error: 2:2-2:2 expected function name
|
// Error: 2:2-2:2 expected identifier
|
||||||
// Error: 1:3-1:3 expected function name
|
// Error: 1:3-1:3 expected identifier
|
||||||
[||f true]
|
[||f true]
|
||||||
|
|
||||||
// Error: 1:6-1:6 expected function name
|
// Error: 1:6-1:6 expected identifier
|
||||||
[f 1|]
|
[f 1|]
|
||||||
|
|
||||||
// Error: 2:2-2:2 expected function name
|
// Error: 2:2-2:2 expected identifier
|
||||||
// Error: 1:3-1:3 expected function name
|
// Error: 1:3-1:3 expected identifier
|
||||||
[|][Nope]
|
[|][Nope]
|
||||||
|
|
||||||
// Error: 2:5-2:5 expected closing paren
|
// Error: 2:5-2:5 expected closing paren
|
||||||
@ -80,16 +80,16 @@
|
|||||||
#let x = "string"
|
#let x = "string"
|
||||||
[x]
|
[x]
|
||||||
|
|
||||||
// Error: 1:2-1:3 expected function name, found invalid token
|
// Error: 1:2-1:3 expected identifier, found invalid token
|
||||||
[# 1]
|
[# 1]
|
||||||
|
|
||||||
// Error: 4:1-4:1 expected function name
|
// Error: 4:1-4:1 expected identifier
|
||||||
// Error: 3:1-3:1 expected closing bracket
|
// Error: 3:1-3:1 expected closing bracket
|
||||||
[
|
[
|
||||||
|
|
||||||
---
|
---
|
||||||
// Ref: false
|
// Ref: false
|
||||||
// Error: 2:2-2:3 expected function name, found closing paren
|
// Error: 2:2-2:3 expected identifier, found closing paren
|
||||||
// Error: 3:1-3:1 expected closing bracket
|
// Error: 3:1-3:1 expected closing bracket
|
||||||
[)
|
[)
|
||||||
|
|
||||||
|
@ -6,15 +6,18 @@
|
|||||||
// Braced condition is fine.
|
// Braced condition is fine.
|
||||||
#if {true} {"3"}
|
#if {true} {"3"}
|
||||||
|
|
||||||
// Newline between body and else-clause.
|
// Newlines.
|
||||||
#if false []
|
#if false [
|
||||||
#else [4]
|
|
||||||
|
] #else [
|
||||||
|
4
|
||||||
|
]
|
||||||
|
|
||||||
// Multiline (condition needs parens because it's terminated by the line break,
|
// Multiline (condition needs parens because it's terminated by the line break,
|
||||||
// just like the right-hand side of a let-binding).
|
// just like the right-hand side of a let-binding).
|
||||||
#if
|
#if (
|
||||||
x
|
x
|
||||||
{
|
) {
|
||||||
"Fi" + "ve"
|
"Fi" + "ve"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,17 +47,13 @@ a#if }
|
|||||||
// Error: 1:16-1:16 expected body
|
// Error: 1:16-1:16 expected body
|
||||||
a#if x b#if (x)c
|
a#if x b#if (x)c
|
||||||
|
|
||||||
// Needs if-body expression.
|
|
||||||
// Error: 1:12-1:12 expected expression
|
|
||||||
a#if true {}
|
|
||||||
|
|
||||||
// Needs else-body.
|
// Needs else-body.
|
||||||
// Error: 1:20-1:20 expected body
|
// Error: 1:20-1:20 expected body
|
||||||
a#if true [b] #else c
|
a#if true [b] #else c
|
||||||
|
|
||||||
// Lone else.
|
// Lone else.
|
||||||
// Error: 2:1-2:6 unexpected keyword `#else`
|
// Error: 2:1-2:6 unexpected keyword `#else`
|
||||||
// Error: 1:8-1:8 expected function name
|
// Error: 1:8-1:8 expected identifier
|
||||||
#else []
|
#else []
|
||||||
|
|
||||||
// Condition must be boolean. If it isn't, neither branch is evaluated.
|
// Condition must be boolean. If it isn't, neither branch is evaluated.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user