Skip to content

Commit

Permalink
Read variables.
Browse files Browse the repository at this point in the history
  • Loading branch information
agrawal-d committed Apr 15, 2024
1 parent 42a68e9 commit ac681ce
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 48 deletions.
2 changes: 2 additions & 0 deletions compiler/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub enum Opcode {
Pop,
Equal,
Less,
DefineGlobal,
GetGlobal,
}

pub fn variant_eq<T>(a: &T, b: &T) -> bool {
Expand Down
49 changes: 45 additions & 4 deletions compiler/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ fn get_rules<'src>() -> HashMap<TokenType, ParseRule<'src>> {
add_rule!(map, GreaterEqual, None, Some(Compiler::binary), Precedence::Comparison);
add_rule!(map, Less, None, Some(Compiler::binary), Precedence::Comparison);
add_rule!(map, LessEqual, None, None, Precedence::Comparison);
add_rule!(map, Identifier, None, None, Precedence::None);
add_rule!(map, Identifier, Some(Compiler::variable), None, Precedence::None);
add_rule!(map, String, Some(Compiler::string), None, Precedence::None);
add_rule!(map, Number, Some(Compiler::number), None, Precedence::None);
add_rule!(map, And, None, None, Precedence::None);
Expand Down Expand Up @@ -162,7 +162,7 @@ impl Parser {
}

fn check_tt(&mut self, typ: TokenType) -> bool {
return self.current.typ == typ;
self.current.typ == typ
}

fn match_tt(&mut self, typ: TokenType) -> bool {
Expand All @@ -172,7 +172,7 @@ impl Parser {

self.advance();

return true;
true
}

fn advance(&mut self) {
Expand Down Expand Up @@ -297,6 +297,19 @@ impl<'src> Compiler<'src> {
self.parse_precedence(Precedence::Assignment);
}

fn var_declaration(&mut self) {
let global = self.parse_variable("Expect variable name");

if self.parser.match_tt(TokenType::Equal) {
self.expression();
} else {
self.emit_byte(Opcode::Nil as u8);
}

self.parser.consume(TokenType::Semicolon, "Expect ';' after variable declaration");
self.define_variable(global);
}

fn expression_statement(&mut self) {
self.expression();
self.parser.consume(TokenType::Semicolon, "Expect ';' after expression");
Expand All @@ -310,7 +323,11 @@ impl<'src> Compiler<'src> {
}

fn declaration(&mut self) {
self.statement();
if self.parser.match_tt(TokenType::Var) {
self.var_declaration();
} else {
self.statement();
}

if self.parser.panic_mode {
self.parser.synchronize();
Expand Down Expand Up @@ -342,6 +359,15 @@ impl<'src> Compiler<'src> {
self.emit_constant(Value::Str(id));
}

fn named_variable(&mut self, token: &Token) {
let arg = self.identifier_constant(token);
self.emit_bytes(Opcode::GetGlobal as u8, arg as u8);
}

fn variable(&mut self) {
self.named_variable(&self.parser.previous.clone());
}

fn unary(&mut self) {
let operator_type = self.parser.previous.typ;
self.parse_precedence(Precedence::Unary);
Expand All @@ -353,6 +379,12 @@ impl<'src> Compiler<'src> {
}
}

fn parse_variable(&mut self, error_message: &str) -> usize {
self.parser.consume(TokenType::Identifier, error_message);
let previous = &self.parser.previous.clone();
self.identifier_constant(previous)
}

// Parse expressions with equal or higher precedence
fn parse_precedence(&mut self, precedence: Precedence) {
self.parser.advance();
Expand Down Expand Up @@ -380,6 +412,15 @@ impl<'src> Compiler<'src> {
}
}

fn identifier_constant(&mut self, name: &Token) -> usize {
let identifier = self.interner.intern(name.source.as_ref());
self.make_constant(Value::Identifier(identifier))
}

fn define_variable(&mut self, global: usize) {
self.emit_bytes(Opcode::DefineGlobal as u8, global as u8);
}

fn make_constant(&mut self, value: Value) -> usize {
self.compiling_chunk.add_constant(value)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub fn disassemble_instruction(chunk: &Chunk, offset: usize, interner: &Interner
};

let ret: usize = match instruction {
Opcode::Constant => constant_instruction(chunk, instruction, offset, interner),
Opcode::Constant | Opcode::DefineGlobal | Opcode::GetGlobal => constant_instruction(chunk, instruction, offset, interner),
Opcode::Add
| Opcode::Return
| Opcode::Negate
Expand Down
6 changes: 5 additions & 1 deletion compiler/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use crate::interner::Interner;
use crate::{interner::StrId, xprint};
use strum_macros::Display;

#[derive(Debug, Display, Clone)]
#[derive(Debug, Display, Clone, Copy)]
pub enum Value {
Bool(bool),
Number(f64),
Str(StrId),
Identifier(StrId),
Nil,
}
pub type ValueArray = Vec<Value>;
Expand All @@ -20,6 +21,9 @@ pub fn print_value(value: &Value, interner: &Interner) {
Value::Str(s) => {
xprint!("{}", interner.lookup(s));
}
Value::Identifier(id) => {
xprint!("Identifier: {}", interner.lookup(id))
}
}
}

Expand Down
123 changes: 81 additions & 42 deletions compiler/src/vm.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
use crate::xprint;
use crate::{
chunk::Chunk,
common::Opcode,
debug::disassemble_instruction,
interner::Interner,
interner::{Interner, StrId},
value::{
print_value,
Value::{self, *},
},
xprintln,
};
use anyhow::*;
use rustc_hash::FxHashMap;

pub struct Vm<'src> {
chunk: Chunk,
ip: usize,
stack: Vec<Value>,
interner: &'src mut Interner,
globals: FxHashMap<StrId, Value>,
}

macro_rules! binop {
Expand All @@ -42,6 +45,7 @@ impl<'src> Vm<'src> {
ip: 0,
stack: Default::default(),
interner,
globals: Default::default(),
}
}

Expand All @@ -51,15 +55,13 @@ impl<'src> Vm<'src> {
value
}

fn read_constant(&mut self) -> Option<&Value> {
fn read_constant(&mut self) -> Option<Value> {
let index: usize = self.read_byte() as usize;
return self.chunk.constants.get(index);
return self.chunk.constants.get(index).copied();
}

#[cfg(feature = "tracing")]
fn stack_trace(&self) {
use crate::{value::print_value, xprint};

xprint!("Stack values: ");
xprint!("[ ");
for value in &self.stack {
Expand All @@ -75,8 +77,7 @@ impl<'src> Vm<'src> {
match value {
Nil => true,
Bool(b) => !b,
Number(n) => n == 0.0,
Str(s) => self.interner.lookup(&s).is_empty(),
_ => false,
}
}

Expand All @@ -93,74 +94,112 @@ impl<'src> Vm<'src> {
self.stack.pop().context("Nothing in stack to pop")
}

pub fn interpret(chunk: Chunk, interner: &'src mut Interner) -> Result<()> {
let mut vm: Vm = Vm::new(chunk, interner);
xprintln!("Interpreting chunk of {} bytes of code", vm.chunk.code.len());
fn read_string_or_id(&mut self) -> StrId {
let value = self.read_constant().expect("Could not read constant");
match value {
Value::Str(id) => {
id
}
Value::Identifier(id) => {
id
}
other => panic!("Found {other} instead"),
}
}

fn run(&mut self) -> Result<()> {
loop {
disassemble_instruction(&vm.chunk, vm.ip, vm.interner);
vm.stack_trace();
let instruction = Opcode::try_from(vm.read_byte()).context("Byte to opcode failed")?;
disassemble_instruction(&self.chunk, self.ip, self.interner);
self.stack_trace();
let instruction = Opcode::try_from(self.read_byte()).context("Byte to opcode failed")?;
match instruction {
Opcode::Print => {
print_value(&vm.pop()?, &vm.interner);
print_value(&self.pop()?, self.interner);
xprintln!("");
}
Opcode::Return => {
return Ok(());
}
Opcode::Constant => {
let constant = vm.read_constant().context("Could not interpret constant opcode")?.clone();
vm.stack.push(constant);
let constant = self.read_constant().context("Could not interpret constant opcode")?;
self.stack.push(constant);
}
Opcode::Negate => {
let value = vm.pop()?;
let value = self.pop()?;
match value {
Number(num) => vm.stack.push(Value::Number(-num)),
Number(num) => self.stack.push(Value::Number(-num)),
_ => {
vm.runtime_error("Operand must be a number");
self.runtime_error("Operand must be a number");
}
}
}
Opcode::True => vm.stack.push(Bool(true)),
Opcode::False => vm.stack.push(Bool(false)),
Opcode::True => self.stack.push(Bool(true)),
Opcode::False => self.stack.push(Bool(false)),
Opcode::Pop => {
vm.pop()?;
self.pop()?;
}
Opcode::GetGlobal => {
let name = self.read_string_or_id();

if let Some(value) = self.globals.get(&name) {
self.stack.push(*value);
} else {
self.runtime_error(&format!("Undefined variable {}", self.interner.lookup(&name)));
}
}
Opcode::DefineGlobal => {
let name = self.read_string_or_id();
self.globals.insert(name, *self.peek(0));
self.pop().unwrap();
}
Opcode::Equal => {
let a = vm.pop()?;
let b = vm.pop()?;
vm.stack.push(Bool(a == b))
let a = self.pop()?;
let b = self.pop()?;
self.stack.push(Bool(a == b))
}
Opcode::Nil => vm.stack.push(Nil),
Opcode::Nil => self.stack.push(Nil),
Opcode::Add => {
let b = vm.pop()?;
let a = vm.pop()?;
let b = self.pop()?;
let a = self.pop()?;
match (b, a) {
(Number(a), Number(b)) => {
vm.stack.push(Number(a + b));
self.stack.push(Number(a + b));
}
(Str(b), Str(a)) => {
let mut new_string = String::from(vm.interner.lookup(&a));
new_string.push_str(vm.interner.lookup(&b));
let id = vm.interner.intern(&new_string);
vm.stack.push(Str(id));
let mut new_string = String::from(self.interner.lookup(&a));
new_string.push_str(self.interner.lookup(&b));
let id = self.interner.intern(&new_string);
self.stack.push(Str(id));
}
_ => {
vm.runtime_error("Operands must be numbers");
vm.stack.push(Nil);
self.runtime_error("Operands must be numbers");
self.stack.push(Nil);
}
}
}
Opcode::Subtract => binop!(vm, Number, -),
Opcode::Multiply => binop!(vm, Number, *),
Opcode::Divide => binop!(vm, Number, /),
Opcode::Subtract => binop!(self, Number, -),
Opcode::Multiply => binop!(self, Number, *),
Opcode::Divide => binop!(self, Number, /),
Opcode::Not => {
let val = vm.pop()?;
vm.stack.push(Bool(vm.is_falsey(val)))
let val = self.pop()?;
self.stack.push(Bool(self.is_falsey(val)))
}
Opcode::Greater => binop!(vm, Bool, >),
Opcode::Less => binop!(vm, Bool, <),
Opcode::Greater => binop!(self, Bool, >),
Opcode::Less => binop!(self, Bool, <),
}
}
}

pub fn interpret(chunk: Chunk, interner: &'src mut Interner) -> Result<()> {
let mut vm: Vm = Vm::new(chunk, interner);
xprintln!("Interpreting chunk of {} bytes of code", vm.chunk.code.len());
vm.run()
}

fn peek(&self, distance: usize) -> &Value {
return self
.stack
.get(self.stack.len() - 1 - distance)
.unwrap_or_else(|| panic!("Failed to peek {distance} deep"));
}
}

0 comments on commit ac681ce

Please sign in to comment.