use crate::cst::{Item, ListKind, Node};
use crate::lexer::{Lexer, ParseError, Token, TokenKind};
pub struct Parser {
lexer: Lexer,
peeked: Option<Token>,
}
impl Parser {
pub fn new(src: &str) -> Self {
Parser {
lexer: Lexer::new(src),
peeked: None,
}
}
fn next(&mut self) -> Result<Option<Token>, ParseError> {
if let Some(t) = self.peeked.take() {
return Ok(Some(t));
}
self.lexer.next_token()
}
fn peek(&mut self) -> Result<Option<&Token>, ParseError> {
if self.peeked.is_none() {
self.peeked = self.lexer.next_token()?;
}
Ok(self.peeked.as_ref())
}
pub fn parse_items(
&mut self,
terminator: Option<char>,
open_pos: (u32, u32),
) -> Result<Vec<Item>, ParseError> {
let mut items = Vec::new();
loop {
let Some(tok) = self.next()? else {
return if terminator.is_none() {
Ok(items)
} else {
Err(ParseError {
message: "unclosed list".into(),
line: open_pos.0,
col: open_pos.1,
})
};
};
match tok.kind {
TokenKind::Ws => items.push(Item::Ws(tok.text)),
TokenKind::LineComment => items.push(Item::LineComment(tok.text)),
TokenKind::BlockComment => items.push(Item::BlockComment(tok.text)),
TokenKind::DatumCommentStart => {
let (line, col) = (tok.line, tok.col);
let mut text = tok.text;
self.collect_trivia(&mut text)?;
let datum = self.parse_datum(line, col)?;
text.push_str(&datum.to_source());
items.push(Item::DatumComment(text));
}
TokenKind::Close(c) => {
return if terminator == Some(c) {
Ok(items)
} else {
Err(ParseError {
message: format!("unexpected `{c}`"),
line: tok.line,
col: tok.col,
})
};
}
_ => {
let node = self.parse_node_from(tok)?;
items.push(Item::Node(node));
}
}
}
}
fn parse_node_from(&mut self, tok: Token) -> Result<Node, ParseError> {
match tok.kind {
TokenKind::Open(kind) => {
let close = match kind {
ListKind::Bracket => ']',
_ => ')',
};
let items = self.parse_items(Some(close), (tok.line, tok.col))?;
Ok(Node::List { kind, items })
}
TokenKind::Atom => Ok(Node::Atom(tok.text)),
TokenKind::Str => Ok(Node::Str(tok.text)),
TokenKind::Prefix => {
let (line, col) = (tok.line, tok.col);
let mut prefix = tok.text;
self.collect_trivia(&mut prefix)?;
let inner = self.parse_datum(line, col)?;
Ok(Node::Prefixed {
prefix,
inner: Box::new(inner),
})
}
_ => Err(ParseError {
message: "expected datum".into(),
line: tok.line,
col: tok.col,
}),
}
}
fn parse_datum(&mut self, line: u32, col: u32) -> Result<Node, ParseError> {
let Some(tok) = self.next()? else {
return Err(ParseError {
message: "expected datum".into(),
line,
col,
});
};
self.parse_node_from(tok)
}
fn collect_trivia(&mut self, out: &mut String) -> Result<(), ParseError> {
loop {
let Some(t) = self.peek()? else {
return Ok(());
};
match t.kind {
TokenKind::Ws | TokenKind::LineComment | TokenKind::BlockComment => {
if let Some(t) = self.next()? {
out.push_str(&t.text);
}
}
TokenKind::DatumCommentStart => {
let Some(t) = self.next()? else {
return Ok(());
};
let (line, col) = (t.line, t.col);
out.push_str(&t.text);
self.collect_trivia(out)?;
let datum = self.parse_datum(line, col)?;
out.push_str(&datum.to_source());
}
_ => return Ok(()),
}
}
}
}