[rlox] Implement Assignment (for global variables)
This commit is contained in:
@@ -26,6 +26,7 @@ pub enum Op {
|
|||||||
|
|
||||||
DefineGlobal { offset: u8 },
|
DefineGlobal { offset: u8 },
|
||||||
GetGlobal { offset: u8 },
|
GetGlobal { offset: u8 },
|
||||||
|
SetGlobal { offset: u8 },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
|||||||
@@ -468,7 +468,14 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
TokenType::Identifier => {
|
TokenType::Identifier => {
|
||||||
let offset = self.add_string(chunk, token.span);
|
let offset = self.add_string(chunk, token.span);
|
||||||
chunk.add_op(Op::GetGlobal {offset}, token.line);
|
|
||||||
|
if self.scanner.peek().is_some_and(|t| t.ttype == TokenType::Equal) {
|
||||||
|
self.scanner.next();
|
||||||
|
self._expression(chunk, Precedence::Assignment)?;
|
||||||
|
chunk.add_op(Op::SetGlobal {offset}, token.line);
|
||||||
|
} else {
|
||||||
|
chunk.add_op(Op::GetGlobal {offset}, token.line);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(self.error_at(token, ParseErrorKind::IncompleteExpression));
|
return Err(self.error_at(token, ParseErrorKind::IncompleteExpression));
|
||||||
@@ -566,6 +573,7 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
match self.scanner.peek() {
|
match self.scanner.peek() {
|
||||||
Some(token) if token.ttype == TokenType::Equal => {
|
Some(token) if token.ttype == TokenType::Equal => {
|
||||||
|
self.scanner.next();
|
||||||
self.expression(chunk)?;
|
self.expression(chunk)?;
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
@@ -757,6 +765,7 @@ mod tests {
|
|||||||
let mut parser = Parser::new(scanner);
|
let mut parser = Parser::new(scanner);
|
||||||
let mut chunk = Chunk::new();
|
let mut chunk = Chunk::new();
|
||||||
parser.compile(&mut chunk);
|
parser.compile(&mut chunk);
|
||||||
|
|
||||||
assert_eq!(parser.errors, vec![]);
|
assert_eq!(parser.errors, vec![]);
|
||||||
assert!(chunk.instr_eq(expected));
|
assert!(chunk.instr_eq(expected));
|
||||||
}
|
}
|
||||||
@@ -897,17 +906,35 @@ mod tests {
|
|||||||
test_parse_program(source, &expected);
|
test_parse_program(source, &expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
fn basic_var_decl_with_initializer() {
|
fn basic_var_decl_with_initializer() {
|
||||||
let source = "var x = 1 + 1;";
|
let source = "var x = 1 + 1;";
|
||||||
use crate::bc::Op::*;
|
use crate::bc::Op::*;
|
||||||
let x = GC::new_string("x");
|
let x = GC::new_string("x");
|
||||||
let expected = Chunk::new_with(
|
let expected = Chunk::new_with(
|
||||||
vec![Constant {offset: 1}, Constant {offset: 2}, Add, DefineGlobal { offset: 0 }],
|
vec![Constant {offset: 1}, Constant {offset: 2}, Add, DefineGlobal { offset: 0 }],
|
||||||
vec![],
|
vec![1, 1, 1, 1],
|
||||||
vec![x.get_object().into(), Value::from(1.0), Value::from(1.0)],
|
vec![x.get_object().into(), Value::from(1.0), Value::from(1.0)],
|
||||||
LinkedList::new(),
|
LinkedList::new(),
|
||||||
);
|
);
|
||||||
|
|
||||||
test_parse_program(source, &expected);
|
test_parse_program(source, &expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assign() {
|
||||||
|
let source = "var x = y = z;";
|
||||||
|
use crate::bc::Op::*;
|
||||||
|
let x = GC::new_string("x");
|
||||||
|
let y = GC::new_string("y");
|
||||||
|
let z = GC::new_string("z");
|
||||||
|
let expected = Chunk::new_with(
|
||||||
|
vec![GetGlobal { offset: 2 }, SetGlobal { offset: 1 }, DefineGlobal { offset: 0 }],
|
||||||
|
vec![1, 1, 1],
|
||||||
|
vec![x.get_object().into(), y.get_object().into(), z.get_object().into()],
|
||||||
|
LinkedList::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
test_parse_program(source, &expected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::bc::{Chunk, Op, TraceInfo, Value};
|
use crate::bc::{Chunk, Op, TraceInfo, Value};
|
||||||
use crate::gc::{GcHandle, ObjString, ObjectType, GC};
|
use crate::gc::{GcHandle, ObjString, ObjectType, GC};
|
||||||
use std::collections::{HashMap, LinkedList};
|
use std::collections::{hash_map, HashMap, LinkedList};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@@ -203,12 +203,35 @@ impl VM {
|
|||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(
|
Err(
|
||||||
VMError::Runtime(
|
VMError::Runtime(
|
||||||
format!("Undefined variable '{}'.", ident).into(),
|
format!("Undefined variable '{}'.", ident).into(),
|
||||||
self.pc,
|
self.pc,
|
||||||
))
|
)
|
||||||
|
)
|
||||||
}?
|
}?
|
||||||
}
|
},
|
||||||
|
Op::SetGlobal { offset } => {
|
||||||
|
let ident = match chunk.constants[offset as usize] {
|
||||||
|
Value::Obj(object) => object.downcast::<ObjString>().unwrap(),
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match globals.entry(ident) {
|
||||||
|
hash_map::Entry::Occupied(mut entry) => {
|
||||||
|
let v = self.stack.last().unwrap();
|
||||||
|
entry.insert(v.clone());
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
hash_map::Entry::Vacant(_) => {
|
||||||
|
Err(
|
||||||
|
VMError::Runtime(
|
||||||
|
format!("Undefined variable '{}'.", ident).into(),
|
||||||
|
self.pc,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}?
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +326,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn read_write_globals() -> Result<(), VMError> {
|
fn define_read_globals() -> Result<(), VMError> {
|
||||||
let var = GC::new_string("global");
|
let var = GC::new_string("global");
|
||||||
use Op::*;
|
use Op::*;
|
||||||
let chunk = Chunk::new_with(
|
let chunk = Chunk::new_with(
|
||||||
@@ -326,4 +349,32 @@ mod tests {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn define_write_read_globals() -> Result<(), VMError> {
|
||||||
|
let var = GC::new_string("global");
|
||||||
|
use Op::*;
|
||||||
|
let chunk = Chunk::new_with(
|
||||||
|
vec![
|
||||||
|
Constant { offset: 0 },
|
||||||
|
DefineGlobal { offset: 1 },
|
||||||
|
GetGlobal { offset: 1 },
|
||||||
|
Constant { offset: 2 },
|
||||||
|
Add,
|
||||||
|
SetGlobal { offset: 1 },
|
||||||
|
Pop,
|
||||||
|
GetGlobal { offset: 1 },
|
||||||
|
],
|
||||||
|
vec![1; 7],
|
||||||
|
vec![Value::from(5.0), Value::from(var.get_object()), Value::from(6.0)],
|
||||||
|
LinkedList::new()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut vm = VM::new();
|
||||||
|
vm.stdrun(&chunk)?;
|
||||||
|
|
||||||
|
assert_eq!(vm.stack, vec![Value::Number(11.0)]);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user