use super::Parser;
use crate::ast::{Expr, Literal};
use crate::diagnostics::{Error, Result, Span, Spanned};
use crate::lexer::TokenKind;
use std::collections::HashMap;
impl Parser {
pub fn parse_expression_sequence(&mut self, delimiter: TokenKind) -> Result<Vec<Spanned<Expr>>> {
let mut expressions = Vec::new();
while !self.check(&delimiter) && !self.is_at_end() {
match self.parse_expression() {
Ok(expr) => {
expressions.push(expr);
self.panic_mode = false;
}
Err(err) => {
self.errors.push(*err);
if self.aggressive_recovery {
self.advance();
if self.errors.len() >= self.max_errors {
break;
}
} else {
return Err(Box::new(self.errors.last().unwrap().clone()))
}
}
}
self.skip_whitespace();
}
Ok(expressions)
}
pub fn parse_delimited_sequence<T, F>(
&mut self,
open: TokenKind,
close: TokenKind,
context: &str,
mut parser_fn: F,
) -> Result<T>
where
F: FnMut(&mut Self) -> Result<T>,
{
self.consume(&open, &format!("Expected {}", Self::token_kind_name(&open)))?;
self.nesting_depth += 1;
let result = self.with_context(context, |parser| {
parser.skip_whitespace();
parser_fn(parser)
});
self.nesting_depth -= 1;
match result {
Ok(value) => {
self.consume(&close, &format!("Expected {}", Self::token_kind_name(&close)))?;
Ok(value)
}
Err(err) => {
self.synchronize_to_closing_paren();
Err(err)
}
}
}
pub fn parse_optional_expression(&mut self) -> Result<Option<Spanned<Expr>>> {
if self.looks_like_expression_start() {
Ok(Some(self.parse_expression()?))
} else {
Ok(None)
}
}
pub fn looks_like_expression_start(&self) -> bool {
if self.is_at_end() {
return false;
}
matches!(self.current_token().kind,
TokenKind::LeftParen |
TokenKind::IntegerNumber | TokenKind::RealNumber |
TokenKind::RationalNumber | TokenKind::ComplexNumber |
TokenKind::String | TokenKind::Character | TokenKind::Boolean |
TokenKind::Identifier | TokenKind::Keyword |
TokenKind::Quote | TokenKind::Quasiquote)
}
pub fn parse_comma_separated_expressions(&mut self) -> Result<Vec<Spanned<Expr>>> {
let mut expressions = Vec::new();
if self.looks_like_expression_start() {
expressions.push(self.parse_expression()?);
while self.check(&TokenKind::Unquote) { self.advance(); self.skip_whitespace();
if !self.looks_like_expression_start() {
return Err(Box::new(Error::parse_error(
"Expected expression after comma",
self.current_span(),
)))
}
expressions.push(self.parse_expression()?);
}
}
Ok(expressions)
}
pub fn expect_identifier<'a>(&self, expr: &'a Spanned<Expr>) -> Result<&'a str> {
match &expr.inner {
Expr::Identifier(name) => {
Self::validate_identifier(name, expr.span)?;
Ok(name)
}
_ => Err(Box::new(Error::parse_error(
"Expected identifier",
expr.span,
)))
}
}
pub fn expect_literal<'a>(&self, expr: &'a Spanned<Expr>) -> Result<&'a Literal> {
match &expr.inner {
Expr::Literal(lit) => Ok(lit),
_ => Err(Box::new(Error::parse_error(
"Expected literal value",
expr.span,
)))
}
}
pub fn make_nil(&self, span: Span) -> Spanned<Expr> {
Spanned::new(Expr::Literal(Literal::Nil), span)
}
pub fn make_boolean(&self, value: bool, span: Span) -> Spanned<Expr> {
Spanned::new(Expr::Literal(Literal::Boolean(value)), span)
}
pub fn make_identifier(&self, name: String, span: Span) -> Result<Spanned<Expr>> {
Self::validate_identifier(&name, span)?;
Ok(Spanned::new(Expr::Identifier(name), span))
}
pub fn try_parse_expression(&mut self) -> Option<Spanned<Expr>> {
match self.parse_expression() {
Ok(expr) => {
self.panic_mode = false;
Some(expr)
}
Err(err) => {
self.errors.push(*err);
if self.aggressive_recovery && self.can_recover_at_current_position() {
if !self.is_at_end() {
self.advance();
}
}
None
}
}
}
pub fn token_kind_name(kind: &TokenKind) -> &'static str {
match kind {
TokenKind::LeftParen => "opening parenthesis '('",
TokenKind::RightParen => "closing parenthesis ')'",
TokenKind::LeftBracket => "opening bracket '['",
TokenKind::RightBracket => "closing bracket ']'",
TokenKind::LeftBrace => "opening brace '{'",
TokenKind::RightBrace => "closing brace '}'",
TokenKind::Quote => "quote '",
TokenKind::Quasiquote => "quasiquote `",
TokenKind::Unquote => "unquote ,",
TokenKind::UnquoteSplicing => "unquote-splicing ,@",
TokenKind::Dot => "dot",
TokenKind::TypeAnnotation => "type annotation ::",
TokenKind::IntegerNumber => "integer",
TokenKind::RealNumber => "real number",
TokenKind::RationalNumber => "rational number",
TokenKind::ComplexNumber => "complex number",
TokenKind::String => "string",
TokenKind::Character => "character",
TokenKind::Boolean => "boolean",
TokenKind::Keyword => "keyword",
TokenKind::Identifier => "identifier",
TokenKind::LineComment => "line comment",
TokenKind::BlockComment => "block comment",
TokenKind::Eof => "end of file",
TokenKind::Error => "error token",
}
}
pub fn parse_binding_pattern(&mut self) -> Result<String> {
if !self.check(&TokenKind::Identifier) {
return Err(Box::new(Error::parse_error(
"Expected identifier in binding pattern",
self.current_span(),
)))
}
let name = self.current_token().text.clone();
Self::validate_identifier(&name, self.current_span())?;
self.advance();
Ok(name)
}
pub fn check_binding_conflict(&self, _name: &str, _span: Span) -> Result<()> {
Ok(())
}
pub fn parse_optional_metadata(&mut self) -> Result<HashMap<String, Spanned<Expr>>> {
if self.check(&TokenKind::Keyword) {
self.parse_metadata_exprs()
} else {
Ok(HashMap::new())
}
}
pub fn expect_token(&mut self, expected: TokenKind, context: &str) -> Result<()> {
if self.check(&expected) {
self.advance();
Ok(())
} else {
Err(Box::new(Error::parse_error(
format!(
"Expected {} in {}, found {}",
Self::token_kind_name(&expected),
context,
self.current_token_text()
),
self.current_span(),
)))
}
}
pub fn skip_to_synchronization_point(&mut self, targets: &[TokenKind]) {
while !self.is_at_end() {
if targets.contains(&self.current_token().kind) {
break;
}
match self.current_token().kind {
TokenKind::LeftParen | TokenKind::RightParen | TokenKind::Eof => break,
_ => { self.advance(); },
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::diagnostics::Span;
use crate::lexer::{Lexer, TokenKind};
fn create_test_parser(source: &str) -> Parser {
let mut lexer = Lexer::new(source, Some("test"));
let tokens = lexer.tokenize().unwrap();
Parser::new(tokens)
}
#[test]
fn test_looks_like_expression_start() {
let mut parser = create_test_parser("42 foo (+ 1 2)");
assert!(parser.looks_like_expression_start()); parser.advance();
assert!(parser.looks_like_expression_start()); parser.advance();
assert!(parser.looks_like_expression_start()); }
#[test]
fn test_token_kind_name() {
assert_eq!(Parser::token_kind_name(&TokenKind::LeftParen), "opening parenthesis '('");
assert_eq!(Parser::token_kind_name(&TokenKind::Identifier), "identifier");
assert_eq!(Parser::token_kind_name(&TokenKind::String), "string");
}
#[test]
fn test_expect_identifier() {
let parser = create_test_parser("test");
let span = Span::new(0, 4);
let expr = Spanned::new(Expr::Identifier("test".to_string()), span);
assert_eq!(parser.expect_identifier(&expr).unwrap(), "test");
let non_id = Spanned::new(Expr::Literal(Literal::Number(42.0)), span);
assert!(parser.expect_identifier(&non_id).is_err());
}
#[test]
fn test_make_helper_functions() {
let parser = create_test_parser("");
let span = Span::new(0, 1);
let nil = parser.make_nil(span);
assert!(matches!(nil.inner, Expr::Literal(Literal::Nil)));
let bool_true = parser.make_boolean(true, span);
assert!(matches!(bool_true.inner, Expr::Literal(Literal::Boolean(true))));
let ident = parser.make_identifier("test".to_string(), span).unwrap();
assert!(matches!(ident.inner, Expr::Identifier(ref name) if name == "test"));
}
}