Fixed variable lookups and assignments
This commit is contained in:
parent
3117dfeac5
commit
ec70ded29e
4 changed files with 47 additions and 26 deletions
22
src/ast.rs
22
src/ast.rs
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue