From 0ee365c80667a3a5f43a86ead75fd0da5be23282 Mon Sep 17 00:00:00 2001 From: Cody Date: Sat, 24 Jun 2023 01:07:51 -0500 Subject: [PATCH] Keep track of line numbers through to parser --- sloth/src/analysis/mod.rs | 22 ++++++++++----- sloth/src/lexer.rs | 8 +++--- sloth/src/main.rs | 6 +++-- sloth/src/parser/ast.rs | 29 +++++++++++++++++--- sloth/src/parser/expr.rs | 17 +++++++++--- sloth/src/parser/mod.rs | 11 +++++--- sloth/src/parser/stmt.rs | 56 +++++++++++++++++++++++++++++++++------ 7 files changed, 119 insertions(+), 30 deletions(-) diff --git a/sloth/src/analysis/mod.rs b/sloth/src/analysis/mod.rs index f8b1f1a..857fbb8 100644 --- a/sloth/src/analysis/mod.rs +++ b/sloth/src/analysis/mod.rs @@ -4,11 +4,21 @@ use crate::symtable::{Symbol, SymbolType}; #[derive(Debug, thiserror::Error)] pub enum AnalysisError { #[error("Mismatched types")] - TypeMismatch, - #[error("Unknown identifier '{0}'")] - UnknownIdentifier(String), + TypeMismatch(u32), + #[error("Unknown identifier '{1}'")] + UnknownIdentifier(u32, String), #[error("Unknown error")] - Unknown, + Unknown(u32), +} + +impl AnalysisError { + pub fn line(&self) -> u32 { + match self { + AnalysisError::TypeMismatch(line) => *line, + AnalysisError::UnknownIdentifier(line, ..) => *line, + AnalysisError::Unknown(line) => *line, + } + } } pub fn analyze(root: &mut Stmt) -> Result<(), AnalysisError> { @@ -40,11 +50,11 @@ fn populate_symtable(node: &AstNode) { fn check_usage(node: &AstNode) -> Result<(), AnalysisError> { if let AstNode::Expr(expr) = node && let ExprKind::Identifier(identifier) = &expr.kind && !expr.symtable.clone().contains(identifier) { - return Err(AnalysisError::UnknownIdentifier(identifier.clone())); + return Err(AnalysisError::UnknownIdentifier(expr.line, identifier.clone())); } if let AstNode::Stmt(stmt) = node && let StmtKind::AssignVariable { identifier, .. } = &stmt.kind && !stmt.symtable.clone().contains(identifier) { - return Err(AnalysisError::UnknownIdentifier(identifier.clone())); + return Err(AnalysisError::UnknownIdentifier(stmt.line, identifier.clone())); } for child in node.children() { diff --git a/sloth/src/lexer.rs b/sloth/src/lexer.rs index 7ce95b5..3ad39a2 100644 --- a/sloth/src/lexer.rs +++ b/sloth/src/lexer.rs @@ -146,11 +146,11 @@ impl Location { #[derive(Debug)] pub struct Token<'a> { - pub tt: TokenType, - pub lexeme: &'a str, + pub(crate) tt: TokenType, + pub(crate) lexeme: &'a str, - start: Location, - end: Location, + pub start: Location, + pub end: Location, } pub struct Lexer<'a> { diff --git a/sloth/src/main.rs b/sloth/src/main.rs index 3a70197..1eb0ead 100644 --- a/sloth/src/main.rs +++ b/sloth/src/main.rs @@ -47,10 +47,12 @@ fn main() { let mut ast = AstParser::parse(tokens, global_symtable).unwrap(); if let Err(error) = analyze(&mut ast) { - eprintln!("Failed to compile code:"); - eprintln!("{error}"); + eprintln!("Error on line {}: {error}", error.line() + 1); + return; } + println!("{ast:#?}"); + // let graph = GraphBuilder::generate(&ast).unwrap(); // println!("{graph}"); } diff --git a/sloth/src/parser/ast.rs b/sloth/src/parser/ast.rs index 8442cc2..a542847 100644 --- a/sloth/src/parser/ast.rs +++ b/sloth/src/parser/ast.rs @@ -21,11 +21,19 @@ impl<'a> AstNode<'a> { } children.into_iter() } + + pub fn line(&self) -> u32 { + match self { + Self::Expr(expr) => expr.line, + Self::Stmt(stmt) => stmt.line, + } + } } #[derive(Clone, Debug)] pub struct Expr { pub id: i32, + pub line: u32, pub kind: ExprKind, pub symtable: SymbolTable, } @@ -37,14 +45,20 @@ impl PartialEq for Expr { } impl Expr { - pub fn new(id: i32, kind: ExprKind, symtable: SymbolTable) -> Self { - Self { id, kind, symtable } + pub fn new(id: i32, line: u32, kind: ExprKind, symtable: SymbolTable) -> Self { + Self { + id, + line, + kind, + symtable, + } } /// Useful for testing pub fn without_table(id: i32, kind: ExprKind) -> Self { Self { id, + line: 0, kind, symtable: SymbolTable::new(), } @@ -98,6 +112,7 @@ pub enum ExprKind { #[derive(Clone, Debug)] pub struct Stmt { pub id: i32, + pub line: u32, pub kind: StmtKind, pub symtable: SymbolTable, } @@ -109,14 +124,20 @@ impl PartialEq for Stmt { } impl Stmt { - pub fn new(id: i32, kind: StmtKind, symtable: SymbolTable) -> Self { - Self { id, kind, symtable } + pub fn new(id: i32, line: u32, kind: StmtKind, symtable: SymbolTable) -> Self { + Self { + id, + line, + kind, + symtable, + } } /// Useful for testing pub fn without_table(id: i32, kind: StmtKind) -> Self { Self { id, + line: 0, kind, symtable: SymbolTable::new(), } diff --git a/sloth/src/parser/expr.rs b/sloth/src/parser/expr.rs index 5fd9b49..7ec283f 100644 --- a/sloth/src/parser/expr.rs +++ b/sloth/src/parser/expr.rs @@ -21,7 +21,12 @@ impl<'a> AstParser<'a> { value: Box::new(value), }; - return Ok(Expr::new(self.reserve_id(), kind, self.top.clone())); + return Ok(Expr::new( + self.reserve_id(), + self.line, + kind, + self.top.clone(), + )); } self.call() @@ -45,6 +50,7 @@ impl<'a> AstParser<'a> { expr = Expr::new( self.reserve_id(), + self.line, ExprKind::Call { callee: Box::new(expr), args: arguments, @@ -70,7 +76,12 @@ impl<'a> AstParser<'a> { _ => return Err(ParsingError::UnexpectedToken), }; - Ok(Expr::new(self.reserve_id(), kind, self.top.clone())) + Ok(Expr::new( + self.reserve_id(), + self.line, + kind, + self.top.clone(), + )) } } @@ -92,7 +103,7 @@ macro_rules! binary_expr { rhs: Box::new(rhs), }; - expr = Expr::new(self.reserve_id(), kind, self.top.clone()); + expr = Expr::new(self.reserve_id(), self.line, kind, self.top.clone()); } Ok(expr) diff --git a/sloth/src/parser/mod.rs b/sloth/src/parser/mod.rs index 3328137..748a0da 100644 --- a/sloth/src/parser/mod.rs +++ b/sloth/src/parser/mod.rs @@ -17,10 +17,11 @@ pub enum ParsingError { #[derive(Debug)] pub struct AstParser<'a> { + top: SymbolTable, tokens: Vec>, index: usize, id: i32, - top: SymbolTable, + line: u32, } impl<'a> AstParser<'a> { @@ -34,6 +35,7 @@ impl<'a> AstParser<'a> { let root = Stmt::new( parser.reserve_id(), + parser.line, StmtKind::Block(statements), parser.top.clone(), ); @@ -46,10 +48,11 @@ impl<'a> AstParser<'a> { impl<'a> AstParser<'a> { pub fn new(tokens: Vec>, root: SymbolTable) -> Self { Self { + top: root, tokens, index: 0, id: 0, - top: root, + line: 0, } } @@ -66,8 +69,10 @@ impl<'a> AstParser<'a> { return None; } + let current = &self.tokens[self.index]; self.index += 1; - Some(&self.tokens[self.index - 1]) + self.line = current.start.row; + Some(current) } pub fn advance_if(&mut self, next: impl FnOnce(&Token) -> bool) -> bool { diff --git a/sloth/src/parser/stmt.rs b/sloth/src/parser/stmt.rs index c7433c2..7897e21 100644 --- a/sloth/src/parser/stmt.rs +++ b/sloth/src/parser/stmt.rs @@ -42,7 +42,12 @@ impl<'a> AstParser<'a> { else_then: else_then.map(|it| it.into()), }; - Ok(Stmt::new(self.reserve_id(), kind, self.top.clone())) + Ok(Stmt::new( + self.reserve_id(), + self.line, + kind, + self.top.clone(), + )) } fn while_stmt(&mut self) -> Result { @@ -57,7 +62,12 @@ impl<'a> AstParser<'a> { body: body.into(), }; - Ok(Stmt::new(self.reserve_id(), kind, self.top.clone())) + Ok(Stmt::new( + self.reserve_id(), + self.line, + kind, + self.top.clone(), + )) } // TODO: Make variable types optional @@ -82,7 +92,12 @@ impl<'a> AstParser<'a> { typ, }; - Ok(Stmt::new(self.reserve_id(), kind, self.top.clone())) + Ok(Stmt::new( + self.reserve_id(), + self.line, + kind, + self.top.clone(), + )) } // TODO: Make argument types optional @@ -126,7 +141,12 @@ impl<'a> AstParser<'a> { body: body.into(), }; - Ok(Stmt::new(self.reserve_id(), kind, self.top.clone())) + Ok(Stmt::new( + self.reserve_id(), + self.line, + kind, + self.top.clone(), + )) } fn return_stmt(&mut self) -> Result { @@ -134,7 +154,12 @@ impl<'a> AstParser<'a> { let value = self.expression()?; self.consume(TokenType::SemiColon, "Expected ';' at end of statement")?; let kind = StmtKind::Return(value); - Ok(Stmt::new(self.reserve_id(), kind, self.top.clone())) + Ok(Stmt::new( + self.reserve_id(), + self.line, + kind, + self.top.clone(), + )) } fn assign_variable(&mut self) -> Result { @@ -143,14 +168,24 @@ impl<'a> AstParser<'a> { let value = self.expression()?; self.consume(TokenType::SemiColon, "Expected ';' at end of statement")?; let kind = StmtKind::AssignVariable { identifier, value }; - Ok(Stmt::new(self.reserve_id(), kind, self.top.clone())) + Ok(Stmt::new( + self.reserve_id(), + self.line, + kind, + self.top.clone(), + )) } fn expression_stmt(&mut self) -> Result { let expr = self.expression()?; self.consume(TokenType::SemiColon, "Expected ';' at end of statement")?; let kind = StmtKind::ExprStmt(expr); - Ok(Stmt::new(self.reserve_id(), kind, self.top.clone())) + Ok(Stmt::new( + self.reserve_id(), + self.line, + kind, + self.top.clone(), + )) } fn block(&mut self) -> Result { @@ -171,7 +206,12 @@ impl<'a> AstParser<'a> { let kind = StmtKind::Block(body); - Ok(Stmt::new(this.reserve_id(), kind, this.top.clone())) + Ok(Stmt::new( + this.reserve_id(), + this.line, + kind, + this.top.clone(), + )) } // Push a table, call the inner function and then pop that table