Implement assignment

master
Nick Krichevsky 2024-05-22 15:08:02 -04:00
parent 6f7e2eb499
commit b4e1fa7690
5 changed files with 67 additions and 6 deletions

View File

@ -40,6 +40,7 @@ fn do_ast_codegen<W: Write>(mut output: W) {
writeln!(output).unwrap();
let expr_types = BTreeMap::from([
("Assign", "name: Token, value: Box<Expr>"),
(
"Binary",
"left: Box<Expr>, operator: Token, right: Box<Expr>",

View File

@ -178,6 +178,21 @@ impl ExprVisitor<Result<EvaluatedValue, ScriptError>> for InterpreterRunner<'_>
})
.cloned()
}
fn visit_assign(&mut self, name: &Token, value: &Expr) -> Result<EvaluatedValue, ScriptError> {
if self.interpreter.env.get_value(name.lexeme()).is_none() {
return Err(ScriptError {
line: name.line(),
location: String::new(),
message: format!("Cannot assign to undefined variable {}", name.lexeme()),
});
}
let value = self.visit_expr(value)?;
let inserted_value = self.interpreter.env.set_value(name.lexeme(), value);
Ok(inserted_value.clone())
}
}
impl StmtVisitor<Result<(), ScriptError>> for InterpreterRunner<'_> {

View File

@ -1,4 +1,7 @@
use std::collections::{BTreeMap, HashMap};
use std::{
borrow::BorrowMut,
collections::{hash_map::Entry, BTreeMap, HashMap},
};
use super::value::EvaluatedValue;
@ -75,8 +78,8 @@ impl Environment {
None
}
pub fn set_value(&mut self, name: &str, value: EvaluatedValue) {
self.current_scope_mut().set(name, value);
pub fn set_value(&mut self, name: &str, value: EvaluatedValue) -> &EvaluatedValue {
self.current_scope_mut().set(name, value)
}
fn current_scope(&self) -> &Scope {
@ -123,8 +126,15 @@ impl Scope {
self.values.get(name)
}
fn set<S: Into<String>>(&mut self, name: S, value: EvaluatedValue) {
self.values.insert(name.into(), value);
fn set<S: Into<String>>(&mut self, name: S, value: EvaluatedValue) -> &EvaluatedValue {
match self.values.entry(name.into()) {
Entry::Vacant(entry) => entry.insert(value),
Entry::Occupied(entry) => {
let item = entry.into_mut();
*item = value;
item
}
}
}
}
@ -184,4 +194,14 @@ mod tests {
assert_eq!(None, global.get_value(r"foo"));
}
#[test]
fn test_inserting_value_returns_reference_to_inserted_item() {
let mut global = Environment::new();
global.enter_scope();
let inserted = global.set_value("foo", EvaluatedValue::Number(42_f64));
assert_eq!(&EvaluatedValue::Number(42_f64), inserted);
}
}

View File

@ -105,6 +105,10 @@ impl ExprVisitor<String> for ASTPrinter {
fn visit_variable(&mut self, name: &lex::Token) -> String {
name.lexeme().to_owned()
}
fn visit_assign(&mut self, name: &lex::Token, value: &Expr) -> String {
todo!()
}
}
#[cfg(test)]

View File

@ -155,7 +155,28 @@ fn parse_block_statement<I: Iterator<Item = Token>>(
}
fn parse_expression<I: Iterator<Item = Token>>(iter: &mut Peekable<I>) -> Result<Expr, ParseError> {
parse_equality(iter)
parse_assignment(iter)
}
fn parse_assignment<I: Iterator<Item = Token>>(iter: &mut Peekable<I>) -> Result<Expr, ParseError> {
let expr = parse_equality(iter)?;
match (match_token_kind!(iter, TokenKind::Equal), expr) {
(Err(_), expr) => Ok(expr),
(Ok(_), Expr::Variable { name }) => {
let rhs = parse_assignment(iter)?;
Ok(Expr::Assign {
name,
value: Box::new(rhs),
})
}
(Ok(equals_token), _) => Err(ParseError {
message: "The left hand side of an assignment must be an identifier".to_string(),
line: equals_token.line().into(),
}),
}
}
fn parse_equality<I: Iterator<Item = Token>>(iter: &mut Peekable<I>) -> Result<Expr, ParseError> {