diff --git a/src/eval/mod.rs b/src/eval/mod.rs index c604f2d0a..8563520eb 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -270,8 +270,8 @@ impl Spanned<&ExprBinary> { // Short-circuit boolean operations. match (self.v.op.v, &lhs) { - (BinOp::And, Value::Bool(false)) => return Value::Bool(false), - (BinOp::Or, Value::Bool(true)) => return Value::Bool(true), + (BinOp::And, Value::Bool(false)) => return lhs, + (BinOp::Or, Value::Bool(true)) => return lhs, _ => {} } @@ -310,12 +310,10 @@ impl Spanned<&ExprBinary> { let lhs = std::mem::replace(slot, Value::None); *slot = op(lhs, rhs); return Value::None; + } else if ctx.scopes.is_const(id) { + ctx.diag(error!(span, "cannot assign to constant")); } else { - if ctx.scopes.is_const(id) { - ctx.diag(error!(span, "cannot assign to constant")); - } else { - ctx.diag(error!(span, "unknown variable")); - } + ctx.diag(error!(span, "unknown variable")); } } else { ctx.diag(error!(span, "cannot assign to this expression")); diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 00512c3f0..9fe8e62e2 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -380,42 +380,50 @@ fn stmt_let(p: &mut Parser) -> Option { p.start_group(Group::Stmt, TokenMode::Code); p.eat_assert(Token::Let); - let pat = p.span_if(ident); - let mut rhs = None; - - if pat.is_some() { - if p.eat_if(Token::Eq) { - rhs = p.span_if(expr); + let pat = match p.span_if(ident) { + Some(pat) => pat, + None => { + p.expected("identifier"); + p.end_group(); + return None; } + }; - if !p.eof() { - p.expected_at("semicolon or line break", p.last_end()); - } - } else { - p.expected("identifier"); + 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(); - Some(Expr::Let(ExprLet { pat: pat?, expr: rhs.map(Box::new) })) + Some(Expr::Let(ExprLet { pat, expr: rhs.map(Box::new) })) } /// Parse an if expresion. fn expr_if(p: &mut Parser) -> Option { p.start_group(Group::Expr, TokenMode::Code); p.eat_assert(Token::If); - let condition = p.span_if(expr); + + let condition = match p.span_if(expr) { + Some(condition) => Box::new(condition), + None => { + p.end_group(); + return None; + } + }; + p.end_group(); - let condition = Box::new(condition?); let if_body = Box::new(control_body(p)?); - let end = p.last_end(); + + let start = p.last_end(); p.skip_white(); let else_body = if p.eat_if(Token::Else) { control_body(p).map(Box::new) } else { - p.jump(end); + p.jump(start); None }; diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index 312e941b9..f5e5baaf9 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -127,7 +127,6 @@ impl<'s> Iterator for Tokens<'s> { '=' => Token::Eq, '<' => Token::Lt, '>' => Token::Gt, - '?' => Token::Question, // Identifiers. c if is_id_start(c) => self.ident(start), @@ -206,7 +205,6 @@ impl<'s> Tokens<'s> { "#if" => Token::If, "#else" => Token::Else, "#for" => Token::For, - "#in" => Token::In, "#while" => Token::While, "#break" => Token::Break, "#continue" => Token::Continue, @@ -612,7 +610,6 @@ mod tests { t!(Code: "-=" => HyphEq); t!(Code: "*=" => StarEq); t!(Code: "/=" => SlashEq); - t!(Code: "?" => Question); t!(Code: ".." => Dots); t!(Code: "=>" => Arrow); @@ -636,7 +633,6 @@ mod tests { ("if", If), ("else", Else), ("for", For), - ("in", In), ("while", While), ("break", Break), ("continue", Continue), @@ -654,6 +650,7 @@ mod tests { ("not", Not), ("and", And), ("or", Or), + ("in", In), ("none", Token::None), ("false", Bool(false)), ("true", Bool(true)), diff --git a/src/syntax/token.rs b/src/syntax/token.rs index 43415198c..432b4dc5b 100644 --- a/src/syntax/token.rs +++ b/src/syntax/token.rs @@ -61,8 +61,6 @@ pub enum Token<'s> { StarEq, /// A slash followed by an equals sign: `/=`. SlashEq, - /// A question mark: `?`. - Question, /// Two dots: `..`. Dots, /// An equals sign followed by a greater-than sign: `=>`. @@ -212,30 +210,29 @@ impl<'s> Token<'s> { Self::Eq => "assignment operator", Self::EqEq => "equality operator", Self::BangEq => "inequality operator", - Self::Lt => "less than operator", - Self::LtEq => "less than or equal operator", - Self::Gt => "greater than operator", - Self::GtEq => "greater than or equal operator", + Self::Lt => "less-than operator", + Self::LtEq => "less-than or equal operator", + Self::Gt => "greater-than operator", + Self::GtEq => "greater-than or equal operator", Self::PlusEq => "add-assign operator", Self::HyphEq => "subtract-assign operator", Self::StarEq => "multiply-assign operator", Self::SlashEq => "divide-assign operator", - Self::Question => "question mark", Self::Dots => "dots", Self::Arrow => "arrow", - Self::Not => "not operator", - Self::And => "and operator", - Self::Or => "or operator", - Self::Let => "let keyword", - Self::If => "if keyword", - Self::Else => "else keyword", - Self::For => "for keyword", - Self::In => "in keyword", - Self::While => "while keyword", - Self::Break => "break keyword", - Self::Continue => "continue keyword", - Self::Return => "return keyword", - Self::None => "none", + Self::Not => "operator `not`", + Self::And => "operator `and`", + Self::Or => "operator `or`", + Self::Let => "keyword `let`", + Self::If => "keyword `if`", + Self::Else => "keyword `else`", + Self::For => "keyword `for`", + Self::In => "keyword `in`", + Self::While => "keyword `while`", + Self::Break => "keyword `break`", + Self::Continue => "keyword `continue`", + Self::Return => "keyword `return`", + Self::None => "`none`", Self::Space(_) => "space", Self::Text(_) => "text", Self::Raw(_) => "raw block", diff --git a/tests/lang/typ/if.typ b/tests/lang/typ/if.typ index 5f4c986a7..f7de7716b 100644 --- a/tests/lang/typ/if.typ +++ b/tests/lang/typ/if.typ @@ -18,7 +18,7 @@ "Fi" + "ve" } -// Spacing is somewhat delicated. We only want to have spacing in the output if +// Spacing is somewhat delicate. We only want to have spacing in the output if // there was whitespace before/after the full if-else statement. In particular, // spacing after a simple if should be retained, but spacing between the first // body and the else should be ignored. @@ -53,7 +53,7 @@ a#if true {} a#if true [b] #else c // Lone else. -// Error: 2:1-2:6 unexpected else keyword +// Error: 2:1-2:6 unexpected keyword `else` // Error: 1:8-1:8 expected function name #else [] diff --git a/tests/lang/typ/let.typ b/tests/lang/typ/let.typ index 7e9246bb9..b3eadeaa2 100644 --- a/tests/lang/typ/let.typ +++ b/tests/lang/typ/let.typ @@ -28,11 +28,15 @@ // Error: 1:6-1:7 expected identifier, found integer #let 1 = 2 -// Terminated by end of line before binding name. +// Missing binding name. // Error: 1:5-1:5 expected identifier #let x = 5 +// Missing right-hand side. +// Error: 1:9-1:9 expected expression +#let a = + // No name at all. // Error: 1:11-1:11 expected identifier The Fi#let;rst diff --git a/tests/typeset.rs b/tests/typeset.rs index dbcb65173..620e11a6a 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -254,7 +254,7 @@ fn parse_metadata(src: &str, map: &LineMap) -> (bool, SpanVec) { let pos = |s: &mut Scanner| -> Pos { let (delta, _, column) = (num(s), s.eat_assert(':'), num(s)); let line = i as u32 + 1 + delta; - map.pos(Location { line, column }).unwrap() + map.pos(Location::new(line, column)).unwrap() }; let mut s = Scanner::new(rest);