Fixed variable lookups and assignments

This commit is contained in:
Emmanuel BENOîT 2023-01-07 08:48:21 +01:00
parent 3117dfeac5
commit ec70ded29e
4 changed files with 47 additions and 26 deletions

View file

@ -65,7 +65,12 @@ impl StmtNode {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ExprNode { pub enum ExprNode {
/// Assignment to a variable. /// Assignment to a variable.
Assignment { name: Token, value: Box<ExprNode> }, Assignment {
name: Token,
value: Box<ExprNode>,
/// Identifier used for variable resolution.
id: usize,
},
/// Logical binary expression. /// Logical binary expression.
Logical { Logical {
@ -94,7 +99,11 @@ pub enum ExprNode {
Litteral { value: Token }, Litteral { value: Token },
/// A reference to a variable. /// A reference to a variable.
Variable { name: Token }, Variable {
name: Token,
/// Identifier used for variable resolution.
id: usize,
},
/// A lambda function. /// A lambda function.
Lambda { Lambda {
@ -227,7 +236,7 @@ impl AstDumper for StmtNode {
impl AstDumper for ExprNode { impl AstDumper for ExprNode {
fn dump(&self) -> String { fn dump(&self) -> String {
match self { match self {
Self::Assignment { name, value } => format!("( = {} {} )", name.lexeme, value.dump()), Self::Assignment { name, value, id } => format!("{{#{}}}( = {} {} )", id, name.lexeme, value.dump()),
Self::Logical { Self::Logical {
left, left,
operator, operator,
@ -240,7 +249,7 @@ impl AstDumper for ExprNode {
} => format!("( {} {} {} )", operator.lexeme, left.dump(), right.dump()), } => format!("( {} {} {} )", operator.lexeme, left.dump(), right.dump()),
Self::Unary { operator, right } => format!("( {} {} )", operator.lexeme, right.dump()), Self::Unary { operator, right } => format!("( {} {} )", operator.lexeme, right.dump()),
Self::Grouping { expression } => format!("( {} )", expression.dump()), Self::Grouping { expression } => format!("( {} )", expression.dump()),
Self::Variable { name } => name.lexeme.clone(), Self::Variable { name, id } => format!("{{#{}}}{}", id, name.lexeme),
Self::Litteral { value } => { Self::Litteral { value } => {
if value.is_litteral() { if value.is_litteral() {
value.lexeme.clone() value.lexeme.clone()
@ -249,10 +258,7 @@ impl AstDumper for ExprNode {
} }
} }
ExprNode::Lambda { ExprNode::Lambda { params, body } => {
params,
body,
} => {
format!( format!(
"( fun ({}) {} )", "( fun ({}) {} )",
params params

View file

@ -49,15 +49,15 @@ impl<'a> InterpreterState<'a> {
} }
} }
fn lookup_var(&self, name: &Token, expr: &ast::ExprNode) -> SloxResult<Value> { fn lookup_var(&self, name: &Token, expr_id: &usize) -> SloxResult<Value> {
match self.variables.get(&(expr as *const ast::ExprNode)) { match self.variables.get(expr_id) {
Some(distance) => self.environment.borrow().get_at(*distance, name), Some(distance) => self.environment.borrow().get_at(*distance, name),
None => self.globals.borrow().get(name), None => self.globals.borrow().get(name),
} }
} }
fn assign_var(&self, name: &Token, expr: &ast::ExprNode, value: Value) -> SloxResult<()> { fn assign_var(&self, name: &Token, expr_id: &usize, value: Value) -> SloxResult<()> {
match self.variables.get(&(expr as *const ast::ExprNode)) { match self.variables.get(expr_id) {
Some(distance) => self Some(distance) => self
.environment .environment
.borrow_mut() .borrow_mut()
@ -311,9 +311,9 @@ impl ast::StmtNode {
impl Interpretable for ast::ExprNode { impl Interpretable for ast::ExprNode {
fn interpret(&self, es: &mut InterpreterState) -> InterpreterResult { fn interpret(&self, es: &mut InterpreterState) -> InterpreterResult {
match self { match self {
ast::ExprNode::Assignment { name, value } => { ast::ExprNode::Assignment { name, value, id } => {
let value = value.interpret(es)?.result(); let value = value.interpret(es)?.result();
es.assign_var(name, &self, value)?; es.assign_var(name, id, value)?;
Ok(InterpreterFlowControl::default()) Ok(InterpreterFlowControl::default())
} }
ast::ExprNode::Logical { 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::Unary { operator, right } => self.on_unary(es, operator, right),
ast::ExprNode::Grouping { expression } => expression.interpret(es), ast::ExprNode::Grouping { expression } => expression.interpret(es),
ast::ExprNode::Litteral { value } => self.on_litteral(value), 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 { ast::ExprNode::Call {
callee, callee,
right_paren, right_paren,

View file

@ -12,6 +12,7 @@ pub struct Parser {
tokens: Vec<Token>, tokens: Vec<Token>,
current: usize, current: usize,
loop_state: Vec<LoopParsingState>, loop_state: Vec<LoopParsingState>,
next_id: usize,
} }
/// The state of the parser regarding loops. We may be parsing an unnamed or /// The state of the parser regarding loops. We may be parsing an unnamed or
@ -69,6 +70,7 @@ impl Parser {
tokens, tokens,
current: 0, current: 0,
loop_state: Vec::default(), loop_state: Vec::default(),
next_id: 0,
} }
} }
@ -511,10 +513,11 @@ impl Parser {
let expr = self.parse_logic_or()?; let expr = self.parse_logic_or()?;
if let Some(equals) = self.expect(&[TokenType::Equal]) { if let Some(equals) = self.expect(&[TokenType::Equal]) {
let value = self.parse_assignment()?; let value = self.parse_assignment()?;
if let ast::ExprNode::Variable { name } = expr { if let ast::ExprNode::Variable { name, id: _ } = expr {
Ok(ast::ExprNode::Assignment { Ok(ast::ExprNode::Assignment {
name, name,
value: Box::new(value), value: Box::new(value),
id: self.make_id(),
}) })
} else { } else {
Err(SloxError::with_token( Err(SloxError::with_token(
@ -688,6 +691,7 @@ impl Parser {
}), }),
TokenType::Identifier(_) => Ok(ast::ExprNode::Variable { TokenType::Identifier(_) => Ok(ast::ExprNode::Variable {
name: self.advance().clone(), name: self.advance().clone(),
id: self.make_id(),
}), }),
_ => self.error("expected expression"), _ => self.error("expected expression"),
} }
@ -830,4 +834,11 @@ impl Parser {
message, message,
)) ))
} }
/// Generate an identifier and return it.
fn make_id(&mut self) -> usize {
let id = self.next_id;
self.next_id += 1;
id
}
} }

View file

@ -9,7 +9,7 @@ use crate::{
/// Resolved variables. Pointers to the AST nodes using the variables are /// Resolved variables. Pointers to the AST nodes using the variables are
/// associated with the relative depth at which the variable definition will be /// associated with the relative depth at which the variable definition will be
/// found. /// found.
pub type ResolvedVariables = HashMap<*const ast::ExprNode, usize>; pub type ResolvedVariables = HashMap<usize, usize>;
/// Resolve all variables in a program's AST. /// Resolve all variables in a program's AST.
pub fn resolve_variables(program: &ast::ProgramNode) -> SloxResult<ResolvedVariables> { pub fn resolve_variables(program: &ast::ProgramNode) -> SloxResult<ResolvedVariables> {
@ -80,25 +80,29 @@ impl ResolverState {
/// Try to resolve some access to a variable. If a local variable is found /// Try to resolve some access to a variable. If a local variable is found
/// matching the specified name, add it to the resolution map. /// 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(); let mut i = self.scopes.len();
while i != 0 { while i != 0 {
i -= 1; i -= 1;
if self.scopes[i].contains_key(&name.lexeme as &str) { 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; return;
} }
} }
} }
/// Add an entry to the resolution map for an AST node. /// Add an entry to the resolution map for an AST node.
fn mark_resolved(&mut self, expr: &ast::ExprNode, depth: usize) { fn mark_resolved(&mut self, expr_id: &usize, depth: usize) {
self.resolved.insert(expr as *const ast::ExprNode, depth); self.resolved.insert(*expr_id, depth);
} }
} }
/// Process a function declaration. /// Process a function declaration.
fn resolve_function(rs: &mut ResolverState, params: &[Token], body: &Vec<ast::StmtNode>) -> ResolverResult { fn resolve_function(
rs: &mut ResolverState,
params: &[Token],
body: &Vec<ast::StmtNode>,
) -> ResolverResult {
rs.begin_scope(); rs.begin_scope();
for param in params { for param in params {
rs.declare(param)?; rs.declare(param)?;
@ -214,7 +218,7 @@ impl VarResolver for ast::StmtNode {
impl VarResolver for ast::ExprNode { impl VarResolver for ast::ExprNode {
fn resolve(&self, rs: &mut ResolverState) -> ResolverResult { fn resolve(&self, rs: &mut ResolverState) -> ResolverResult {
match self { match self {
ast::ExprNode::Variable { name } => { ast::ExprNode::Variable { name, id } => {
if rs.check(&name.lexeme) == Some(false) { if rs.check(&name.lexeme) == Some(false) {
Err(SloxError::with_token( Err(SloxError::with_token(
ErrorKind::Parse, ErrorKind::Parse,
@ -222,14 +226,14 @@ impl VarResolver for ast::ExprNode {
"can't read local variable in its own initializer".to_owned(), "can't read local variable in its own initializer".to_owned(),
)) ))
} else { } else {
rs.resolve_local(self, name); rs.resolve_local(id, name);
Ok(()) Ok(())
} }
} }
ast::ExprNode::Assignment { name, value } => { ast::ExprNode::Assignment { name, value, id } => {
value.resolve(rs)?; value.resolve(rs)?;
rs.resolve_local(self, name); rs.resolve_local(id, name);
Ok(()) Ok(())
} }