Skip to content

Commit

Permalink
assignment syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
martonmoro committed Mar 10, 2025
1 parent ac63315 commit 9a3e333
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 6 deletions.
13 changes: 13 additions & 0 deletions src/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,17 @@ impl Environment {
})
}
}

pub fn assign(&mut self, name: &Token, value: Object) -> Result<(), Error> {
let key = &*name.lexeme;
if self.values.contains_key(key) {
self.values.insert(name.lexeme.clone(), value);
Ok(())
} else {
Err(Error::Runtime {
token: name.clone(),
message: format!("Undefined variable '{}'.", key),
})
}
}
}
6 changes: 6 additions & 0 deletions src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ impl expr::Visitor<Object> for Interpreter {
fn visit_variable_expr(&self, name: &Token) -> Result<Object, Error> {
self.environment.borrow().get(name)
}

fn visit_assign_expr(&self, name: &Token, value: &Expr) -> Result<Object, Error> {
let v = self.evaluate(value)?;
self.environment.borrow_mut().assign(name, v.clone())?;
Ok(v)
}
}

impl stmt::Visitor<()> for Interpreter {
Expand Down
28 changes: 26 additions & 2 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,33 @@ impl<'t> Parser<'t> {
Ok(Stmt::Var { name, initializer })
}

// expression → equality ;
// expression → assignment ;
fn expression(&mut self) -> Result<Expr, Error> {
self.equality()
self.assignment()
}

// The trick is that the parser first processes the left side as it it were an expression (r-value),
// then converts it to an assignment target (l-value) if an = sign follows
// This conversion works because it turns out that every valid assignment target happens to also be valid syntax as a normal expression.
// assignment → IDENTIFIER "=" assignment | equality ;
fn assignment(&mut self) -> Result<Expr, Error> {
let expr = self.equality()?;

if matches!(self, TokenType::Equal) {
// contrary to binary operators we don't loop to build up a sequence of the same operator
// since assignment is right-associative, we instead recurisvely call assignment() to parse the right hand side
let value = Box::new(self.assignment()?);

if let Expr::Variable { name } = expr {
return Ok(Expr::Assign { name, value });
}

let equals = self.previous();
// we are not throwing because the parser is not in a confused state where we need to go into panic mode and synchronize
self.error(equals, "Invalid assignment target.");
}

Ok(expr)
}

// equality → comparison ( ( "!=" | "==" ) comparison )* ;
Expand Down
12 changes: 8 additions & 4 deletions src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ impl AstPrinter {
expr.accept(self)
}

fn paranthesize(&self, name: String, exprs: Vec<&Expr>) -> Result<String, Error> {
fn parenthesize(&self, name: String, exprs: Vec<&Expr>) -> Result<String, Error> {
let mut builder = String::new();

builder.push_str("(");
Expand All @@ -165,24 +165,28 @@ impl expr::Visitor<String> for AstPrinter {
operator: &Token,
right: &Expr,
) -> Result<String, Error> {
self.paranthesize(operator.lexeme.clone(), vec![left, right])
self.parenthesize(operator.lexeme.clone(), vec![left, right])
}

fn visit_grouping_expr(&self, expression: &Expr) -> Result<String, Error> {
self.paranthesize("group".to_string(), vec![expression])
self.parenthesize("group".to_string(), vec![expression])
}

fn visit_literal_expr(&self, value: &LiteralValue) -> Result<String, Error> {
Ok(value.to_string())
}

fn visit_unary_expr(&self, operator: &Token, right: &Expr) -> Result<String, Error> {
self.paranthesize(operator.lexeme.clone(), vec![right])
self.parenthesize(operator.lexeme.clone(), vec![right])
}

fn visit_variable_expr(&self, name: &Token) -> Result<String, Error> {
Ok(name.lexeme.clone())
}

fn visit_assign_expr(&self, name: &Token, value: &Expr) -> Result<String, Error> {
self.parenthesize(name.lexeme.clone(), vec![value])
}
}

// test from the book
Expand Down

0 comments on commit 9a3e333

Please sign in to comment.