Refactor Binder to use Diagnostics
The Binder and its helper functions have been updated to accept and propagate a `Diagnostics` struct. This allows for better error reporting during the binding phase, enabling the compiler to continue processing even after encountering errors and collect all issues before halting. The `BoundKind::Error` node and `StaticType::Error` are introduced as "poison" nodes/types. These nodes indicate that an error occurred during compilation, preventing further valid processing of that specific AST fragment but allowing the compiler to continue with other parts of the code. The `Parser` has also been updated to return a `Diagnostics` struct, enabling it to report errors during the initial parsing stage while still attempting to build a partial AST. This adheres to the same error recovery strategy.
This commit is contained in:
+207
-135
@@ -1,33 +1,55 @@
|
||||
use crate::ast::lexer::{Lexer, Token, TokenKind};
|
||||
use crate::ast::nodes::{Node, Symbol, UntypedKind};
|
||||
use crate::ast::types::{Identity, Keyword, NodeIdentity, Value};
|
||||
use crate::ast::diagnostics::Diagnostics;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct Parser<'a> {
|
||||
lexer: Lexer<'a>,
|
||||
current_token: Token,
|
||||
pub diagnostics: Diagnostics,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
pub fn new(input: &'a str) -> Result<Self, String> {
|
||||
pub fn new(input: &'a str) -> Self {
|
||||
let mut lexer = Lexer::new(input);
|
||||
let current_token = lexer.next_token()?;
|
||||
Ok(Self {
|
||||
let mut diagnostics = Diagnostics::new();
|
||||
let current_token = match lexer.next_token() {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
diagnostics.push_error(e, None);
|
||||
Token {
|
||||
kind: TokenKind::EOF,
|
||||
location: crate::ast::types::SourceLocation { line: 1, col: 1 },
|
||||
}
|
||||
}
|
||||
};
|
||||
Self {
|
||||
lexer,
|
||||
current_token,
|
||||
})
|
||||
diagnostics,
|
||||
}
|
||||
}
|
||||
|
||||
fn advance(&mut self) -> Result<Token, String> {
|
||||
let prev = std::mem::replace(&mut self.current_token, self.lexer.next_token()?);
|
||||
Ok(prev)
|
||||
fn advance(&mut self) -> Token {
|
||||
let next = match self.lexer.next_token() {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
self.diagnostics.push_error(e, Some(NodeIdentity::new(self.current_token.location)));
|
||||
Token {
|
||||
kind: TokenKind::EOF,
|
||||
location: self.current_token.location,
|
||||
}
|
||||
}
|
||||
};
|
||||
std::mem::replace(&mut self.current_token, next)
|
||||
}
|
||||
|
||||
fn peek(&self) -> &TokenKind {
|
||||
&self.current_token.kind
|
||||
}
|
||||
|
||||
pub fn parse_expression(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||
pub fn parse_expression(&mut self) -> Node<UntypedKind> {
|
||||
let token_loc = self.current_token.location;
|
||||
let identity = NodeIdentity::new(token_loc);
|
||||
|
||||
@@ -36,9 +58,9 @@ impl<'a> Parser<'a> {
|
||||
TokenKind::LeftBracket => self.parse_vector_literal(),
|
||||
TokenKind::LeftBrace => self.parse_record_literal(),
|
||||
TokenKind::Quote => {
|
||||
self.advance()?; // consume '
|
||||
let expr = self.parse_expression()?;
|
||||
Ok(Node {
|
||||
self.advance(); // consume '
|
||||
let expr = self.parse_expression();
|
||||
Node {
|
||||
identity: identity.clone(),
|
||||
kind: UntypedKind::Call {
|
||||
callee: Box::new(self.make_id_node("quote", identity.clone())),
|
||||
@@ -51,34 +73,34 @@ impl<'a> Parser<'a> {
|
||||
}),
|
||||
},
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
TokenKind::Backtick => {
|
||||
self.advance()?; // consume `
|
||||
let expr = self.parse_expression()?;
|
||||
Ok(Node {
|
||||
self.advance(); // consume `
|
||||
let expr = self.parse_expression();
|
||||
Node {
|
||||
identity,
|
||||
kind: UntypedKind::Template(Box::new(expr)),
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
TokenKind::Tilde => {
|
||||
self.advance()?; // consume ~
|
||||
self.advance(); // consume ~
|
||||
if *self.peek() == TokenKind::At {
|
||||
self.advance()?; // consume @
|
||||
let expr = self.parse_expression()?;
|
||||
Ok(Node {
|
||||
self.advance(); // consume @
|
||||
let expr = self.parse_expression();
|
||||
Node {
|
||||
identity,
|
||||
kind: UntypedKind::Splice(Box::new(expr)),
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
let expr = self.parse_expression()?;
|
||||
Ok(Node {
|
||||
let expr = self.parse_expression();
|
||||
Node {
|
||||
identity,
|
||||
kind: UntypedKind::Placeholder(Box::new(expr)),
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => self.parse_atom(),
|
||||
@@ -89,8 +111,22 @@ impl<'a> Parser<'a> {
|
||||
matches!(self.current_token.kind, TokenKind::EOF)
|
||||
}
|
||||
|
||||
fn parse_atom(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||
let token = self.advance()?;
|
||||
fn synchronize(&mut self) {
|
||||
while !self.at_eof() {
|
||||
match self.peek() {
|
||||
TokenKind::RightParen | TokenKind::RightBracket | TokenKind::RightBrace => {
|
||||
self.advance();
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
self.advance();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_atom(&mut self) -> Node<UntypedKind> {
|
||||
let token = self.advance();
|
||||
let identity = NodeIdentity::new(token.location);
|
||||
|
||||
let kind = match token.kind {
|
||||
@@ -105,35 +141,43 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
_ => UntypedKind::Identifier(id.into()),
|
||||
},
|
||||
TokenKind::EOF => UntypedKind::Error, // Error already logged by advance
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"Unexpected token in atom: {:?} at {:?}",
|
||||
token.kind, token.location
|
||||
));
|
||||
self.diagnostics.push_error(
|
||||
format!("Unexpected token in atom: {:?}", token.kind),
|
||||
Some(identity.clone()),
|
||||
);
|
||||
UntypedKind::Error
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Node {
|
||||
Node {
|
||||
identity,
|
||||
kind,
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_list(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||
let start_loc = self.advance()?.location; // consume '('
|
||||
fn parse_list(&mut self) -> Node<UntypedKind> {
|
||||
let start_loc = self.advance().location; // consume '('
|
||||
let identity = NodeIdentity::new(start_loc);
|
||||
|
||||
if *self.peek() == TokenKind::RightParen {
|
||||
return Err(format!(
|
||||
"Empty list () is not a valid expression at {:?}",
|
||||
start_loc
|
||||
));
|
||||
self.diagnostics.push_error(
|
||||
"Empty list () is not a valid expression",
|
||||
Some(identity.clone()),
|
||||
);
|
||||
self.advance(); // consume )
|
||||
return Node {
|
||||
identity,
|
||||
kind: UntypedKind::Error,
|
||||
ty: (),
|
||||
};
|
||||
}
|
||||
|
||||
let head = self.parse_expression()?;
|
||||
let head = self.parse_expression();
|
||||
|
||||
let result = if let UntypedKind::Identifier(ref sym) = head.kind {
|
||||
let node = if let UntypedKind::Identifier(ref sym) = head.kind {
|
||||
match sym.name.as_ref() {
|
||||
"if" => self.parse_if(identity),
|
||||
"fn" => self.parse_fn(identity),
|
||||
@@ -149,15 +193,15 @@ impl<'a> Parser<'a> {
|
||||
self.parse_call(head, identity)
|
||||
};
|
||||
|
||||
self.expect(TokenKind::RightParen)?;
|
||||
result
|
||||
self.expect(TokenKind::RightParen);
|
||||
node
|
||||
}
|
||||
|
||||
fn parse_again(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||
fn parse_again(&mut self, identity: Identity) -> Node<UntypedKind> {
|
||||
let mut elements = Vec::new();
|
||||
|
||||
while *self.peek() != TokenKind::RightParen && *self.peek() != TokenKind::EOF {
|
||||
elements.push(self.parse_expression()?);
|
||||
elements.push(self.parse_expression());
|
||||
}
|
||||
|
||||
let args_node = Node {
|
||||
@@ -166,25 +210,25 @@ impl<'a> Parser<'a> {
|
||||
ty: (),
|
||||
};
|
||||
|
||||
Ok(Node {
|
||||
Node {
|
||||
identity,
|
||||
kind: UntypedKind::Again {
|
||||
args: Box::new(args_node),
|
||||
},
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_if(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||
let cond = Box::new(self.parse_expression()?);
|
||||
let then_br = Box::new(self.parse_expression()?);
|
||||
fn parse_if(&mut self, identity: Identity) -> Node<UntypedKind> {
|
||||
let cond = Box::new(self.parse_expression());
|
||||
let then_br = Box::new(self.parse_expression());
|
||||
let mut else_br = None;
|
||||
|
||||
if *self.peek() != TokenKind::RightParen {
|
||||
else_br = Some(Box::new(self.parse_expression()?));
|
||||
else_br = Some(Box::new(self.parse_expression()));
|
||||
}
|
||||
|
||||
Ok(Node {
|
||||
Node {
|
||||
identity,
|
||||
kind: UntypedKind::If {
|
||||
cond,
|
||||
@@ -192,82 +236,88 @@ impl<'a> Parser<'a> {
|
||||
else_br,
|
||||
},
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_def(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||
let target = Box::new(self.parse_pattern()?);
|
||||
let value = Box::new(self.parse_expression()?);
|
||||
fn parse_def(&mut self, identity: Identity) -> Node<UntypedKind> {
|
||||
let target = Box::new(self.parse_pattern());
|
||||
let value = Box::new(self.parse_expression());
|
||||
|
||||
Ok(Node {
|
||||
Node {
|
||||
identity,
|
||||
kind: UntypedKind::Def { target, value },
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_assign(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||
fn parse_assign(&mut self, identity: Identity) -> Node<UntypedKind> {
|
||||
// (assign target value)
|
||||
let target = Box::new(self.parse_expression()?);
|
||||
let value = Box::new(self.parse_expression()?);
|
||||
let target = Box::new(self.parse_expression());
|
||||
let value = Box::new(self.parse_expression());
|
||||
|
||||
Ok(Node {
|
||||
Node {
|
||||
identity,
|
||||
kind: UntypedKind::Assign { target, value },
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_do(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||
fn parse_do(&mut self, identity: Identity) -> Node<UntypedKind> {
|
||||
let mut exprs = Vec::new();
|
||||
while *self.peek() != TokenKind::RightParen && *self.peek() != TokenKind::EOF {
|
||||
exprs.push(self.parse_expression()?);
|
||||
exprs.push(self.parse_expression());
|
||||
}
|
||||
Ok(Node {
|
||||
Node {
|
||||
identity,
|
||||
kind: UntypedKind::Block { exprs },
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_pipe(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||
let inputs_node = self.parse_expression()?;
|
||||
fn parse_pipe(&mut self, identity: Identity) -> Node<UntypedKind> {
|
||||
let inputs_node = self.parse_expression();
|
||||
let inputs = match inputs_node.kind {
|
||||
UntypedKind::Tuple { elements } => elements,
|
||||
_ => vec![inputs_node],
|
||||
};
|
||||
let lambda = Box::new(self.parse_expression()?);
|
||||
let lambda = Box::new(self.parse_expression());
|
||||
|
||||
Ok(Node {
|
||||
Node {
|
||||
identity,
|
||||
kind: UntypedKind::Pipe { inputs, lambda },
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_fn(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||
let params = Box::new(self.parse_param_vector()?);
|
||||
let body = self.parse_expression()?;
|
||||
fn parse_fn(&mut self, identity: Identity) -> Node<UntypedKind> {
|
||||
let params = Box::new(self.parse_param_vector());
|
||||
let body = self.parse_expression();
|
||||
|
||||
Ok(Node {
|
||||
Node {
|
||||
identity,
|
||||
kind: UntypedKind::Lambda {
|
||||
params,
|
||||
body: Rc::new(body),
|
||||
},
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_macro_decl(&mut self, identity: Identity) -> Result<Node<UntypedKind>, String> {
|
||||
let name_node = self.parse_expression()?;
|
||||
fn parse_macro_decl(&mut self, identity: Identity) -> Node<UntypedKind> {
|
||||
let name_node = self.parse_expression();
|
||||
let name = match name_node.kind {
|
||||
UntypedKind::Identifier(sym) => sym,
|
||||
_ => return Err("Expected identifier for macro name".to_string()),
|
||||
_ => {
|
||||
self.diagnostics.push_error(
|
||||
"Expected identifier for macro name",
|
||||
Some(name_node.identity.clone()),
|
||||
);
|
||||
Symbol::from("error")
|
||||
}
|
||||
};
|
||||
let params = Box::new(self.parse_param_vector()?);
|
||||
let body = self.parse_expression()?;
|
||||
Ok(Node {
|
||||
let params = Box::new(self.parse_param_vector());
|
||||
let body = self.parse_expression();
|
||||
Node {
|
||||
identity,
|
||||
kind: UntypedKind::MacroDecl {
|
||||
name,
|
||||
@@ -275,54 +325,66 @@ impl<'a> Parser<'a> {
|
||||
body: Box::new(body),
|
||||
},
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_param_vector(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||
fn parse_param_vector(&mut self) -> Node<UntypedKind> {
|
||||
if *self.peek() != TokenKind::LeftBracket {
|
||||
return Err(format!(
|
||||
"Expected parameter vector [...] for fn, found {:?}",
|
||||
self.peek()
|
||||
));
|
||||
self.diagnostics.push_error(
|
||||
format!("Expected parameter vector [...] for fn, found {:?}", self.peek()),
|
||||
Some(NodeIdentity::new(self.current_token.location)),
|
||||
);
|
||||
return Node {
|
||||
identity: NodeIdentity::new(self.current_token.location),
|
||||
kind: UntypedKind::Error,
|
||||
ty: (),
|
||||
};
|
||||
}
|
||||
self.parse_pattern()
|
||||
}
|
||||
|
||||
fn parse_pattern(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||
fn parse_pattern(&mut self) -> Node<UntypedKind> {
|
||||
let next = self.peek();
|
||||
match next {
|
||||
TokenKind::Identifier(_) => {
|
||||
let token = self.advance()?;
|
||||
let token = self.advance();
|
||||
let sym: Symbol = match token.kind {
|
||||
TokenKind::Identifier(s) => s.into(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ok(Node {
|
||||
Node {
|
||||
identity: NodeIdentity::new(token.location),
|
||||
kind: UntypedKind::Parameter(sym),
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
TokenKind::LeftBracket => {
|
||||
let token = self.advance()?;
|
||||
let token = self.advance();
|
||||
let identity = NodeIdentity::new(token.location);
|
||||
|
||||
let mut elements = Vec::new();
|
||||
while *self.peek() != TokenKind::RightBracket {
|
||||
elements.push(self.parse_pattern()?);
|
||||
while *self.peek() != TokenKind::RightBracket && *self.peek() != TokenKind::EOF {
|
||||
elements.push(self.parse_pattern());
|
||||
}
|
||||
self.expect(TokenKind::RightBracket)?;
|
||||
self.expect(TokenKind::RightBracket);
|
||||
|
||||
Ok(Node {
|
||||
Node {
|
||||
identity,
|
||||
kind: UntypedKind::Tuple { elements },
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.diagnostics.push_error(
|
||||
format!("Expected identifier or pattern vector [...] for definition, found {:?}", next),
|
||||
Some(NodeIdentity::new(self.current_token.location)),
|
||||
);
|
||||
Node {
|
||||
identity: NodeIdentity::new(self.current_token.location),
|
||||
kind: UntypedKind::Error,
|
||||
ty: (),
|
||||
}
|
||||
}
|
||||
_ => Err(format!(
|
||||
"Expected identifier or pattern vector [...] for definition, found {:?}",
|
||||
next
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,11 +392,11 @@ impl<'a> Parser<'a> {
|
||||
&mut self,
|
||||
callee: Node<UntypedKind>,
|
||||
identity: Identity,
|
||||
) -> Result<Node<UntypedKind>, String> {
|
||||
) -> Node<UntypedKind> {
|
||||
let mut elements = Vec::new();
|
||||
|
||||
while *self.peek() != TokenKind::RightParen && *self.peek() != TokenKind::EOF {
|
||||
elements.push(self.parse_expression()?);
|
||||
elements.push(self.parse_expression());
|
||||
}
|
||||
|
||||
// The arguments are wrapped in a Tuple node, reusing the call's identity/location.
|
||||
@@ -344,77 +406,87 @@ impl<'a> Parser<'a> {
|
||||
ty: (),
|
||||
};
|
||||
|
||||
Ok(Node {
|
||||
Node {
|
||||
identity,
|
||||
kind: UntypedKind::Call {
|
||||
callee: Box::new(callee),
|
||||
args: Box::new(args_node),
|
||||
},
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_vector_literal(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||
let token = self.advance()?;
|
||||
fn parse_vector_literal(&mut self) -> Node<UntypedKind> {
|
||||
let token = self.advance();
|
||||
let mut elements = Vec::new();
|
||||
|
||||
while *self.peek() != TokenKind::RightBracket && *self.peek() != TokenKind::EOF {
|
||||
let expr = self.parse_expression()?;
|
||||
let expr = self.parse_expression();
|
||||
elements.push(expr);
|
||||
}
|
||||
|
||||
self.expect(TokenKind::RightBracket)?;
|
||||
self.expect(TokenKind::RightBracket);
|
||||
|
||||
Ok(Node {
|
||||
Node {
|
||||
identity: NodeIdentity::new(token.location),
|
||||
kind: UntypedKind::Tuple { elements },
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_record_literal(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||
let token = self.advance()?;
|
||||
fn parse_record_literal(&mut self) -> Node<UntypedKind> {
|
||||
let token = self.advance();
|
||||
let mut fields = Vec::new();
|
||||
|
||||
while *self.peek() != TokenKind::RightBrace {
|
||||
if *self.peek() == TokenKind::EOF {
|
||||
return Err("Unexpected EOF in record literal".to_string());
|
||||
}
|
||||
|
||||
let key_node = self.parse_expression()?;
|
||||
while *self.peek() != TokenKind::RightBrace && *self.peek() != TokenKind::EOF {
|
||||
let key_node = self.parse_expression();
|
||||
// We check for keyword kind here (syntactically) to avoid ambiguity, but
|
||||
// strictly we could allow any expression and check at runtime.
|
||||
// Delphi enforces keywords. We can do minimal check here.
|
||||
match &key_node.kind {
|
||||
UntypedKind::Constant(Value::Keyword(_)) => {}
|
||||
_ => return Err("Record keys must be keywords (syntactically)".to_string()),
|
||||
_ => {
|
||||
self.diagnostics.push_error(
|
||||
"Record keys must be keywords (syntactically)",
|
||||
Some(key_node.identity.clone()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if *self.peek() == TokenKind::RightBrace {
|
||||
return Err("Record literal must have even number of forms".to_string());
|
||||
if *self.peek() == TokenKind::RightBrace || *self.peek() == TokenKind::EOF {
|
||||
self.diagnostics.push_error(
|
||||
"Record literal must have even number of forms",
|
||||
Some(NodeIdentity::new(self.current_token.location)),
|
||||
);
|
||||
break;
|
||||
}
|
||||
let val_node = self.parse_expression()?;
|
||||
let val_node = self.parse_expression();
|
||||
|
||||
fields.push((key_node, val_node));
|
||||
}
|
||||
self.expect(TokenKind::RightBrace)?;
|
||||
self.expect(TokenKind::RightBrace);
|
||||
|
||||
Ok(Node {
|
||||
Node {
|
||||
identity: NodeIdentity::new(token.location),
|
||||
kind: UntypedKind::Record { fields },
|
||||
ty: (),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn expect(&mut self, kind: TokenKind) -> Result<(), String> {
|
||||
let token = self.advance()?;
|
||||
if token.kind == kind {
|
||||
Ok(())
|
||||
fn expect(&mut self, kind: TokenKind) -> Token {
|
||||
if self.peek() == &kind {
|
||||
self.advance()
|
||||
} else {
|
||||
Err(format!(
|
||||
"Expected {:?}, but found {:?} at {:?}",
|
||||
kind, token.kind, token.location
|
||||
))
|
||||
self.diagnostics.push_error(
|
||||
format!("Expected {:?}, but found {:?}", kind, self.peek()),
|
||||
Some(NodeIdentity::new(self.current_token.location)),
|
||||
);
|
||||
// Recovery: skip until we find what we expected or a synchronization point
|
||||
self.synchronize();
|
||||
Token {
|
||||
kind,
|
||||
location: self.current_token.location,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user