[rlox] Clean up
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use std::convert::From;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Op {
|
||||
Return,
|
||||
Constant { offset: usize },
|
||||
@@ -44,14 +44,28 @@ impl Chunk {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_op(&mut self, op: Op, line: usize) {
|
||||
self.code.push(op);
|
||||
self.debug_info.push(line);
|
||||
pub fn new_with(code: Vec<Op>, debug_info: Vec<usize>, constants: Vec<Value>) -> Self {
|
||||
Chunk {
|
||||
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.len() - 1
|
||||
self.add_op(Op::Constant {offset: self.constants.len() - 1}, line)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,8 +83,8 @@ impl fmt::Debug for Chunk {
|
||||
"{:?}",
|
||||
TraceInfo {
|
||||
offset: idx,
|
||||
op: op,
|
||||
chunk: &self
|
||||
op,
|
||||
chunk: self
|
||||
}
|
||||
)?;
|
||||
}
|
||||
@@ -82,9 +96,7 @@ impl fmt::Debug for Chunk {
|
||||
impl fmt::Debug for NamedChunk {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
writeln!(f, "-*-*- {} -*-*-", self.name)?;
|
||||
write!(f, "{:?}", self.chunk)?;
|
||||
|
||||
Ok(())
|
||||
write!(f, "{:?}", self.chunk)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ use crate::bc::{Chunk, Op};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum TokenType {
|
||||
Eof,
|
||||
|
||||
LeftParen,
|
||||
RightParen,
|
||||
LeftBrace,
|
||||
@@ -111,7 +109,7 @@ impl<'src> Scanner<'src> {
|
||||
where
|
||||
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> {
|
||||
@@ -132,7 +130,7 @@ impl<'src> Scanner<'src> {
|
||||
}
|
||||
|
||||
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 {
|
||||
return Some(p);
|
||||
}
|
||||
@@ -265,7 +263,6 @@ impl<'src> Iterator for Scanner<'src> {
|
||||
|
||||
struct Parser<'src> {
|
||||
scanner: Peekable<Scanner<'src>>,
|
||||
chunk: Chunk,
|
||||
}
|
||||
|
||||
enum Associativity {
|
||||
@@ -309,20 +306,16 @@ impl<'src> Parser<'src> {
|
||||
fn new(sc: Scanner<'src>) -> Self {
|
||||
Parser {
|
||||
scanner: sc.into_iter().peekable(),
|
||||
chunk: Chunk::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn result(&self) -> &Chunk {
|
||||
return &self.chunk;
|
||||
}
|
||||
|
||||
fn precedence(ttype: TokenType) -> Precedence {
|
||||
use TokenType::*;
|
||||
match ttype {
|
||||
Plus | Minus => Precedence::Term,
|
||||
Star | Slash => Precedence::Factor,
|
||||
_ => todo!(),
|
||||
RightParen => Precedence::None,
|
||||
_ => panic!("{:?}", ttype),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,34 +323,40 @@ impl<'src> Parser<'src> {
|
||||
use Precedence::*;
|
||||
match prec {
|
||||
Term | Factor => Associativity::Left,
|
||||
None => Associativity::Left,
|
||||
_ => Associativity::NonAssoc,
|
||||
}
|
||||
}
|
||||
|
||||
fn _expression(&mut self, min_prec: Precedence) {
|
||||
fn _expression(&mut self, chunk: &mut Chunk, min_prec: Precedence) {
|
||||
match self.scanner.next().unwrap() {
|
||||
Token {
|
||||
ttype: TokenType::Minus,
|
||||
span: _,
|
||||
} => {
|
||||
self._expression(Precedence::Unary);
|
||||
self.chunk.add_op(Op::Negate, 0)
|
||||
self._expression(chunk, Precedence::Unary);
|
||||
chunk.add_op(Op::Negate, 0);
|
||||
}
|
||||
Token {
|
||||
ttype: TokenType::Number,
|
||||
span,
|
||||
} => match span.parse::<f64>() {
|
||||
Ok(c) => {
|
||||
let constant_pos = self.chunk.add_constant(c.into());
|
||||
self.chunk.add_op(Op::Constant { offset: constant_pos }, 0);
|
||||
}
|
||||
} => {
|
||||
match span.parse::<f64>() {
|
||||
Ok(c) => chunk.add_constant(c.into(), 0),
|
||||
_ => 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"),
|
||||
};
|
||||
|
||||
loop {
|
||||
let op = match self.scanner.next_if(|token| {
|
||||
while let Some(op) = self.scanner.next_if(|token| {
|
||||
let op_prec = Self::precedence(token.ttype);
|
||||
if op_prec == min_prec {
|
||||
match Self::associativity(min_prec) {
|
||||
@@ -368,15 +367,11 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return op_prec > min_prec;
|
||||
op_prec > min_prec
|
||||
}
|
||||
}) {
|
||||
Some(token) => token,
|
||||
None => break,
|
||||
};
|
||||
|
||||
// Generates code for rhs
|
||||
self._expression(Self::precedence(op.ttype));
|
||||
self._expression(chunk, Self::precedence(op.ttype));
|
||||
|
||||
let op_decoded = match op.ttype {
|
||||
TokenType::Plus => Op::Add,
|
||||
@@ -386,22 +381,25 @@ impl<'src> Parser<'src> {
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
self.chunk.add_op(op_decoded, 0)
|
||||
chunk.add_op(op_decoded, 0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expression(&mut self) {
|
||||
self._expression(Precedence::None);
|
||||
pub fn expression(&mut self, chunk: &mut Chunk) {
|
||||
self._expression(chunk, Precedence::None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile(source: &str) {
|
||||
pub fn compile(source: &str, chunk: &mut Chunk) {
|
||||
let scanner = Scanner::new(source);
|
||||
let parser = Parser::new(scanner);
|
||||
let mut parser = Parser::new(scanner);
|
||||
parser.expression(chunk);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::bc::Value;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
@@ -456,12 +454,27 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parser() {
|
||||
let source = "1 + 1 * 2";
|
||||
let source = "1 + 1 * (2 + 1)";
|
||||
let scanner = Scanner::new(source);
|
||||
let mut parser = Parser::new(scanner);
|
||||
parser.expression();
|
||||
let result = parser.result();
|
||||
let mut chunk = Chunk::new();
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@ fn run_file() {
|
||||
fn main() {
|
||||
|
||||
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 {
|
||||
repl();
|
||||
@@ -26,3 +27,17 @@ fn main() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ impl VM {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -30,7 +30,9 @@ impl VM {
|
||||
}
|
||||
|
||||
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> {
|
||||
@@ -50,7 +52,7 @@ impl VM {
|
||||
TraceInfo {
|
||||
offset: self.pc - 1,
|
||||
op: instr,
|
||||
chunk: chunk
|
||||
chunk
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -70,14 +72,14 @@ impl VM {
|
||||
Op::Subtract => Ok(a.val - b.val),
|
||||
Op::Multiply => 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,30 +89,30 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn simple_arithmetic() {
|
||||
let mut chunk = Chunk::new();
|
||||
chunk.add_constant(Value::from(3.));
|
||||
chunk.add_constant(Value::from(7.));
|
||||
chunk.add_constant(Value::from(11.));
|
||||
chunk.add_constant(Value::from(17.));
|
||||
chunk.add_constant(Value::from(500.));
|
||||
chunk.add_constant(Value::from(1000.));
|
||||
chunk.add_constant(Value::from(250.));
|
||||
|
||||
chunk.add_op(Op::Constant { offset: 0 }, 1);
|
||||
chunk.add_op(Op::Constant { offset: 1 }, 1);
|
||||
chunk.add_op(Op::Multiply, 1);
|
||||
chunk.add_op(Op::Constant { offset: 2 }, 1);
|
||||
chunk.add_op(Op::Constant { offset: 3 }, 1);
|
||||
chunk.add_op(Op::Multiply, 1);
|
||||
chunk.add_op(Op::Multiply, 1);
|
||||
chunk.add_op(Op::Negate, 1);
|
||||
chunk.add_op(Op::Constant { offset: 4 }, 2);
|
||||
chunk.add_op(Op::Constant { offset: 5 }, 2);
|
||||
chunk.add_op(Op::Add, 2);
|
||||
chunk.add_op(Op::Constant { offset: 6 }, 2);
|
||||
chunk.add_op(Op::Subtract, 2);
|
||||
chunk.add_op(Op::Negate, 2);
|
||||
chunk.add_op(Op::Divide, 2);
|
||||
let chunk = Chunk::new_with(
|
||||
vec![
|
||||
Op::Constant { offset: 0 },
|
||||
Op::Constant { offset: 1 },
|
||||
Op::Multiply,
|
||||
Op::Constant { offset: 2 },
|
||||
Op::Constant { offset: 3 },
|
||||
Op::Multiply,
|
||||
Op::Multiply,
|
||||
Op::Negate,
|
||||
Op::Constant { offset: 4 },
|
||||
Op::Constant { offset: 5 },
|
||||
Op::Add,
|
||||
Op::Constant { offset: 6 },
|
||||
Op::Subtract,
|
||||
Op::Negate,
|
||||
Op::Divide,
|
||||
],
|
||||
vec![],
|
||||
vec![3., 7., 11., 17., 500., 1000., 250.]
|
||||
.into_iter()
|
||||
.map(Value::from)
|
||||
.collect(),
|
||||
);
|
||||
|
||||
let mut vm = VM::new();
|
||||
vm.run(&chunk).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user