add multiple error reporting
This commit is contained in:
parent
a67d4cb273
commit
1da18a14b7
|
@ -36,7 +36,7 @@ impl Debug for Program {
|
||||||
pub struct LetStatement {
|
pub struct LetStatement {
|
||||||
pub token: Token,
|
pub token: Token,
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
pub value: Box<dyn Expression>,
|
pub value: Rc<dyn Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for LetStatement {}
|
impl Node for LetStatement {}
|
||||||
|
@ -65,5 +65,4 @@ impl Expression for DummyExpression {
|
||||||
fn expression_node(&self) {
|
fn expression_node(&self) {
|
||||||
panic!("this is dummy");
|
panic!("this is dummy");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
mod ast;
|
mod ast;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
|
mod parser;
|
||||||
mod repl;
|
mod repl;
|
||||||
mod token;
|
mod token;
|
||||||
mod parser;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let stdout = std::io::stdout();
|
let stdout = std::io::stdout();
|
||||||
|
|
116
src/parser.rs
116
src/parser.rs
|
@ -1,28 +1,53 @@
|
||||||
use std::rc::Rc;
|
use std::{fmt, rc::Rc};
|
||||||
|
|
||||||
use crate::{lexer::Lexer, token::Token, ast::{Program, Statement, LetStatement, Identifier, DummyExpression}};
|
use crate::{
|
||||||
|
ast::{DummyExpression, Expression, Identifier, LetStatement, Program, Statement},
|
||||||
|
lexer::Lexer,
|
||||||
|
token::Token,
|
||||||
|
};
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Error {
|
pub enum Error {
|
||||||
UnexpectedToken {
|
UnexpectedToken {
|
||||||
expected: Token,
|
expected: Token,
|
||||||
actual: Option<Token>,
|
actual: Option<Token>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Parser {
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "parser error: ")?;
|
||||||
|
use Error::*;
|
||||||
|
match self {
|
||||||
|
UnexpectedToken { expected, actual } => write!(
|
||||||
|
f,
|
||||||
|
"expected token `{:?}`, actual token: `{:?}`",
|
||||||
|
expected, actual
|
||||||
|
)?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Parser {
|
||||||
lexer: Lexer,
|
lexer: Lexer,
|
||||||
cur_token: Option<Token>,
|
cur_token: Option<Token>,
|
||||||
peek_token: Option<Token>,
|
peek_token: Option<Token>,
|
||||||
|
errors: Vec<Error>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
pub fn new(mut lexer: Lexer) -> Self {
|
pub fn new(mut lexer: Lexer) -> Self {
|
||||||
let cur_token = lexer.next();
|
let cur_token = lexer.next();
|
||||||
let peek_token = lexer.next();
|
let peek_token = lexer.next();
|
||||||
Self { lexer, cur_token, peek_token }
|
Self {
|
||||||
|
lexer,
|
||||||
|
cur_token,
|
||||||
|
peek_token,
|
||||||
|
errors: Vec::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(&mut self) -> Result<Program> {
|
pub fn parse(&mut self) -> Result<Program> {
|
||||||
|
@ -30,12 +55,12 @@ impl Parser {
|
||||||
statements: Vec::new(),
|
statements: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let mut done = Some(());
|
let mut done = Some(());
|
||||||
|
|
||||||
while done.is_some() {
|
while done.is_some() {
|
||||||
let stmt = self.parse_statement();
|
if let Some(stmt) = self.parse_statement() {
|
||||||
program.statements.push(stmt?);
|
program.statements.push(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
done = self.next();
|
done = self.next();
|
||||||
}
|
}
|
||||||
|
@ -43,53 +68,70 @@ impl Parser {
|
||||||
Ok(program)
|
Ok(program)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_statement(&mut self) -> Result<Rc<dyn Statement>> {
|
pub fn errors(&self) -> &Vec<Error> {
|
||||||
|
&self.errors
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_statement(&mut self) -> Option<Rc<dyn Statement>> {
|
||||||
match &self.cur_token {
|
match &self.cur_token {
|
||||||
Some(token) => {
|
Some(token) => {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
match token {
|
match token {
|
||||||
Let => self.parse_let_statement(),
|
Let => self.parse_let_statement(),
|
||||||
t => unimplemented!("{t:?} statement token not impl"),
|
t => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => unreachable!()
|
None => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_let_statement(&mut self) -> Result<Rc<dyn Statement>> {
|
fn parse_let_statement(&mut self) -> Option<Rc<dyn Statement>> {
|
||||||
let token = self.cur_token.clone().unwrap();
|
let token = self.cur_token.clone().unwrap();
|
||||||
|
|
||||||
self.expect_peek(&Token::Ident("".to_string()))?;
|
if !self.expect_peek(&Token::Ident("".to_string())) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let name = Identifier {
|
let name = Identifier {
|
||||||
token: self.cur_token.clone().unwrap(),
|
token: self.cur_token.clone().unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.expect_peek(&Token::Assign)?;
|
if !self.expect_peek(&Token::Assign) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = self.parse_expression()?;
|
||||||
|
|
||||||
|
Some(Rc::new(LetStatement { token, name, value }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expression(&mut self) -> Option<Rc<dyn Expression>> {
|
||||||
while !self.cur_token_is(&Token::Semicolon) {
|
while !self.cur_token_is(&Token::Semicolon) {
|
||||||
self.next();
|
self.next();
|
||||||
}
|
}
|
||||||
|
Some(Rc::new(DummyExpression {}))
|
||||||
Ok(Rc::new(LetStatement {
|
|
||||||
token,
|
|
||||||
name,
|
|
||||||
value: Box::new(DummyExpression {}),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect_peek(&mut self, token: &Token) -> Result<()> {
|
fn expect_peek(&mut self, token: &Token) -> bool {
|
||||||
if self.peek_token.is_none() {
|
if self.peek_token.is_none() {
|
||||||
return Err(Error::UnexpectedToken { expected: token.clone(), actual: None });
|
self.errors.push(Error::UnexpectedToken {
|
||||||
|
expected: token.clone(),
|
||||||
|
actual: None,
|
||||||
|
});
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let peek_token = self.peek_token.clone().unwrap();
|
let peek_token = self.peek_token.clone().unwrap();
|
||||||
|
|
||||||
if token.is_same_type(&peek_token) {
|
if token.is_same_type(&peek_token) {
|
||||||
self.next();
|
self.next();
|
||||||
Ok(())
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Err(Error::UnexpectedToken { expected: token.clone(), actual: None })
|
self.errors.push(Error::UnexpectedToken {
|
||||||
|
expected: token.clone(),
|
||||||
|
actual: None,
|
||||||
|
});
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +158,6 @@ impl Iterator for Parser {
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let peek_token = self.lexer.next();
|
let peek_token = self.lexer.next();
|
||||||
|
|
||||||
|
|
||||||
self.cur_token = self.peek_token.clone();
|
self.cur_token = self.peek_token.clone();
|
||||||
self.peek_token = peek_token;
|
self.peek_token = peek_token;
|
||||||
|
|
||||||
|
@ -131,7 +172,7 @@ impl Iterator for Parser {
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::{lexer::Lexer, ast::Statement};
|
use crate::{ast::Statement, lexer::Lexer};
|
||||||
|
|
||||||
use super::Parser;
|
use super::Parser;
|
||||||
|
|
||||||
|
@ -140,21 +181,19 @@ mod tests {
|
||||||
let source = "let x = 5;\
|
let source = "let x = 5;\
|
||||||
let y = 10;\
|
let y = 10;\
|
||||||
let foobar = 838383;\
|
let foobar = 838383;\
|
||||||
".to_string();
|
"
|
||||||
|
.to_string();
|
||||||
|
|
||||||
let lexer = Lexer::new(source);
|
let lexer = Lexer::new(source);
|
||||||
|
|
||||||
let mut parser = Parser::new(lexer);
|
let mut parser = Parser::new(lexer);
|
||||||
|
|
||||||
let program = parser.parse().unwrap();
|
let program = parser.parse().unwrap();
|
||||||
|
check_parser_errors(parser);
|
||||||
|
|
||||||
assert_eq!(program.statements.len(), 3);
|
assert_eq!(program.statements.len(), 3);
|
||||||
|
|
||||||
let expected_identifiers = vec![
|
let expected_identifiers = vec!["x", "y", "foobar"];
|
||||||
"x",
|
|
||||||
"y",
|
|
||||||
"foobar",
|
|
||||||
];
|
|
||||||
let mut statements_iter = program.statements.iter();
|
let mut statements_iter = program.statements.iter();
|
||||||
for tt in expected_identifiers {
|
for tt in expected_identifiers {
|
||||||
let statement = statements_iter.next().unwrap();
|
let statement = statements_iter.next().unwrap();
|
||||||
|
@ -169,4 +208,17 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_parser_errors(parser: Parser) {
|
||||||
|
if parser.errors().len() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut err = "parser errors:".to_owned();
|
||||||
|
|
||||||
|
for (i, error) in parser.errors().iter().enumerate() {
|
||||||
|
err.push_str(&format!("\n\t{i}: {error}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("{err}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::io::{BufRead, BufReader, Read, Write};
|
use std::io::{BufRead, BufReader, Read, Write};
|
||||||
|
|
||||||
use crate::{lexer::Lexer, token::Token};
|
use crate::{lexer::Lexer, parser::Parser, token::Token};
|
||||||
|
|
||||||
const PROMPT: &str = ">> ";
|
const PROMPT: &str = ">> ";
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ pub fn start(mut w: impl Write, r: impl Read) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let lex = Lexer::new(line);
|
let lex = Lexer::new(line.clone());
|
||||||
|
|
||||||
for token in lex {
|
for token in lex {
|
||||||
if token == Token::EOF {
|
if token == Token::EOF {
|
||||||
|
@ -26,5 +26,10 @@ pub fn start(mut w: impl Write, r: impl Read) {
|
||||||
}
|
}
|
||||||
writeln!(w, "{token:?}").unwrap();
|
writeln!(w, "{token:?}").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut parser = Parser::new(Lexer::new(line));
|
||||||
|
let program = parser.parse().unwrap();
|
||||||
|
|
||||||
|
writeln!(w, "{program:?}").unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
src/token.rs
16
src/token.rs
|
@ -39,8 +39,20 @@ impl Token {
|
||||||
pub fn is_same_type(&self, other: &Token) -> bool {
|
pub fn is_same_type(&self, other: &Token) -> bool {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
match self {
|
match self {
|
||||||
Ident(_) => if let Ident(_) = other { true } else { false },
|
Ident(_) => {
|
||||||
Int(_) => if let Int(_) = other { true } else { false },
|
if let Ident(_) = other {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Int(_) => {
|
||||||
|
if let Int(_) = other {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
tok => tok == other,
|
tok => tok == other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue