[rlox] Clean up
This commit is contained in:
@@ -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(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
109
rlox/src/lc.rs
109
rlox/src/lc.rs
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user