summaryrefslogtreecommitdiff
path: root/src/parse
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse')
-rw-r--r--src/parse/collection.rs69
-rw-r--r--src/parse/mod.rs260
-rw-r--r--src/parse/parser.rs139
-rw-r--r--src/parse/resolve.rs13
4 files changed, 246 insertions, 235 deletions
diff --git a/src/parse/collection.rs b/src/parse/collection.rs
index 95ca9847..162a8bd5 100644
--- a/src/parse/collection.rs
+++ b/src/parse/collection.rs
@@ -1,8 +1,10 @@
use super::*;
/// Parse the arguments to a function call.
-pub fn arguments(p: &mut Parser) -> ExprArgs {
- collection(p, vec![])
+pub fn args(p: &mut Parser) -> ExprArgs {
+ let start = p.start();
+ let items = collection(p, vec![]);
+ ExprArgs { span: p.span_from(start), items }
}
/// Parse a parenthesized group, which can be either of:
@@ -16,8 +18,8 @@ pub fn parenthesized(p: &mut Parser) -> Expr {
} else {
collection(p, State::Unknown)
};
- p.end_group();
- state.into_expr()
+ let span = p.end_group();
+ state.into_expr(span)
}
/// Parse a collection.
@@ -25,7 +27,7 @@ fn collection<T: Collection>(p: &mut Parser, mut collection: T) -> T {
let mut missing_coma = None;
while !p.eof() {
- if let Some(arg) = p.span_if(argument) {
+ if let Some(arg) = argument(p) {
collection.push_arg(p, arg);
if let Some(pos) = missing_coma.take() {
@@ -36,7 +38,7 @@ fn collection<T: Collection>(p: &mut Parser, mut collection: T) -> T {
break;
}
- let behind = p.last_end();
+ let behind = p.end();
if p.eat_if(Token::Comma) {
collection.push_comma();
} else {
@@ -50,14 +52,12 @@ fn collection<T: Collection>(p: &mut Parser, mut collection: T) -> T {
/// Parse an expression or a named pair.
fn argument(p: &mut Parser) -> Option<Argument> {
- let first = p.span_if(expr)?;
+ let first = expr(p)?;
if p.eat_if(Token::Colon) {
- if let Expr::Ident(ident) = first.v {
- let name = ident.with_span(first.span);
- let expr = p.span_if(expr)?;
- Some(Argument::Named(Named { name, expr }))
+ if let Expr::Ident(name) = first {
+ Some(Argument::Named(Named { name, expr: expr(p)? }))
} else {
- p.diag(error!(first.span, "expected identifier"));
+ p.diag(error!(first.span(), "expected identifier"));
expr(p);
None
}
@@ -68,13 +68,13 @@ fn argument(p: &mut Parser) -> Option<Argument> {
/// Abstraction for comma-separated list of expression / named pairs.
trait Collection {
- fn push_arg(&mut self, p: &mut Parser, arg: Spanned<Argument>);
+ fn push_arg(&mut self, p: &mut Parser, arg: Argument);
fn push_comma(&mut self) {}
}
-impl Collection for ExprArgs {
- fn push_arg(&mut self, _: &mut Parser, arg: Spanned<Argument>) {
- self.push(arg.v);
+impl Collection for Vec<Argument> {
+ fn push_arg(&mut self, _: &mut Parser, arg: Argument) {
+ self.push(arg);
}
}
@@ -82,38 +82,38 @@ impl Collection for ExprArgs {
#[derive(Debug)]
enum State {
Unknown,
- Expr(Spanned<Expr>),
- Array(ExprArray),
- Dict(ExprDict),
+ Expr(Expr),
+ Array(Vec<Expr>),
+ Dict(Vec<Named>),
}
impl State {
- fn into_expr(self) -> Expr {
+ fn into_expr(self, span: Span) -> Expr {
match self {
- Self::Unknown => Expr::Array(vec![]),
- Self::Expr(expr) => Expr::Group(Box::new(expr)),
- Self::Array(array) => Expr::Array(array),
- Self::Dict(dict) => Expr::Dict(dict),
+ Self::Unknown => Expr::Array(ExprArray { span, items: vec![] }),
+ Self::Expr(expr) => Expr::Group(ExprGroup { span, expr: Box::new(expr) }),
+ Self::Array(items) => Expr::Array(ExprArray { span, items }),
+ Self::Dict(items) => Expr::Dict(ExprDict { span, items }),
}
}
}
impl Collection for State {
- fn push_arg(&mut self, p: &mut Parser, arg: Spanned<Argument>) {
+ fn push_arg(&mut self, p: &mut Parser, arg: Argument) {
match self {
- Self::Unknown => match arg.v {
+ Self::Unknown => match arg {
Argument::Pos(expr) => *self = Self::Expr(expr),
Argument::Named(named) => *self = Self::Dict(vec![named]),
},
- Self::Expr(prev) => match arg.v {
+ Self::Expr(prev) => match arg {
Argument::Pos(expr) => *self = Self::Array(vec![take(prev), expr]),
Argument::Named(_) => diag(p, arg),
},
- Self::Array(array) => match arg.v {
+ Self::Array(array) => match arg {
Argument::Pos(expr) => array.push(expr),
Argument::Named(_) => diag(p, arg),
},
- Self::Dict(dict) => match arg.v {
+ Self::Dict(dict) => match arg {
Argument::Pos(_) => diag(p, arg),
Argument::Named(named) => dict.push(named),
},
@@ -127,13 +127,16 @@ impl Collection for State {
}
}
-fn take(expr: &mut Spanned<Expr>) -> Spanned<Expr> {
+fn take(expr: &mut Expr) -> Expr {
// Replace with anything, it's overwritten anyway.
- std::mem::replace(expr, Spanned::zero(Expr::Bool(false)))
+ std::mem::replace(
+ expr,
+ Expr::Lit(Lit { span: Span::ZERO, kind: LitKind::None }),
+ )
}
-fn diag(p: &mut Parser, arg: Spanned<Argument>) {
- p.diag(error!(arg.span, "{}", match arg.v {
+fn diag(p: &mut Parser, arg: Argument) {
+ p.diag(error!(arg.span(), "{}", match arg {
Argument::Pos(_) => "expected named pair, found expression",
Argument::Named(_) => "expected expression, found named pair",
}));
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 3fd2cca5..2c34d7b8 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -13,10 +13,11 @@ pub use resolve::*;
pub use scanner::*;
pub use tokens::*;
+use std::rc::Rc;
+
use crate::diag::Pass;
use crate::syntax::*;
-
-use collection::{arguments, parenthesized};
+use collection::{args, parenthesized};
/// Parse a string of source code.
pub fn parse(src: &str) -> Pass<Tree> {
@@ -31,8 +32,8 @@ fn tree(p: &mut Parser) -> Tree {
let mut at_start = true;
let mut tree = vec![];
while !p.eof() {
- if let Some(node) = p.span_if(|p| node(p, &mut at_start)) {
- if !matches!(node.v, Node::Parbreak | Node::Space) {
+ if let Some(node) = node(p, &mut at_start) {
+ if !matches!(node, Node::Parbreak | Node::Space) {
at_start = false;
}
tree.push(node);
@@ -78,12 +79,12 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
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.expected_at("semicolon or line break", p.end());
}
p.end_group();
// Uneat spaces we might have eaten eagerly.
- p.jump(p.last_end());
+ p.jump(p.end());
return expr.map(Node::Expr);
}
@@ -123,28 +124,24 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
/// Parse a heading.
fn heading(p: &mut Parser) -> Node {
- // Count depth.
- let mut level = p.span(|p| {
- p.assert(&[Token::Eq]);
+ let start = p.start();
+ p.assert(&[Token::Eq]);
- let mut level = 0u8;
- while p.eat_if(Token::Eq) {
- level = level.saturating_add(1);
- }
- level
- });
+ // Count depth.
+ let mut level: usize = 0;
+ while p.eat_if(Token::Eq) {
+ level += 1;
+ }
- if level.v > 5 {
- p.diag(warning!(level.span, "should not exceed depth 6"));
- level.v = 5;
+ if level > 5 {
+ p.diag(warning!(start .. p.end(), "should not exceed depth 6"));
+ level = 5;
}
// Parse the heading contents.
let mut contents = vec![];
while p.check(|t| !matches!(t, Token::Space(n) if n >= 1)) {
- if let Some(node) = p.span_if(|p| node(p, &mut false)) {
- contents.push(node);
- }
+ contents.extend(node(p, &mut false));
}
Node::Heading(NodeHeading { level, contents })
@@ -152,7 +149,7 @@ fn heading(p: &mut Parser) -> Node {
/// Handle a raw block.
fn raw(p: &mut Parser, token: TokenRaw) -> Node {
- let raw = resolve::resolve_raw(token.text, token.backticks);
+ let raw = resolve::resolve_raw(token.text, token.backticks, p.start());
if !token.terminated {
p.diag(error!(p.peek_span().end, "expected backtick(s)"));
}
@@ -183,10 +180,9 @@ fn bracket_call(p: &mut Parser) -> Option<Expr> {
// One header is guaranteed, but there may be more (through chaining).
let mut outer = vec![];
- let mut inner = p.span_if(bracket_subheader);
-
+ let mut inner = bracket_subheader(p);
while p.eat_if(Token::Pipe) {
- if let Some(new) = p.span_if(bracket_subheader) {
+ if let Some(new) = bracket_subheader(p) {
outer.extend(inner);
inner = Some(new);
}
@@ -194,49 +190,44 @@ fn bracket_call(p: &mut Parser) -> Option<Expr> {
p.end_group();
- let body = if p.peek() == Some(Token::LeftBracket) {
- Some(p.span(|p| Expr::Template(bracket_body(p))))
- } else {
- None
+ let body = match p.peek() {
+ Some(Token::LeftBracket) => Some(bracket_body(p)),
+ _ => None,
};
let mut inner = inner?;
if let Some(body) = body {
- inner.span.expand(body.span);
- inner.v.args.v.push(Argument::Pos(body));
+ inner.span.expand(body.span());
+ inner.args.items.push(Argument::Pos(body));
}
while let Some(mut top) = outer.pop() {
- let span = inner.span;
- let node = inner.map(|c| Node::Expr(Expr::Call(c)));
- let expr = Expr::Template(vec![node]).with_span(span);
- top.v.args.v.push(Argument::Pos(expr));
+ top.args.items.push(Argument::Pos(Expr::Call(inner)));
inner = top;
}
- Some(Expr::Call(inner.v))
+ Some(Expr::Call(inner))
}
/// Parse one subheader of a bracketed function call.
fn bracket_subheader(p: &mut Parser) -> Option<ExprCall> {
p.start_group(Group::Subheader, TokenMode::Code);
-
- let name = p.span_if(ident);
- let args = p.span(arguments);
- p.end_group();
-
+ let name = ident(p);
+ let args = args(p);
+ let span = p.end_group();
Some(ExprCall {
- callee: Box::new(name?.map(Expr::Ident)),
+ span,
+ callee: Box::new(Expr::Ident(name?)),
args,
})
}
/// Parse the body of a bracketed function call.
-fn bracket_body(p: &mut Parser) -> Tree {
+fn bracket_body(p: &mut Parser) -> Expr {
p.start_group(Group::Bracket, TokenMode::Markup);
- let tree = tree(p);
- p.end_group();
- tree
+ let tree = Rc::new(tree(p));
+ let span = p.end_group();
+ Expr::Template(ExprTemplate { span, tree })
}
/// Parse an expression.
@@ -246,15 +237,14 @@ fn expr(p: &mut Parser) -> Option<Expr> {
/// Parse an expression with operators having at least the minimum precedence.
fn expr_with(p: &mut Parser, min_prec: usize) -> Option<Expr> {
- let mut lhs = match p.span_if(|p| p.eat_map(UnOp::from_token)) {
+ let start = p.start();
+ let mut lhs = match p.eat_map(UnOp::from_token) {
Some(op) => {
- let prec = op.v.precedence();
- let expr = p.span_if(|p| expr_with(p, prec))?;
- let span = op.span.join(expr.span);
- let unary = Expr::Unary(ExprUnary { op, expr: Box::new(expr) });
- unary.with_span(span)
+ let prec = op.precedence();
+ let expr = Box::new(expr_with(p, prec)?);
+ Expr::Unary(ExprUnary { span: p.span_from(start), op, expr })
}
- None => p.span_if(primary)?,
+ None => primary(p)?,
};
loop {
@@ -268,95 +258,87 @@ fn expr_with(p: &mut Parser, min_prec: usize) -> Option<Expr> {
break;
}
+ p.eat();
match op.associativity() {
Associativity::Left => prec += 1,
Associativity::Right => {}
}
- let op = op.with_span(p.peek_span());
- p.eat();
-
- let rhs = match p.span_if(|p| expr_with(p, prec)) {
+ let rhs = match expr_with(p, prec) {
Some(rhs) => Box::new(rhs),
None => break,
};
- let span = lhs.span.join(rhs.span);
- let binary = Expr::Binary(ExprBinary { lhs: Box::new(lhs), op, rhs });
- lhs = binary.with_span(span);
+ let span = lhs.span().join(rhs.span());
+ lhs = Expr::Binary(ExprBinary { span, lhs: Box::new(lhs), op, rhs });
}
- Some(lhs.v)
+ Some(lhs)
}
/// Parse a primary expression.
fn primary(p: &mut Parser) -> Option<Expr> {
- let expr = match p.peek() {
- // Basic values.
- Some(Token::None) => Expr::None,
- Some(Token::Bool(b)) => Expr::Bool(b),
- Some(Token::Int(i)) => Expr::Int(i),
- Some(Token::Float(f)) => Expr::Float(f),
- Some(Token::Length(val, unit)) => Expr::Length(val, unit),
- Some(Token::Angle(val, unit)) => Expr::Angle(val, unit),
- Some(Token::Percent(p)) => Expr::Percent(p),
- Some(Token::Color(color)) => Expr::Color(color),
- Some(Token::Str(token)) => Expr::Str(string(p, token)),
+ if let Some(expr) = literal(p) {
+ return Some(expr);
+ }
+ match p.peek() {
// Function or identifier.
- Some(Token::Ident(id)) => {
- p.eat();
- let ident = Ident(id.into());
+ Some(Token::Ident(string)) => {
+ let ident = Ident {
+ span: p.eat_span(),
+ string: string.into(),
+ };
if p.peek() == Some(Token::LeftParen) {
- let name = ident.with_span(p.peek_span());
- return Some(paren_call(p, name));
+ Some(paren_call(p, ident))
} else {
- return Some(Expr::Ident(ident));
+ 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),
-
- // Block.
- Some(Token::LeftBrace) => {
- return block(p, true);
- }
-
- // Template.
- Some(Token::LeftBracket) => {
- return Some(template(p));
- }
+ Some(Token::Let) => expr_let(p),
+ Some(Token::If) => expr_if(p),
+ Some(Token::For) => expr_for(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));
- }
+ // Structures.
+ Some(Token::LeftBrace) => block(p, true),
+ Some(Token::LeftBracket) => Some(template(p)),
+ Some(Token::HashBracket) => bracket_call(p),
+ Some(Token::LeftParen) => Some(parenthesized(p)),
// Nothing.
_ => {
p.expected("expression");
- return None;
+ None
}
+ }
+}
+
+/// Parse a literal.
+fn literal(p: &mut Parser) -> Option<Expr> {
+ let kind = match p.peek()? {
+ // Basic values.
+ Token::None => LitKind::None,
+ Token::Bool(b) => LitKind::Bool(b),
+ Token::Int(i) => LitKind::Int(i),
+ Token::Float(f) => LitKind::Float(f),
+ Token::Length(val, unit) => LitKind::Length(val, unit),
+ Token::Angle(val, unit) => LitKind::Angle(val, unit),
+ Token::Percent(p) => LitKind::Percent(p),
+ Token::Color(color) => LitKind::Color(color),
+ Token::Str(token) => LitKind::Str(string(p, token)),
+ _ => return None,
};
- p.eat();
- Some(expr)
+ Some(Expr::Lit(Lit { span: p.eat_span(), kind }))
}
// Parse a template value: `[...]`.
fn template(p: &mut Parser) -> Expr {
p.start_group(Group::Bracket, TokenMode::Markup);
- let tree = tree(p);
- p.end_group();
- Expr::Template(tree)
+ let tree = Rc::new(tree(p));
+ let span = p.end_group();
+ Expr::Template(ExprTemplate { span, tree })
}
/// Parse a block expression: `{...}`.
@@ -365,26 +347,27 @@ fn block(p: &mut Parser, scopes: bool) -> Option<Expr> {
let mut exprs = vec![];
while !p.eof() {
p.start_group(Group::Stmt, TokenMode::Code);
- if let Some(expr) = p.span_if(expr) {
+ if let Some(expr) = expr(p) {
exprs.push(expr);
if !p.eof() {
- p.expected_at("semicolon or line break", p.last_end());
+ p.expected_at("semicolon or line break", p.end());
}
}
p.end_group();
p.skip_white();
}
- p.end_group();
- Some(Expr::Block(ExprBlock { exprs, scopes }))
+ let span = p.end_group();
+ Some(Expr::Block(ExprBlock { span, exprs, scoping: scopes }))
}
/// Parse a parenthesized function call.
-fn paren_call(p: &mut Parser, name: Spanned<Ident>) -> Expr {
+fn paren_call(p: &mut Parser, name: Ident) -> Expr {
p.start_group(Group::Paren, TokenMode::Code);
- let args = p.span(arguments);
+ let args = args(p);
p.end_group();
Expr::Call(ExprCall {
- callee: Box::new(name.map(Expr::Ident)),
+ span: p.span_from(name.span.start),
+ callee: Box::new(Expr::Ident(name)),
args,
})
}
@@ -394,22 +377,26 @@ fn string(p: &mut Parser, token: TokenStr) -> String {
if !token.terminated {
p.expected_at("quote", p.peek_span().end);
}
-
resolve::resolve_string(token.string)
}
/// Parse a let expression.
fn expr_let(p: &mut Parser) -> Option<Expr> {
+ let start = p.start();
p.assert(&[Token::Let]);
let mut expr_let = None;
- if let Some(pat) = p.span_if(ident) {
+ if let Some(binding) = ident(p) {
let mut init = None;
if p.eat_if(Token::Eq) {
- init = p.span_if(expr);
+ init = expr(p);
}
- expr_let = Some(Expr::Let(ExprLet { pat, init: init.map(Box::new) }))
+ expr_let = Some(Expr::Let(ExprLet {
+ span: p.span_from(start),
+ binding,
+ init: init.map(Box::new),
+ }))
}
expr_let
@@ -417,17 +404,19 @@ fn expr_let(p: &mut Parser) -> Option<Expr> {
/// Parse an if expresion.
fn expr_if(p: &mut Parser) -> Option<Expr> {
+ let start = p.start();
p.assert(&[Token::If]);
let mut expr_if = None;
- if let Some(condition) = p.span_if(expr) {
- if let Some(if_body) = p.span_if(body) {
+ if let Some(condition) = expr(p) {
+ if let Some(if_body) = body(p) {
let mut else_body = None;
if p.eat_if(Token::Else) {
- else_body = p.span_if(body);
+ else_body = body(p);
}
expr_if = Some(Expr::If(ExprIf {
+ span: p.span_from(start),
condition: Box::new(condition),
if_body: Box::new(if_body),
else_body: else_body.map(Box::new),
@@ -440,15 +429,17 @@ fn expr_if(p: &mut Parser) -> Option<Expr> {
/// Parse a for expression.
fn expr_for(p: &mut Parser) -> Option<Expr> {
+ let start = p.start();
p.assert(&[Token::For]);
let mut expr_for = None;
- if let Some(pat) = p.span_if(for_pattern) {
+ if let Some(pattern) = for_pattern(p) {
if p.expect(Token::In) {
- if let Some(iter) = p.span_if(expr) {
- if let Some(body) = p.span_if(body) {
+ if let Some(iter) = expr(p) {
+ if let Some(body) = body(p) {
expr_for = Some(Expr::For(ExprFor {
- pat,
+ span: p.span_from(start),
+ pattern,
iter: Box::new(iter),
body: Box::new(body),
}));
@@ -473,15 +464,14 @@ fn for_pattern(p: &mut Parser) -> Option<ForPattern> {
/// Parse an identifier.
fn ident(p: &mut Parser) -> Option<Ident> {
- match p.peek() {
- Some(Token::Ident(id)) => {
- p.eat();
- Some(Ident(id.into()))
- }
- _ => {
- p.expected("identifier");
- None
- }
+ if let Some(Token::Ident(string)) = p.peek() {
+ Some(Ident {
+ span: p.eat_span(),
+ string: string.to_string(),
+ })
+ } else {
+ p.expected("identifier");
+ None
}
}
@@ -491,7 +481,7 @@ fn body(p: &mut Parser) -> Option<Expr> {
Some(Token::LeftBracket) => Some(template(p)),
Some(Token::LeftBrace) => block(p, true),
_ => {
- p.expected_at("body", p.last_end());
+ p.expected_at("body", p.end());
None
}
}
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index 986a36b0..a64e39dd 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter};
use super::{Scanner, TokenMode, Tokens};
use crate::diag::Diag;
use crate::diag::{Deco, Feedback};
-use crate::syntax::{Pos, Span, Spanned, Token, WithSpan};
+use crate::syntax::{Pos, Span, Spanned, Token};
/// A convenient token-based parser.
pub struct Parser<'s> {
@@ -18,14 +18,43 @@ pub struct Parser<'s> {
next_start: Pos,
/// The end position of the last (non-whitespace if in code mode) token.
last_end: Pos,
- /// The stack of modes we were in.
- modes: Vec<TokenMode>,
/// The stack of open groups.
- groups: Vec<Group>,
+ groups: Vec<GroupEntry>,
/// Accumulated feedback.
feedback: Feedback,
}
+/// A logical group of tokens, e.g. `[...]`.
+struct GroupEntry {
+ /// The start position of the group. Used by `Parser::end_group` to return
+ /// The group's full span.
+ start: Pos,
+ /// The kind of group this is. This decides which tokens will end the group.
+ /// For example, a [`GroupKind::Paren`] will be ended by
+ /// [`Token::RightParen`].
+ kind: Group,
+ /// The mode the parser was in _before_ the group started.
+ prev_mode: TokenMode,
+}
+
+/// A group, confined by optional start and end delimiters.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum Group {
+ /// A parenthesized group: `(...)`.
+ Paren,
+ /// A bracketed group: `[...]`.
+ Bracket,
+ /// A curly-braced group: `{...}`.
+ Brace,
+ /// A group ended by a chained subheader or a closing bracket:
+ /// `... >>`, `...]`.
+ Subheader,
+ /// A group ended by a semicolon or a line break: `;`, `\n`.
+ Stmt,
+ /// A group for a single expression. Not ended by something specific.
+ Expr,
+}
+
impl<'s> Parser<'s> {
/// Create a new parser for the source string.
pub fn new(src: &'s str) -> Self {
@@ -37,7 +66,6 @@ impl<'s> Parser<'s> {
peeked: next,
next_start: Pos::ZERO,
last_end: Pos::ZERO,
- modes: vec![],
groups: vec![],
feedback: Feedback::new(),
}
@@ -97,14 +125,17 @@ impl<'s> Parser<'s> {
///
/// # Panics
/// This panics if the next token does not start the given group.
- pub fn start_group(&mut self, group: Group, mode: TokenMode) {
- self.modes.push(self.tokens.mode());
- self.tokens.set_mode(mode);
+ pub fn start_group(&mut self, kind: Group, mode: TokenMode) {
+ self.groups.push(GroupEntry {
+ start: self.next_start,
+ kind,
+ prev_mode: self.tokens.mode(),
+ });
- self.groups.push(group);
+ self.tokens.set_mode(mode);
self.repeek();
- match group {
+ match kind {
Group::Paren => self.assert(&[Token::LeftParen]),
Group::Bracket => self.assert(&[Token::HashBracket, Token::LeftBracket]),
Group::Brace => self.assert(&[Token::LeftBrace]),
@@ -118,15 +149,16 @@ impl<'s> Parser<'s> {
///
/// # Panics
/// This panics if no group was started.
- pub fn end_group(&mut self) {
+ pub fn end_group(&mut self) -> Span {
let prev_mode = self.tokens.mode();
- self.tokens.set_mode(self.modes.pop().expect("no pushed mode"));
-
let group = self.groups.pop().expect("no started group");
+ self.tokens.set_mode(group.prev_mode);
self.repeek();
+ let mut rescan = self.tokens.mode() != prev_mode;
+
// Eat the end delimiter if there is one.
- if let Some((end, required)) = match group {
+ if let Some((end, required)) = match group.kind {
Group::Paren => Some((Token::RightParen, true)),
Group::Bracket => Some((Token::RightBracket, true)),
Group::Brace => Some((Token::RightBrace, true)),
@@ -137,37 +169,19 @@ impl<'s> Parser<'s> {
if self.next == Some(end) {
// Bump the delimeter and return. No need to rescan in this case.
self.bump();
- return;
+ rescan = false;
} else if required {
self.diag(error!(self.next_start, "expected {}", end.name()));
}
}
// Rescan the peeked token if the mode changed.
- if self.tokens.mode() != prev_mode {
+ if rescan {
self.tokens.jump(self.last_end);
self.bump();
}
- }
-
- /// Execute `f` and return the result alongside the span of everything `f`
- /// ate. Excludes leading and trailing whitespace in code mode.
- pub fn span<T, F>(&mut self, f: F) -> Spanned<T>
- where
- F: FnOnce(&mut Self) -> T,
- {
- let start = self.next_start;
- let output = f(self);
- let end = self.last_end;
- output.with_span(start .. end)
- }
- /// A version of [`span`](Self::span) that works better with options.
- pub fn span_if<T, F>(&mut self, f: F) -> Option<Spanned<T>>
- where
- F: FnOnce(&mut Self) -> Option<T>,
- {
- self.span(f).transpose()
+ Span::new(group.start, self.last_end)
}
/// Consume the next token.
@@ -200,6 +214,13 @@ impl<'s> Parser<'s> {
mapped
}
+ /// Eat the next token and return its span.
+ pub fn eat_span(&mut self) -> Span {
+ let start = self.next_start;
+ self.eat();
+ Span::new(start, self.last_end)
+ }
+
/// Consume the next token if it is the given one and produce an error if
/// not.
pub fn expect(&mut self, t: Token) -> bool {
@@ -264,17 +285,22 @@ impl<'s> Parser<'s> {
}
/// The position at which the next token starts.
- pub fn next_start(&self) -> Pos {
+ pub fn start(&self) -> Pos {
self.next_start
}
/// The position at which the last token ended.
///
/// Refers to the end of the last _non-whitespace_ token in code mode.
- pub fn last_end(&self) -> Pos {
+ pub fn end(&self) -> Pos {
self.last_end
}
+ /// The span from
+ pub fn span_from(&self, start: Pos) -> Span {
+ Span::new(start, self.last_end)
+ }
+
/// Jump to a position in the source string.
pub fn jump(&mut self, pos: Pos) {
self.tokens.jump(pos);
@@ -325,13 +351,14 @@ impl<'s> Parser<'s> {
None => return,
};
+ let inside = |x| self.kinds().any(|k| k == x);
match token {
- Token::RightParen if self.groups.contains(&Group::Paren) => {}
- Token::RightBracket if self.groups.contains(&Group::Bracket) => {}
- Token::RightBrace if self.groups.contains(&Group::Brace) => {}
- Token::Semicolon if self.groups.contains(&Group::Stmt) => {}
+ Token::RightParen if inside(Group::Paren) => {}
+ Token::RightBracket if inside(Group::Bracket) => {}
+ Token::RightBrace if inside(Group::Brace) => {}
+ Token::Semicolon if inside(Group::Stmt) => {}
+ Token::Pipe if inside(Group::Subheader) => {}
Token::Space(n) if n >= 1 && self.in_line_group() => {}
- Token::Pipe if self.groups.contains(&Group::Subheader) => {}
_ => return,
}
@@ -340,7 +367,15 @@ impl<'s> Parser<'s> {
/// Whether the active group ends at a newline.
fn in_line_group(&self) -> bool {
- matches!(self.groups.last(), Some(&Group::Stmt) | Some(&Group::Expr))
+ matches!(
+ self.kinds().next_back(),
+ Some(Group::Stmt) | Some(Group::Expr)
+ )
+ }
+
+ /// The outer groups.
+ fn kinds(&self) -> impl DoubleEndedIterator<Item = Group> + '_ {
+ self.groups.iter().map(|group| group.kind)
}
}
@@ -350,21 +385,3 @@ impl Debug for Parser<'_> {
write!(f, "Parser({}|{})", s.eaten(), s.rest())
}
}
-
-/// A group, confined by optional start and end delimiters.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub enum Group {
- /// A parenthesized group: `(...)`.
- Paren,
- /// A bracketed group: `[...]`.
- Bracket,
- /// A curly-braced group: `{...}`.
- Brace,
- /// A group ended by a chained subheader or a closing bracket:
- /// `... >>`, `...]`.
- Subheader,
- /// A group ended by a semicolon or a line break: `;`, `\n`.
- Stmt,
- /// A group for a single expression. Not ended by something specific.
- Expr,
-}
diff --git a/src/parse/resolve.rs b/src/parse/resolve.rs
index a5e831da..4592acbc 100644
--- a/src/parse/resolve.rs
+++ b/src/parse/resolve.rs
@@ -1,5 +1,5 @@
use super::{is_newline, Scanner};
-use crate::syntax::{Ident, NodeRaw};
+use crate::syntax::{Ident, NodeRaw, Offset, Pos};
/// Resolve all escape sequences in a string.
pub fn resolve_string(string: &str) -> String {
@@ -47,12 +47,12 @@ pub fn resolve_hex(sequence: &str) -> Option<char> {
}
/// Resolve the language tag and trims the raw text.
-pub fn resolve_raw(text: &str, backticks: usize) -> NodeRaw {
+pub fn resolve_raw(text: &str, backticks: usize, start: Pos) -> NodeRaw {
if backticks > 1 {
let (tag, inner) = split_at_lang_tag(text);
let (lines, had_newline) = trim_and_split_raw(inner);
NodeRaw {
- lang: Ident::new(tag),
+ lang: Ident::new(tag, start .. start.offset(tag.len())),
lines,
block: had_newline,
}
@@ -125,6 +125,7 @@ pub fn split_lines(text: &str) -> Vec<String> {
#[cfg(test)]
#[rustfmt::skip]
mod tests {
+ use crate::syntax::Span;
use super::*;
#[test]
@@ -173,11 +174,11 @@ mod tests {
lines: &[&str],
block: bool,
) {
- assert_eq!(resolve_raw(raw, backticks), NodeRaw {
- lang: lang.map(|id| Ident(id.into())),
+ Span::without_cmp(|| assert_eq!(resolve_raw(raw, backticks, Pos(0)), NodeRaw {
+ lang: lang.and_then(|id| Ident::new(id, 0)),
lines: lines.iter().map(ToString::to_string).collect(),
block,
- });
+ }));
}
// Just one backtick.