aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCody <cody@codyq.dev>2023-06-24 01:07:51 -0500
committerCody <cody@codyq.dev>2023-06-24 01:07:51 -0500
commit0ee365c80667a3a5f43a86ead75fd0da5be23282 (patch)
tree8fc522e5ba17cafad574581fea9f1814502fc4c8
parenta8b21ebd34aa2d1d750eecc22f3f252c04198fd0 (diff)
downloadsloth-0ee365c80667a3a5f43a86ead75fd0da5be23282.tar.gz
Keep track of line numbers through to parser
-rw-r--r--sloth/src/analysis/mod.rs22
-rw-r--r--sloth/src/lexer.rs8
-rw-r--r--sloth/src/main.rs6
-rw-r--r--sloth/src/parser/ast.rs29
-rw-r--r--sloth/src/parser/expr.rs17
-rw-r--r--sloth/src/parser/mod.rs11
-rw-r--r--sloth/src/parser/stmt.rs56
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<Token<'a>>,
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<Token<'a>>, 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<Stmt, ParsingError> {
@@ -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<Stmt, ParsingError> {
@@ -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<Stmt, ParsingError> {
@@ -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<Stmt, ParsingError> {
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<Stmt, ParsingError> {
@@ -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