[rlox] Clean up

This commit is contained in:
ctsk
2023-10-08 11:20:00 +02:00
parent 8ce54fc17c
commit e7e7ef3b10
4 changed files with 131 additions and 89 deletions

View File

@@ -1,7 +1,7 @@
use std::convert::From; use std::convert::From;
use std::fmt; use std::fmt;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Op { pub enum Op {
Return, Return,
Constant { offset: usize }, Constant { offset: usize },
@@ -44,14 +44,28 @@ impl Chunk {
} }
} }
pub fn add_op(&mut self, op: Op, line: usize) { pub fn new_with(code: Vec<Op>, debug_info: Vec<usize>, constants: Vec<Value>) -> Self {
self.code.push(op); Chunk {
self.debug_info.push(line); code,
debug_info,
constants
}
} }
pub fn add_constant(&mut self, value: Value) -> usize { pub fn instr_eq(&self, other: &Chunk) -> bool {
self.code == other.code && self.constants == other.constants
}
pub fn add_op(&mut self, op: Op, line: usize) -> &mut Self {
self.code.push(op);
self.debug_info.push(line);
self
}
pub fn add_constant(&mut self, value: Value, line: usize) -> &mut Self {
self.constants.push(value); self.constants.push(value);
self.constants.len() - 1 self.add_op(Op::Constant {offset: self.constants.len() - 1}, line)
} }
} }
@@ -69,8 +83,8 @@ impl fmt::Debug for Chunk {
"{:?}", "{:?}",
TraceInfo { TraceInfo {
offset: idx, offset: idx,
op: op, op,
chunk: &self chunk: self
} }
)?; )?;
} }
@@ -82,9 +96,7 @@ impl fmt::Debug for Chunk {
impl fmt::Debug for NamedChunk { impl fmt::Debug for NamedChunk {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
writeln!(f, "-*-*- {} -*-*-", self.name)?; writeln!(f, "-*-*- {} -*-*-", self.name)?;
write!(f, "{:?}", self.chunk)?; write!(f, "{:?}", self.chunk)
Ok(())
} }
} }

View File

