Implement assignment
parent
6f7e2eb499
commit
b4e1fa7690
1
build.rs
1
build.rs
|
@ -40,6 +40,7 @@ fn do_ast_codegen<W: Write>(mut output: W) {
|
||||||
writeln!(output).unwrap();
|
writeln!(output).unwrap();
|
||||||
|
|
||||||
let expr_types = BTreeMap::from([
|
let expr_types = BTreeMap::from([
|
||||||
|
("Assign", "name: Token, value: Box<Expr>"),
|
||||||
(
|
(
|
||||||
"Binary",
|
"Binary",
|
||||||
"left: Box<Expr>, operator: Token, right: Box<Expr>",
|
"left: Box<Expr>, operator: Token, right: Box<Expr>",
|
||||||
|
|
15
src/eval.rs
15
src/eval.rs
|
@ -178,6 +178,21 @@ impl ExprVisitor<Result<EvaluatedValue, ScriptError>> for InterpreterRunner<'_>
|
||||||
})
|
})
|
||||||
.cloned()
|
.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<'_> {
|
impl StmtVisitor<Result<(), ScriptError>> for InterpreterRunner<'_> {
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::{
|
||||||
|
borrow::BorrowMut,
|
||||||
|
collections::{hash_map::Entry, BTreeMap, HashMap},
|
||||||
|
};
|
||||||
|
|
||||||
use super::value::EvaluatedValue;
|
use super::value::EvaluatedValue;
|
||||||
|
|
||||||
|
@ -75,8 +78,8 @@ impl Environment {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_value(&mut self, name: &str, value: EvaluatedValue) {
|
pub fn set_value(&mut self, name: &str, value: EvaluatedValue) -> &EvaluatedValue {
|
||||||
self.current_scope_mut().set(name, value);
|
self.current_scope_mut().set(name, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn current_scope(&self) -> &Scope {
|
fn current_scope(&self) -> &Scope {
|
||||||
|
@ -123,8 +126,15 @@ impl Scope {
|
||||||
self.values.get(name)
|
self.values.get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set<S: Into<String>>(&mut self, name: S, value: EvaluatedValue) {
|
fn set<S: Into<String>>(&mut self, name: S, value: EvaluatedValue) -> &EvaluatedValue {
|
||||||
self.values.insert(name.into(), value);
|
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"));
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,10 @@ impl ExprVisitor<String> for ASTPrinter {
|
||||||
fn visit_variable(&mut self, name: &lex::Token) -> String {
|
fn visit_variable(&mut self, name: &lex::Token) -> String {
|
||||||
name.lexeme().to_owned()
|
name.lexeme().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_assign(&mut self, name: &lex::Token, value: &Expr) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
23
src/parse.rs
23
src/parse.rs
|
@ -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> {
|
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> {
|
fn parse_equality<I: Iterator<Item = Token>>(iter: &mut Peekable<I>) -> Result<Expr, ParseError> {
|
||||||
|
|
Loading…
Reference in New Issue