From a8b21ebd34aa2d1d750eecc22f3f252c04198fd0 Mon Sep 17 00:00:00 2001 From: Cody Date: Fri, 23 Jun 2023 22:01:38 -0500 Subject: Integrated symbol tables into AST --- examples/hello.sloth | 3 +- examples/hello2.sloth | 11 ++++ sloth/src/analysis/mod.rs | 55 +++++++++++++++++ sloth/src/main.rs | 25 ++++++-- sloth/src/parser/ast.rs | 122 +++++++++++++++++++++++++++++++++++-- sloth/src/parser/expr.rs | 54 +++++++++++------ sloth/src/parser/mod.rs | 27 +++++++-- sloth/src/parser/stmt.rs | 149 +++++++++++++++++++++++++++------------------- sloth/src/symtable.rs | 15 ++++- 9 files changed, 365 insertions(+), 96 deletions(-) create mode 100644 examples/hello2.sloth create mode 100644 sloth/src/analysis/mod.rs diff --git a/examples/hello.sloth b/examples/hello.sloth index fea5304..8b395e8 100644 --- a/examples/hello.sloth +++ b/examples/hello.sloth @@ -1,8 +1,9 @@ fn main() { var i: Int = 10; - var j: Int = int(1.0) - 1; + var j: Int = 1.0 - 1; while i > j { i = i - 1; } + example(i); } diff --git a/examples/hello2.sloth b/examples/hello2.sloth new file mode 100644 index 0000000..589d969 --- /dev/null +++ b/examples/hello2.sloth @@ -0,0 +1,11 @@ +fn example() {} + +fn main() { + var i: Int = 10; + var j: Int = 1.0 - 1; + while i > j { + i = i - 1; + } + example(i); +} + diff --git a/sloth/src/analysis/mod.rs b/sloth/src/analysis/mod.rs new file mode 100644 index 0000000..f8b1f1a --- /dev/null +++ b/sloth/src/analysis/mod.rs @@ -0,0 +1,55 @@ +use crate::parser::ast::{AstNode, ExprKind, Stmt, StmtKind}; +use crate::symtable::{Symbol, SymbolType}; + +#[derive(Debug, thiserror::Error)] +pub enum AnalysisError { + #[error("Mismatched types")] + TypeMismatch, + #[error("Unknown identifier '{0}'")] + UnknownIdentifier(String), + #[error("Unknown error")] + Unknown, +} + +pub fn analyze(root: &mut Stmt) -> Result<(), AnalysisError> { + populate_symtable(&root.as_node()); + check_usage(&root.as_node())?; + + Ok(()) +} + +fn populate_symtable(node: &AstNode) { + if let AstNode::Stmt(stmt) = node { + match &stmt.kind { + StmtKind::DefineVariable { identifier, .. } => { + let mut table = stmt.symtable.clone(); + table.insert(identifier.to_owned(), Symbol::new(SymbolType::Variable)); + } + StmtKind::DefineFunction { identifier, .. } => { + let mut table = stmt.symtable.clone(); + table.insert(identifier.to_owned(), Symbol::new(SymbolType::Function)); + } + _ => (), + } + } + + for child in node.children() { + populate_symtable(&child); + } +} + +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())); + } + + if let AstNode::Stmt(stmt) = node && let StmtKind::AssignVariable { identifier, .. } = &stmt.kind && !stmt.symtable.clone().contains(identifier) { + return Err(AnalysisError::UnknownIdentifier(identifier.clone())); + } + + for child in node.children() { + check_usage(&child)?; + } + + Ok(()) +} diff --git a/sloth/src/main.rs b/sloth/src/main.rs index a85a8d5..3a70197 100644 --- a/sloth/src/main.rs +++ b/sloth/src/main.rs @@ -1,3 +1,4 @@ +#![feature(let_chains)] #![warn( clippy::wildcard_imports, clippy::string_add, @@ -6,6 +7,7 @@ unused_lifetimes )] +pub mod analysis; pub mod lexer; pub mod parser; pub mod sloth_std; @@ -13,10 +15,11 @@ pub mod symtable; use std::{env, fs}; +use analysis::analyze; use itertools::Itertools; use lexer::Lexer; -use parser::graph::GraphBuilder; use parser::AstParser; +use symtable::{Symbol, SymbolTable, SymbolType}; fn main() { let args = env::args().collect_vec(); @@ -29,13 +32,25 @@ fn main() { let source_path = &args[1]; let Ok(source) = fs::read_to_string(source_path) else { - println!("Error while reading '{source_path}'"); + eprintln!("Error while reading '{source_path}'"); return; }; + // Symbol table + let mut global_symtable = SymbolTable::new(); + global_symtable.insert("print".to_owned(), Symbol::new(SymbolType::Function)); + global_symtable.insert("println".to_owned(), Symbol::new(SymbolType::Function)); + global_symtable.insert("readln".to_owned(), Symbol::new(SymbolType::Function)); + + // Parsing let tokens = Lexer::new(&source).collect_vec(); - let ast = AstParser::parse(tokens).unwrap(); + let mut ast = AstParser::parse(tokens, global_symtable).unwrap(); + + if let Err(error) = analyze(&mut ast) { + eprintln!("Failed to compile code:"); + eprintln!("{error}"); + } - let graph = GraphBuilder::generate(&ast).unwrap(); - println!("{graph}"); + // let graph = GraphBuilder::generate(&ast).unwrap(); + // println!("{graph}"); } diff --git a/sloth/src/parser/ast.rs b/sloth/src/parser/ast.rs index 1a994df..8442cc2 100644 --- a/sloth/src/parser/ast.rs +++ b/sloth/src/parser/ast.rs @@ -2,16 +2,76 @@ use std::fmt::Display; use crate::lexer::{self, TokenType}; use crate::parser::ParsingError; +use crate::symtable::SymbolTable; #[derive(PartialEq, Clone, Debug)] +/// AstNode that is either an Expr or Stmt, typically used for iterating over an +/// Ast for analysis reasons. +pub enum AstNode<'a> { + Expr(&'a Expr), + Stmt(&'a Stmt), +} + +impl<'a> AstNode<'a> { + pub fn children(&self) -> impl Iterator { + let mut children = Vec::new(); + match self { + Self::Expr(expr) => children.extend(expr.children()), + Self::Stmt(stmt) => children.extend(stmt.children()), + } + children.into_iter() + } +} + +#[derive(Clone, Debug)] pub struct Expr { pub id: i32, pub kind: ExprKind, + pub symtable: SymbolTable, +} + +impl PartialEq for Expr { + fn eq(&self, other: &Self) -> bool { + self.id == other.id && self.kind == other.kind + } } impl Expr { - pub fn new(id: i32, kind: ExprKind) -> Self { - Self { id, kind } + pub fn new(id: i32, kind: ExprKind, symtable: SymbolTable) -> Self { + Self { id, kind, symtable } + } + + /// Useful for testing + pub fn without_table(id: i32, kind: ExprKind) -> Self { + Self { + id, + kind, + symtable: SymbolTable::new(), + } + } + + pub fn as_node(&self) -> AstNode { + AstNode::Expr(self) + } + + pub fn children(&self) -> impl Iterator { + let mut children = Vec::new(); + + match &self.kind { + ExprKind::Grouping(inner) => children.push(inner.as_node()), + ExprKind::BinaryOp { lhs, rhs, .. } => { + children.push(lhs.as_node()); + children.push(rhs.as_node()); + } + ExprKind::UnaryOp { value, .. } => children.push(value.as_node()), + ExprKind::Call { callee, args } => { + children.push(callee.as_node()); + children.extend(args.iter().map(Expr::as_node)); + } + _ => (), + } + + children.into_iter() } } @@ -35,15 +95,67 @@ pub enum ExprKind { }, } -#[derive(PartialEq, Clone, Debug)] +#[derive(Clone, Debug)] pub struct Stmt { pub id: i32, pub kind: StmtKind, + pub symtable: SymbolTable, +} + +impl PartialEq for Stmt { + fn eq(&self, other: &Self) -> bool { + self.id == other.id && self.kind == other.kind + } } impl Stmt { - pub fn new(id: i32, kind: StmtKind) -> Self { - Self { id, kind } + pub fn new(id: i32, kind: StmtKind, symtable: SymbolTable) -> Self { + Self { id, kind, symtable } + } + + /// Useful for testing + pub fn without_table(id: i32, kind: StmtKind) -> Self { + Self { + id, + kind, + symtable: SymbolTable::new(), + } + } + + pub fn as_node(&self) -> AstNode { + AstNode::Stmt(self) + } + + pub fn children(&self) -> impl Iterator { + let mut children = Vec::new(); + + match &self.kind { + StmtKind::Block(inner) => { + children.extend(inner.iter().map(Self::as_node)); + } + StmtKind::ExprStmt(expr) => children.push(expr.as_node()), + StmtKind::IfStmt { + condition, + if_then, + else_then, + } => { + children.push(condition.as_node()); + children.push(if_then.as_node()); + if let Some(else_then) = else_then { + children.push(else_then.as_node()); + } + } + StmtKind::WhileStmt { condition, body } => { + children.push(condition.as_node()); + children.push(body.as_node()); + } + StmtKind::DefineVariable { value, .. } => children.push(value.as_node()), + StmtKind::AssignVariable { value, .. } => children.push(value.as_node()), + StmtKind::DefineFunction { body, .. } => children.push(body.as_node()), + StmtKind::Return(value) => children.push(value.as_node()), + } + + children.into_iter() } } diff --git a/sloth/src/parser/expr.rs b/sloth/src/parser/expr.rs index 48ee038..5fd9b49 100644 --- a/sloth/src/parser/expr.rs +++ b/sloth/src/parser/expr.rs @@ -21,7 +21,7 @@ impl<'a> AstParser<'a> { value: Box::new(value), }; - return Ok(Expr::new(self.reserve_id(), kind)); + return Ok(Expr::new(self.reserve_id(), kind, self.top.clone())); } self.call() @@ -43,10 +43,14 @@ impl<'a> AstParser<'a> { self.consume(TokenType::ClosingParen, "Expected ')'")?; - expr = Expr::new(self.reserve_id(), ExprKind::Call { - callee: Box::new(expr), - args: arguments, - }); + expr = Expr::new( + self.reserve_id(), + ExprKind::Call { + callee: Box::new(expr), + args: arguments, + }, + self.top.clone(), + ); } Ok(expr) @@ -66,7 +70,7 @@ impl<'a> AstParser<'a> { _ => return Err(ParsingError::UnexpectedToken), }; - Ok(Expr::new(self.reserve_id(), kind)) + Ok(Expr::new(self.reserve_id(), kind, self.top.clone())) } } @@ -88,7 +92,7 @@ macro_rules! binary_expr { rhs: Box::new(rhs), }; - expr = Expr::new(self.reserve_id(), kind); + expr = Expr::new(self.reserve_id(), kind, self.top.clone()); } Ok(expr) @@ -115,6 +119,7 @@ mod tests { use crate::lexer::Lexer; use crate::parser::ast::{BinaryOp, Expr, ExprKind, Literal}; + use crate::symtable::SymbolTable; use crate::AstParser; #[test] @@ -122,25 +127,40 @@ mod tests { let lexer = Lexer::new("3 + 5 * 4 - 9 / 3"); let tokens = lexer.collect_vec(); - let expected_ast = Ok(Expr::new(8, ExprKind::BinaryOp { + let expected_ast = Ok(Expr::without_table(8, ExprKind::BinaryOp { op: BinaryOp::Sub, - lhs: Box::new(Expr::new(4, ExprKind::BinaryOp { + lhs: Box::new(Expr::without_table(4, ExprKind::BinaryOp { op: BinaryOp::Add, - lhs: Box::new(Expr::new(0, ExprKind::Literal(Literal::Integer(3)))), - rhs: Box::new(Expr::new(3, ExprKind::BinaryOp { + lhs: Box::new(Expr::without_table( + 0, + ExprKind::Literal(Literal::Integer(3)), + )), + rhs: Box::new(Expr::without_table(3, ExprKind::BinaryOp { op: BinaryOp::Mul, - lhs: Box::new(Expr::new(1, ExprKind::Literal(Literal::Integer(5)))), - rhs: Box::new(Expr::new(2, ExprKind::Literal(Literal::Integer(4)))), + lhs: Box::new(Expr::without_table( + 1, + ExprKind::Literal(Literal::Integer(5)), + )), + rhs: Box::new(Expr::without_table( + 2, + ExprKind::Literal(Literal::Integer(4)), + )), })), })), - rhs: Box::new(Expr::new(7, ExprKind::BinaryOp { + rhs: Box::new(Expr::without_table(7, ExprKind::BinaryOp { op: BinaryOp::Div, - lhs: Box::new(Expr::new(5, ExprKind::Literal(Literal::Integer(9)))), - rhs: Box::new(Expr::new(6, ExprKind::Literal(Literal::Integer(3)))), + lhs: Box::new(Expr::without_table( + 5, + ExprKind::Literal(Literal::Integer(9)), + )), + rhs: Box::new(Expr::without_table( + 6, + ExprKind::Literal(Literal::Integer(3)), + )), })), })); - let mut parser = AstParser::new(tokens); + let mut parser = AstParser::new(tokens, SymbolTable::new()); let generated_ast = parser.expression(); println!("Expected AST:\n{expected_ast:#?}\n\n"); diff --git a/sloth/src/parser/mod.rs b/sloth/src/parser/mod.rs index bc47ddd..3328137 100644 --- a/sloth/src/parser/mod.rs +++ b/sloth/src/parser/mod.rs @@ -3,8 +3,9 @@ pub mod expr; pub mod graph; pub mod stmt; -use self::ast::{Literal, Stmt}; +use self::ast::{Literal, Stmt, StmtKind}; use crate::lexer::{Token, TokenType}; +use crate::symtable::SymbolTable; #[derive(thiserror::Error, Debug, PartialEq)] pub enum ParsingError { @@ -19,28 +20,36 @@ pub struct AstParser<'a> { tokens: Vec>, index: usize, id: i32, + top: SymbolTable, } impl<'a> AstParser<'a> { - pub fn parse(tokens: Vec>) -> Result, ParsingError> { - let mut parser = Self::new(tokens); + pub fn parse(tokens: Vec>, root: SymbolTable) -> Result { + let mut parser = Self::new(tokens, root); let mut statements = Vec::new(); while !parser.eof() { statements.push(parser.statement()?); } - Ok(statements) + let root = Stmt::new( + parser.reserve_id(), + StmtKind::Block(statements), + parser.top.clone(), + ); + + Ok(root) } } /// Implementation containing utilities used by the parsers internal components impl<'a> AstParser<'a> { - pub fn new(tokens: Vec>) -> Self { + pub fn new(tokens: Vec>, root: SymbolTable) -> Self { Self { tokens, index: 0, id: 0, + top: root, } } @@ -109,6 +118,14 @@ impl<'a> AstParser<'a> { id } + pub fn push_table(&mut self) { + self.top = self.top.make_child(); + } + + pub fn pop_table(&mut self) { + self.top = self.top.parent().unwrap(); + } + pub fn eof(&self) -> bool { self.index >= self.tokens.len() } diff --git a/sloth/src/parser/stmt.rs b/sloth/src/parser/stmt.rs index 2370311..c7433c2 100644 --- a/sloth/src/parser/stmt.rs +++ b/sloth/src/parser/stmt.rs @@ -42,7 +42,7 @@ impl<'a> AstParser<'a> { else_then: else_then.map(|it| it.into()), }; - Ok(Stmt::new(self.reserve_id(), kind)) + Ok(Stmt::new(self.reserve_id(), kind, self.top.clone())) } fn while_stmt(&mut self) -> Result { @@ -57,7 +57,7 @@ impl<'a> AstParser<'a> { body: body.into(), }; - Ok(Stmt::new(self.reserve_id(), kind)) + Ok(Stmt::new(self.reserve_id(), kind, self.top.clone())) } // TODO: Make variable types optional @@ -82,7 +82,7 @@ impl<'a> AstParser<'a> { typ, }; - Ok(Stmt::new(self.reserve_id(), kind)) + Ok(Stmt::new(self.reserve_id(), kind, self.top.clone())) } // TODO: Make argument types optional @@ -126,7 +126,7 @@ impl<'a> AstParser<'a> { body: body.into(), }; - Ok(Stmt::new(self.reserve_id(), kind)) + Ok(Stmt::new(self.reserve_id(), kind, self.top.clone())) } fn return_stmt(&mut self) -> Result { @@ -134,7 +134,7 @@ 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)) + Ok(Stmt::new(self.reserve_id(), kind, self.top.clone())) } fn assign_variable(&mut self) -> Result { @@ -143,32 +143,43 @@ 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)) + Ok(Stmt::new(self.reserve_id(), 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)) + Ok(Stmt::new(self.reserve_id(), kind, self.top.clone())) } fn block(&mut self) -> Result { - // Consume the opening brace - self.consume(TokenType::OpeningBrace, "Expected '{'")?; + // This inner function exists to make cleanup of the pushed symbol table easier + // in the case of a parsing error. + fn inner(this: &mut AstParser) -> Result { + // Consume the opening brace + this.consume(TokenType::OpeningBrace, "Expected '{'")?; + + // Get the body of the block + let mut body = Vec::new(); + while !this.eof() && this.peek().tt != TokenType::ClosingBrace { + body.push(this.statement()?); + } - // Get the body of the block - let mut body = Vec::new(); - while !self.eof() && self.peek().tt != TokenType::ClosingBrace { - body.push(self.statement()?); - } + // Consume the closing brace + this.consume(TokenType::ClosingBrace, "Expected '}'")?; + + let kind = StmtKind::Block(body); - // Consume the closing brace - self.consume(TokenType::ClosingBrace, "Expected '}'")?; + Ok(Stmt::new(this.reserve_id(), kind, this.top.clone())) + } - let kind = StmtKind::Block(body); + // Push a table, call the inner function and then pop that table + self.push_table(); + let result = inner(self); + self.pop_table(); - Ok(Stmt::new(self.reserve_id(), kind)) + result } } @@ -179,26 +190,27 @@ mod tests { use super::{AstParser, StmtKind}; use crate::lexer::Lexer; use crate::parser::ast::{BinaryOp, Expr, ExprKind, FunctionInput, Literal, Stmt}; + use crate::symtable::SymbolTable; #[test] fn standalone_blocks() { let tokens = Lexer::new("{{{ 0; }}}").collect_vec(); - let expected_ast = Ok(Stmt::new( + let expected_ast = Ok(Stmt::without_table( 4, - StmtKind::Block(vec![Stmt::new( + StmtKind::Block(vec![Stmt::without_table( 3, - StmtKind::Block(vec![Stmt::new( + StmtKind::Block(vec![Stmt::without_table( 2, - StmtKind::Block(vec![Stmt::new( + StmtKind::Block(vec![Stmt::without_table( 1, - StmtKind::ExprStmt(Expr::new(0, Literal::Integer(0).into())), + StmtKind::ExprStmt(Expr::without_table(0, Literal::Integer(0).into())), )]), )]), )]), )); - let mut parser = AstParser::new(tokens); + let mut parser = AstParser::new(tokens, SymbolTable::new()); let generated_ast = parser.statement(); println!("Expected AST:\n{expected_ast:#?}\n\n"); @@ -211,17 +223,23 @@ mod tests { fn basic_variable_definition() { let tokens = Lexer::new("var foo: Int = 5 + 3;").collect_vec(); - let expected_ast = Ok(Stmt::new(3, StmtKind::DefineVariable { + let expected_ast = Ok(Stmt::without_table(3, StmtKind::DefineVariable { identifier: "foo".to_string(), - value: Expr::new(2, ExprKind::BinaryOp { + value: Expr::without_table(2, ExprKind::BinaryOp { op: BinaryOp::Add, - lhs: Box::new(Expr::new(0, ExprKind::Literal(Literal::Integer(5)))), - rhs: Box::new(Expr::new(1, ExprKind::Literal(Literal::Integer(3)))), + lhs: Box::new(Expr::without_table( + 0, + ExprKind::Literal(Literal::Integer(5)), + )), + rhs: Box::new(Expr::without_table( + 1, + ExprKind::Literal(Literal::Integer(3)), + )), }), typ: "Int".to_string(), })); - let mut parser = AstParser::new(tokens); + let mut parser = AstParser::new(tokens, SymbolTable::new()); let generated_ast = parser.statement(); println!("Expected AST:\n{expected_ast:#?}\n\n"); @@ -243,42 +261,51 @@ mod tests { ) .collect_vec(); - let expected_ast = Ok(Stmt::new(11, StmtKind::DefineFunction { + let expected_ast = Ok(Stmt::without_table(11, StmtKind::DefineFunction { identifier: "foo".to_owned(), inputs: vec![FunctionInput { identifier: "bar".to_owned(), typ: "Int".to_owned(), }], output: Some("Int".to_owned()), - body: Box::new(Stmt::new( + body: Box::new(Stmt::without_table( 10, StmtKind::Block(vec![ - Stmt::new(3, StmtKind::DefineVariable { + Stmt::without_table(3, StmtKind::DefineVariable { identifier: "baz".to_owned(), - value: Expr::new(2, ExprKind::BinaryOp { + value: Expr::without_table(2, ExprKind::BinaryOp { op: BinaryOp::Add, - lhs: Box::new(Expr::new(0, ExprKind::Identifier("bar".to_owned()))), - rhs: Box::new(Expr::new(1, Literal::Integer(1).into())), + lhs: Box::new(Expr::without_table( + 0, + ExprKind::Identifier("bar".to_owned()), + )), + rhs: Box::new(Expr::without_table(1, Literal::Integer(1).into())), }), typ: "Int".to_owned(), }), - Stmt::new(7, StmtKind::AssignVariable { + Stmt::without_table(7, StmtKind::AssignVariable { identifier: "baz".to_owned(), - value: Expr::new(6, ExprKind::BinaryOp { + value: Expr::without_table(6, ExprKind::BinaryOp { op: BinaryOp::Add, - lhs: Box::new(Expr::new(4, ExprKind::Identifier("baz".to_owned()))), - rhs: Box::new(Expr::new(5, Literal::Integer(1).into())), + lhs: Box::new(Expr::without_table( + 4, + ExprKind::Identifier("baz".to_owned()), + )), + rhs: Box::new(Expr::without_table(5, Literal::Integer(1).into())), }), }), - Stmt::new( + Stmt::without_table( 9, - StmtKind::Return(Expr::new(8, ExprKind::Identifier("baz".to_owned()))), + StmtKind::Return(Expr::without_table( + 8, + ExprKind::Identifier("baz".to_owned()), + )), ), ]), )), })); - let mut parser = AstParser::new(tokens); + let mut parser = AstParser::new(tokens, SymbolTable::new()); let generated_ast = parser.statement(); println!("Expected AST:\n{expected_ast:#?}\n\n"); @@ -304,45 +331,45 @@ mod tests { ) .collect_vec(); - let expected_ast = Ok(Stmt::new(17, StmtKind::IfStmt { - condition: Expr::new(0, ExprKind::Identifier("foo".to_owned())), - if_then: Box::new(Stmt::new( + let expected_ast = Ok(Stmt::without_table(17, StmtKind::IfStmt { + condition: Expr::without_table(0, ExprKind::Identifier("foo".to_owned())), + if_then: Box::new(Stmt::without_table( 3, - StmtKind::Block(vec![Stmt::new( + StmtKind::Block(vec![Stmt::without_table( 2, - StmtKind::ExprStmt(Expr::new(1, Literal::Integer(0).into())), + StmtKind::ExprStmt(Expr::without_table(1, Literal::Integer(0).into())), )]), )), - else_then: Some(Box::new(Stmt::new(16, StmtKind::IfStmt { - condition: Expr::new(4, ExprKind::Identifier("bar".to_owned())), - if_then: Box::new(Stmt::new( + else_then: Some(Box::new(Stmt::without_table(16, StmtKind::IfStmt { + condition: Expr::without_table(4, ExprKind::Identifier("bar".to_owned())), + if_then: Box::new(Stmt::without_table( 7, - StmtKind::Block(vec![Stmt::new( + StmtKind::Block(vec![Stmt::without_table( 6, - StmtKind::ExprStmt(Expr::new(5, Literal::Integer(1).into())), + StmtKind::ExprStmt(Expr::without_table(5, Literal::Integer(1).into())), )]), )), - else_then: Some(Box::new(Stmt::new(15, StmtKind::IfStmt { - condition: Expr::new(8, ExprKind::Identifier("baz".to_owned())), - if_then: Box::new(Stmt::new( + else_then: Some(Box::new(Stmt::without_table(15, StmtKind::IfStmt { + condition: Expr::without_table(8, ExprKind::Identifier("baz".to_owned())), + if_then: Box::new(Stmt::without_table( 11, - StmtKind::Block(vec![Stmt::new( + StmtKind::Block(vec![Stmt::without_table( 10, - StmtKind::ExprStmt(Expr::new(9, Literal::Integer(2).into())), + StmtKind::ExprStmt(Expr::without_table(9, Literal::Integer(2).into())), )]), )), - else_then: Some(Box::new(Stmt::new( + else_then: Some(Box::new(Stmt::without_table( 14, - StmtKind::Block(vec![Stmt::new( + StmtKind::Block(vec![Stmt::without_table( 13, - StmtKind::ExprStmt(Expr::new(12, Literal::Integer(3).into())), + StmtKind::ExprStmt(Expr::without_table(12, Literal::Integer(3).into())), )]), ))), }))), }))), })); - let mut parser = AstParser::new(tokens); + let mut parser = AstParser::new(tokens, SymbolTable::new()); let generated_ast = parser.statement(); println!("Expected AST:\n{expected_ast:#?}\n\n"); diff --git a/sloth/src/symtable.rs b/sloth/src/symtable.rs index 29ea210..2cb4741 100644 --- a/sloth/src/symtable.rs +++ b/sloth/src/symtable.rs @@ -27,6 +27,10 @@ impl SymbolTable { })) } + pub fn parent(&self) -> Option { + Some(Self(self.0.parent.clone()?)) + } + pub fn contains(&self, identifier: &str) -> bool { for scope in self.iter() { if scope.symbols.borrow().contains_key(identifier) { @@ -59,7 +63,7 @@ impl SymbolTable { None } - pub fn insert(&self, identifier: String, symbol: Symbol) -> bool { + pub fn insert(&mut self, identifier: String, symbol: Symbol) -> bool { let mut reference = self.0.symbols.borrow_mut(); if let Vacant(e) = reference.entry(identifier) { e.insert(symbol); @@ -105,10 +109,17 @@ impl<'a> Iterator for Iter<'a> { #[derive(Debug)] pub struct Symbol { - pub typ: Option, + pub typ: SymbolType, +} + +impl Symbol { + pub fn new(typ: SymbolType) -> Self { + Self { typ } + } } #[derive(Debug)] pub enum SymbolType { + Variable, Function, } -- cgit v1.2.3