@@ -6,8 +6,6 @@ use crate::bc::{Chunk, Op};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum TokenType { enum TokenType {
Eof,
LeftParen, LeftParen,
RightParen, RightParen,
LeftBrace, LeftBrace,
@@ -111,7 +109,7 @@ impl<'src> Scanner<'src> {
where where
P: Fn(char) -> bool, P: Fn(char) -> bool,
{ {
self.iter.next_if(|&(_, c)| p(c)).map(|(p, c)| p) self.iter.next_if(|&(_, c)| p(c)).map(|(p, _c)| p)
} }
fn consume_if_eq(&mut self, expected: char) -> Option<usize> { fn consume_if_eq(&mut self, expected: char) -> Option<usize> {
@@ -132,7 +130,7 @@ impl<'src> Scanner<'src> {
} }
fn consume_until_eq(&mut self, limit: char) -> Option<usize> { fn consume_until_eq(&mut self, limit: char) -> Option<usize> {
while let Some((p, c)) = self.iter.next() { for (p, c) in self.iter.by_ref() {
if c == limit { if c == limit {
return Some(p); return Some(p);
} }
@@ -265,7 +263,6 @@ impl<'src> Iterator for Scanner<'src> {
struct Parser<'src> { struct Parser<'src> {
scanner: Peekable<Scanner<'src>>, scanner: Peekable<Scanner<'src>>,
chunk: Chunk,
} }
enum Associativity { enum Associativity {
@@ -309,20 +306,16 @@ impl<'src> Parser<'src> {
fn new(sc: Scanner<'src>) -> Self { fn new(sc: Scanner<'src>) -> Self {
Parser { Parser {
scanner: sc.into_iter().peekable(), scanner: sc.into_iter().peekable(),
chunk: Chunk::new(),
} }
} }
fn result(&self) -> &Chunk {
return &self.chunk;
}
fn precedence(ttype: TokenType) -> Precedence { fn precedence(ttype: TokenType) -> Precedence {
use TokenType::*; use TokenType::*;
match ttype { match ttype {
Plus | Minus => Precedence::Term, Plus | Minus => Precedence::Term,
Star | Slash => Precedence::Factor, Star | Slash => Precedence::Factor,
_ => todo!(), RightParen => Precedence::None,
_ => panic!("{:?}", ttype),
} }
} }
@@ -330,53 +323,55 @@ impl<'src> Parser<'src> {
use Precedence::*; use Precedence::*;
match prec { match prec {
Term | Factor => Associativity::Left, Term | Factor => Associativity::Left,
None => Associativity::Left,
_ => Associativity::NonAssoc, _ => Associativity::NonAssoc,
} }
} }
fn _expression(&mut self, min_prec: Precedence) { fn _expression(&mut self, chunk: &mut Chunk, min_prec: Precedence) {
match self.scanner.next().unwrap() { match self.scanner.next().unwrap() {
Token { Token {
ttype: TokenType::Minus, ttype: TokenType::Minus,
span: _, span: _,
} => { } => {
self._expression(Precedence::Unary); self._expression(chunk, Precedence::Unary);
self.chunk.add_op(Op::Negate, 0) chunk.add_op(Op::Negate, 0);
} }
Token { Token {
ttype: TokenType::Number, ttype: TokenType::Number,
span, span,
} => match span.parse::<f64>() { } => {
Ok(c) => { match span.parse::<f64>() {
let constant_pos = self.chunk.add_constant(c.into()); Ok(c) => chunk.add_constant(c.into(), 0),
self.chunk.add_op(Op::Constant { offset: constant_pos }, 0); _ => panic!("Could not parse number"),
} };
_ => panic!("Could not parse number"), }
}, Token {
ttype: TokenType::LeftParen,
span: _,
} => {
self._expression(chunk, Precedence::None);
assert_eq!(self.scanner.next().unwrap().ttype, TokenType::RightParen)
}
_ => panic!("Expected '-' or number"), _ => panic!("Expected '-' or number"),
}; };
loop { while let Some(op) = self.scanner.next_if(|token| {
let op = match self.scanner.next_if(|token| { let op_prec = Self::precedence(token.ttype);
let op_prec = Self::precedence(token.ttype); if op_prec == min_prec {
if op_prec == min_prec { match Self::associativity(min_prec) {
match Self::associativity(min_prec) { Associativity::Left => false,
Associativity::Left => false, Associativity::Right => true,
Associativity::Right => true, Associativity::NonAssoc => {
Associativity::NonAssoc => { panic!("NonAssoc operation found in associative position")
panic!("NonAssoc operation found in associative position")
}
} }
} else {
return op_prec > min_prec;
} }
}) { } else {
Some(token) => token, op_prec > min_prec
None => break, }
}; }) {
// Generates code for rhs // Generates code for rhs
self._expression(Self::precedence(op.ttype)); self._expression(chunk, Self::precedence(op.ttype));
let op_decoded = match op.ttype { let op_decoded = match op.ttype {
TokenType::Plus => Op::Add, TokenType::Plus => Op::Add,
@@ -386,22 +381,25 @@ impl<'src> Parser<'src> {
_ => todo!(), _ => todo!(),
}; };
self.chunk.add_op(op_decoded, 0) chunk.add_op(op_decoded, 0);
} }
} }
pub fn expression(&mut self) { pub fn expression(&mut self, chunk: &mut Chunk) {
self._expression(Precedence::None); self._expression(chunk, Precedence::None)
} }
} }
pub fn compile(source: &str) { pub fn compile(source: &str, chunk: &mut Chunk) {
let scanner = Scanner::new(source); let scanner = Scanner::new(source);
let parser = Parser::new(scanner); let mut parser = Parser::new(scanner);
parser.expression(chunk);
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::bc::Value;
use super::*; use super::*;
#[test] #[test]
@@ -456,12 +454,27 @@ mod tests {
#[test] #[test]
fn test_parser() { fn test_parser() {
let source = "1 + 1 * 2"; let source = "1 + 1 * (2 + 1)";
let scanner = Scanner::new(source); let scanner = Scanner::new(source);
let mut parser = Parser::new(scanner); let mut parser = Parser::new(scanner);
parser.expression(); let mut chunk = Chunk::new();
let result = parser.result(); parser.expression(&mut chunk);
print!("{:?}", result) use crate::bc::Op::*;
let expected = Chunk::new_with(
vec![
Constant { offset: 0 },
Constant { offset: 1 },
Constant { offset: 2 },
Constant { offset: 3 },
Add,
Multiply,
Add,
],
vec![],
vec![1., 1., 2., 1.].into_iter().map(Value::from).collect()
);
assert!(chunk.instr_eq(&expected));
} }
} }

View File

@@ -15,8 +15,9 @@ fn run_file() {
fn main() { fn main() {
let num_args = env::args().len(); let num_args = env::args().len();
let mut chunk = bc::Chunk::new();
lc::compile("print(1+2*3)"); lc::compile("print(1+2*3)", &mut chunk);
if num_args == 1 { if num_args == 1 {
repl(); repl();
@@ -26,3 +27,17 @@ fn main() {
println!("Usage: rlox [path]"); println!("Usage: rlox [path]");
} }
} }
#[cfg(test)]
mod tests {
use crate::{bc::Chunk, lc::compile, vm::VM};
#[test]
fn test_compile_and_run_pi_math() {
let source = "-(3 * 7 * 11 * 17) / -(500 + 1000 - 250)";
let mut chunk = Chunk::new();
compile(source, &mut chunk);
let mut vm = VM::new();
vm.run(&chunk).unwrap();
}
}

View File

@@ -22,7 +22,7 @@ impl VM {
} }
fn runtime_err(&self, msg: &'static str) -> VMError { fn runtime_err(&self, msg: &'static str) -> VMError {
return VMError::Runtime(msg, self.pc); VMError::Runtime(msg, self.pc)
} }
fn push(&mut self, value: Value) { fn push(&mut self, value: Value) {
@@ -30,7 +30,9 @@ impl VM {
} }
fn pop(&mut self) -> Result<Value, VMError> { fn pop(&mut self) -> Result<Value, VMError> {
self.stack.pop().ok_or_else(|| self.runtime_err("Attempt to pop of empty stack.")) self.stack
.pop()
.ok_or_else(|| self.runtime_err("Attempt to pop of empty stack."))
} }
pub fn run(&mut self, chunk: &Chunk) -> Result<(), VMError> { pub fn run(&mut self, chunk: &Chunk) -> Result<(), VMError> {
@@ -50,7 +52,7 @@ impl VM {
TraceInfo { TraceInfo {
offset: self.pc - 1, offset: self.pc - 1,
op: instr, op: instr,
chunk: chunk chunk
} }
); );
} }
@@ -70,14 +72,14 @@ impl VM {
Op::Subtract => Ok(a.val - b.val), Op::Subtract => Ok(a.val - b.val),
Op::Multiply => Ok(a.val * b.val), Op::Multiply => Ok(a.val * b.val),
Op::Divide => Ok(a.val / b.val), Op::Divide => Ok(a.val / b.val),
_ => Err(self.runtime_err("Op not implemented")) _ => Err(self.runtime_err("Op not implemented")),
}?; }?;
self.push(r.into()) self.push(r.into())
} }
} }
} }
return Ok(()); Ok(())
} }
} }
@@ -87,30 +89,30 @@ mod tests {
#[test] #[test]
fn simple_arithmetic() { fn simple_arithmetic() {
let mut chunk = Chunk::new(); let chunk = Chunk::new_with(
chunk.add_constant(Value::from(3.)); vec![
chunk.add_constant(Value::from(7.)); Op::Constant { offset: 0 },
chunk.add_constant(Value::from(11.)); Op::Constant { offset: 1 },
chunk.add_constant(Value::from(17.)); Op::Multiply,
chunk.add_constant(Value::from(500.)); Op::Constant { offset: 2 },
chunk.add_constant(Value::from(1000.)); Op::Constant { offset: 3 },
chunk.add_constant(Value::from(250.)); Op::Multiply,
Op::Multiply,
chunk.add_op(Op::Constant { offset: 0 }, 1); Op::Negate,
chunk.add_op(Op::Constant { offset: 1 }, 1); Op::Constant { offset: 4 },
chunk.add_op(Op::Multiply, 1); Op::Constant { offset: 5 },
chunk.add_op(Op::Constant { offset: 2 }, 1); Op::Add,
chunk.add_op(Op::Constant { offset: 3 }, 1); Op::Constant { offset: 6 },
chunk.add_op(Op::Multiply, 1); Op::Subtract,
chunk.add_op(Op::Multiply, 1); Op::Negate,
chunk.add_op(Op::Negate, 1); Op::Divide,
chunk.add_op(Op::Constant { offset: 4 }, 2); ],
chunk.add_op(Op::Constant { offset: 5 }, 2); vec![],
chunk.add_op(Op::Add, 2); vec![3., 7., 11., 17., 500., 1000., 250.]
chunk.add_op(Op::Constant { offset: 6 }, 2); .into_iter()
chunk.add_op(Op::Subtract, 2); .map(Value::from)
chunk.add_op(Op::Negate, 2); .collect(),
chunk.add_op(Op::Divide, 2); );
let mut vm = VM::new(); let mut vm = VM::new();
vm.run(&chunk).unwrap(); vm.run(&chunk).unwrap();