From ec70ded29ef63fb8390538e12b5cc4f8cc0db369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Sat, 7 Jan 2023 08:48:21 +0100 Subject: [PATCH] Fixed variable lookups and assignments --- src/ast.rs | 22 ++++++++++++++-------- src/interpreter/interpretable.rs | 14 +++++++------- src/parser.rs | 13 ++++++++++++- src/resolver.rs | 24 ++++++++++++++---------- 4 files changed, 47 insertions(+), 26 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index c812d44..3da8065 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -65,7 +65,12 @@ impl StmtNode { #[derive(Debug, Clone)] pub enum ExprNode { /// Assignment to a variable. - Assignment { name: Token, value: Box }, + Assignment { + name: Token, + value: Box, + /// Identifier used for variable resolution. + id: usize, + }, /// Logical binary expression. Logical { @@ -94,7 +99,11 @@ pub enum ExprNode { Litteral { value: Token }, /// A reference to a variable. - Variable { name: Token }, + Variable { + name: Token, + /// Identifier used for variable resolution. + id: usize, + }, /// A lambda function. Lambda { @@ -227,7 +236,7 @@ impl AstDumper for StmtNode { impl AstDumper for ExprNode { fn dump(&self) -> String { match self { - Self::Assignment { name, value } => format!("( = {} {} )", name.lexeme, value.dump()), + Self::Assignment { name, value, id } => format!("{{#{}}}( = {} {} )", id, name.lexeme, value.dump()), Self::Logical { left, operator, @@ -240,7 +249,7 @@ impl AstDumper for ExprNode { } => format!("( {} {} {} )", operator.lexeme, left.dump(), right.dump()), Self::Unary { operator, right } => format!("( {} {} )", operator.lexeme, right.dump()), Self::Grouping { expression } => format!("( {} )", expression.dump()), - Self::Variable { name } => name.lexeme.clone(), + Self::Variable { name, id } => format!("{{#{}}}{}", id, name.lexeme), Self::Litteral { value } => { if value.is_litteral() { value.lexeme.clone() @@ -249,10 +258,7 @@ impl AstDumper for ExprNode { } } - ExprNode::Lambda { - params, - body, - } => { + ExprNode::Lambda { params, body } => { format!( "( fun ({}) {} )", params diff --git a/src/interpreter/interpretable.rs b/src/interpreter/interpretable.rs index f5c547f..602e076 100644 --- a/src/interpreter/interpretable.rs +++ b/src/interpreter/interpretable.rs @@ -49,15 +49,15 @@ impl<'a> InterpreterState<'a> { } } - fn lookup_var(&self, name: &Token, expr: &ast::ExprNode) -> SloxResult { - match self.variables.get(&(expr as *const ast::ExprNode)) { + fn lookup_var(&self, name: &Token, expr_id: &usize) -> SloxResult { + match self.variables.get(expr_id) { Some(distance) => self.environment.borrow().get_at(*distance, name), None => self.globals.borrow().get(name), } } - fn assign_var(&self, name: &Token, expr: &ast::ExprNode, value: Value) -> SloxResult<()> { - match self.variables.get(&(expr as *const ast::ExprNode)) { + fn assign_var(&self, name: &Token, expr_id: &usize, value: Value) -> SloxResult<()> { + match self.variables.get(expr_id) { Some(distance) => self .environment .borrow_mut() @@ -311,9 +311,9 @@ impl ast::StmtNode { impl Interpretable for ast::ExprNode { fn interpret(&self, es: &mut InterpreterState) -> InterpreterResult { match self { - ast::ExprNode::Assignment { name, value } => { + ast::ExprNode::Assignment { name, value, id } => { let value = value.interpret(es)?.result(); - es.assign_var(name, &self, value)?; + es.assign_var(name, id, value)?; Ok(InterpreterFlowControl::default()) } ast::ExprNode::Logical { @@ -329,7 +329,7 @@ impl Interpretable for ast::ExprNode { ast::ExprNode::Unary { operator, right } => self.on_unary(es, operator, right), ast::ExprNode::Grouping { expression } => expression.interpret(es), ast::ExprNode::Litteral { value } => self.on_litteral(value), - ast::ExprNode::Variable { name } => Ok(es.lookup_var(name, &self)?.into()), + ast::ExprNode::Variable { name, id } => Ok(es.lookup_var(name, id)?.into()), ast::ExprNode::Call { callee, right_paren, diff --git a/src/parser.rs b/src/parser.rs index b5ca6e6..4f7f1b1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -12,6 +12,7 @@ pub struct Parser { tokens: Vec, current: usize, loop_state: Vec, + next_id: usize, } /// The state of the parser regarding loops. We may be parsing an unnamed or @@ -69,6 +70,7 @@ impl Parser { tokens, current: 0, loop_state: Vec::default(), + next_id: 0, } } @@ -511,10 +513,11 @@ impl Parser { let expr = self.parse_logic_or()?; if let Some(equals) = self.expect(&[TokenType::Equal]) { let value = self.parse_assignment()?; - if let ast::ExprNode::Variable { name } = expr { + if let ast::ExprNode::Variable { name, id: _ } = expr { Ok(ast::ExprNode::Assignment { name, value: Box::new(value), + id: self.make_id(), }) } else { Err(SloxError::with_token( @@ -688,6 +691,7 @@ impl Parser { }), TokenType::Identifier(_) => Ok(ast::ExprNode::Variable { name: self.advance().clone(), + id: self.make_id(), }), _ => self.error("expected expression"), } @@ -830,4 +834,11 @@ impl Parser { message, )) } + + /// Generate an identifier and return it. + fn make_id(&mut self) -> usize { + let id = self.next_id; + self.next_id += 1; + id + } } diff --git a/src/resolver.rs b/src/resolver.rs index 6ceb280..d2369eb 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -9,7 +9,7 @@ use crate::{ /// Resolved variables. Pointers to the AST nodes using the variables are /// associated with the relative depth at which the variable definition will be /// found. -pub type ResolvedVariables = HashMap<*const ast::ExprNode, usize>; +pub type ResolvedVariables = HashMap; /// Resolve all variables in a program's AST. pub fn resolve_variables(program: &ast::ProgramNode) -> SloxResult { @@ -80,25 +80,29 @@ impl ResolverState { /// Try to resolve some access to a variable. If a local variable is found /// matching the specified name, add it to the resolution map. - fn resolve_local(&mut self, expr: &ast::ExprNode, name: &Token) { + fn resolve_local(&mut self, expr_id: &usize, name: &Token) { let mut i = self.scopes.len(); while i != 0 { i -= 1; if self.scopes[i].contains_key(&name.lexeme as &str) { - self.mark_resolved(expr, self.scopes.len() - 1 - i); + self.mark_resolved(expr_id, self.scopes.len() - 1 - i); return; } } } /// Add an entry to the resolution map for an AST node. - fn mark_resolved(&mut self, expr: &ast::ExprNode, depth: usize) { - self.resolved.insert(expr as *const ast::ExprNode, depth); + fn mark_resolved(&mut self, expr_id: &usize, depth: usize) { + self.resolved.insert(*expr_id, depth); } } /// Process a function declaration. -fn resolve_function(rs: &mut ResolverState, params: &[Token], body: &Vec) -> ResolverResult { +fn resolve_function( + rs: &mut ResolverState, + params: &[Token], + body: &Vec, +) -> ResolverResult { rs.begin_scope(); for param in params { rs.declare(param)?; @@ -214,7 +218,7 @@ impl VarResolver for ast::StmtNode { impl VarResolver for ast::ExprNode { fn resolve(&self, rs: &mut ResolverState) -> ResolverResult { match self { - ast::ExprNode::Variable { name } => { + ast::ExprNode::Variable { name, id } => { if rs.check(&name.lexeme) == Some(false) { Err(SloxError::with_token( ErrorKind::Parse, @@ -222,14 +226,14 @@ impl VarResolver for ast::ExprNode { "can't read local variable in its own initializer".to_owned(), )) } else { - rs.resolve_local(self, name); + rs.resolve_local(id, name); Ok(()) } } - ast::ExprNode::Assignment { name, value } => { + ast::ExprNode::Assignment { name, value, id } => { value.resolve(rs)?; - rs.resolve_local(self, name); + rs.resolve_local(id, name); Ok(()) }