[rlox] Add first sprout of parser
This commit is contained in:
@@ -49,8 +49,9 @@ impl Chunk {
|
|||||||
self.debug_info.push(line);
|
self.debug_info.push(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_constant(&mut self, value: Value) {
|
pub fn add_constant(&mut self, value: Value) -> usize {
|
||||||
self.constants.push(value);
|
self.constants.push(value);
|
||||||
|
self.constants.len() - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
206
rlox/src/lc.rs
206
rlox/src/lc.rs
@@ -1,9 +1,10 @@
|
|||||||
|
use std::convert::identity;
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
use std::str::CharIndices;
|
use std::str::CharIndices;
|
||||||
|
|
||||||
use crate::bc::Chunk;
|
use crate::bc::{Chunk, Op};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
enum TokenType {
|
enum TokenType {
|
||||||
Eof,
|
Eof,
|
||||||
|
|
||||||
@@ -78,8 +79,15 @@ impl LineMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_lines(&self, slice: &str) -> (usize, usize) {
|
fn get_line(&self, pos: usize) -> usize {
|
||||||
return (0, 0)
|
self.line_breaks
|
||||||
|
.binary_search(&pos)
|
||||||
|
.unwrap_or_else(identity)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_lines(&self, start: usize, slice: &str) -> (usize, usize) {
|
||||||
|
let end = start + slice.len();
|
||||||
|
(self.get_line(start), self.get_line(end))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,17 +264,140 @@ impl<'src> Iterator for Scanner<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct Parser<'src> {
|
struct Parser<'src> {
|
||||||
scanner: Peekable<Scanner<'src>>
|
scanner: Peekable<Scanner<'src>>,
|
||||||
|
chunk: Chunk,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Associativity {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
NonAssoc,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
|
||||||
|
enum Precedence {
|
||||||
|
None,
|
||||||
|
Assignment,
|
||||||
|
Or,
|
||||||
|
And,
|
||||||
|
Equality,
|
||||||
|
Comparison,
|
||||||
|
Term,
|
||||||
|
Factor,
|
||||||
|
Unary,
|
||||||
|
Call,
|
||||||
|
Primary,
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParseInfo = (Associativity, Precedence, Op, Option<Op>);
|
||||||
|
fn get_info(ttype: TokenType) -> ParseInfo {
|
||||||
|
match ttype {
|
||||||
|
TokenType::Plus => (Associativity::Left, Precedence::Term, Op::Add, None),
|
||||||
|
TokenType::Minus => (
|
||||||
|
Associativity::Left,
|
||||||
|
Precedence::Term,
|
||||||
|
Op::Subtract,
|
||||||
|
Some(Op::Negate),
|
||||||
|
),
|
||||||
|
TokenType::Slash => (Associativity::Left, Precedence::Factor, Op::Divide, None),
|
||||||
|
TokenType::Star => (Associativity::Left, Precedence::Factor, Op::Multiply, None),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Parser<'src> {
|
impl<'src> Parser<'src> {
|
||||||
fn expression() {
|
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!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn associativity(prec: Precedence) -> Associativity {
|
||||||
|
use Precedence::*;
|
||||||
|
match prec {
|
||||||
|
Term | Factor => Associativity::Left,
|
||||||
|
_ => Associativity::NonAssoc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _expression(&mut self, min_prec: Precedence) {
|
||||||
|
match self.scanner.next().unwrap() {
|
||||||
|
Token {
|
||||||
|
ttype: TokenType::Minus,
|
||||||
|
span: _,
|
||||||
|
} => {
|
||||||
|
self._expression(Precedence::Unary);
|
||||||
|
self.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);
|
||||||
|
}
|
||||||
|
_ => panic!("Could not parse number"),
|
||||||
|
},
|
||||||
|
_ => panic!("Expected '-' or number"),
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let op = match self.scanner.next_if(|token| {
|
||||||
|
let op_prec = Self::precedence(token.ttype);
|
||||||
|
if op_prec == min_prec {
|
||||||
|
match Self::associativity(min_prec) {
|
||||||
|
Associativity::Left => false,
|
||||||
|
Associativity::Right => true,
|
||||||
|
Associativity::NonAssoc => {
|
||||||
|
panic!("NonAssoc operation found in associative position")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return op_prec > min_prec;
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Some(token) => token,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generates code for rhs
|
||||||
|
self._expression(Self::precedence(op.ttype));
|
||||||
|
|
||||||
|
let op_decoded = match op.ttype {
|
||||||
|
TokenType::Plus => Op::Add,
|
||||||
|
TokenType::Minus => Op::Subtract,
|
||||||
|
TokenType::Star => Op::Multiply,
|
||||||
|
TokenType::Slash => Op::Divide,
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.chunk.add_op(op_decoded, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expression(&mut self) {
|
||||||
|
self._expression(Precedence::None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(source: &str) {
|
pub fn compile(source: &str) {
|
||||||
let scanner = Scanner::new(source);
|
let scanner = Scanner::new(source);
|
||||||
|
let parser = Parser::new(scanner);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -280,16 +411,57 @@ mod tests {
|
|||||||
|
|
||||||
let tokens: Vec<Token> = scanner.collect();
|
let tokens: Vec<Token> = scanner.collect();
|
||||||
|
|
||||||
assert_eq!(tokens, vec![
|
assert_eq!(
|
||||||
Token { ttype: TokenType::Print, span: &source[0..=4]},
|
tokens,
|
||||||
Token { ttype: TokenType::LeftParen, span: &source[5..=5]},
|
vec![
|
||||||
Token { ttype: TokenType::Number, span: &source[6..=6]},
|
Token {
|
||||||
Token { ttype: TokenType::Plus, span: &source[7..=7]},
|
ttype: TokenType::Print,
|
||||||
Token { ttype: TokenType::Number, span: &source[8..=8]},
|
span: &source[0..=4]
|
||||||
Token { ttype: TokenType::Star, span: &source[9..=9]},
|
},
|
||||||
Token { ttype: TokenType::Number, span: &source[10..=10]},
|
Token {
|
||||||
Token { ttype: TokenType::RightParen, span: &source[11..=11]},
|
ttype: TokenType::LeftParen,
|
||||||
Token { ttype: TokenType::Semicolon, span: &source[12..=12]}
|
span: &source[5..=5]
|
||||||
]);
|
},
|
||||||
|
Token {
|
||||||
|
ttype: TokenType::Number,
|
||||||
|
span: &source[6..=6]
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
ttype: TokenType::Plus,
|
||||||
|
span: &source[7..=7]
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
ttype: TokenType::Number,
|
||||||
|
span: &source[8..=8]
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
ttype: TokenType::Star,
|
||||||
|
span: &source[9..=9]
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
ttype: TokenType::Number,
|
||||||
|
span: &source[10..=10]
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
ttype: TokenType::RightParen,
|
||||||
|
span: &source[11..=11]
|
||||||
|
},
|
||||||
|
Token {
|
||||||
|
ttype: TokenType::Semicolon,
|
||||||
|
span: &source[12..=12]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parser() {
|
||||||
|
let source = "1 + 1 * 2";
|
||||||
|
let scanner = Scanner::new(source);
|
||||||
|
let mut parser = Parser::new(scanner);
|
||||||
|
parser.expression();
|
||||||
|
let result = parser.result();
|
||||||
|
|
||||||
|
print!("{:?}", result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ pub struct VM {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum VMError {
|
pub enum VMError {
|
||||||
Compile,
|
Compile,
|
||||||
Runtime,
|
Runtime(&'static str, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VM {
|
impl VM {
|
||||||
@@ -21,12 +21,16 @@ impl VM {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn runtime_err(&self, msg: &'static str) -> VMError {
|
||||||
|
return VMError::Runtime(msg, self.pc);
|
||||||
|
}
|
||||||
|
|
||||||
fn push(&mut self, value: Value) {
|
fn push(&mut self, value: Value) {
|
||||||
self.stack.push(value);
|
self.stack.push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop(&mut self) -> Result<Value, VMError> {
|
fn pop(&mut self) -> Result<Value, VMError> {
|
||||||
self.stack.pop().ok_or(VMError::Runtime)
|
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> {
|
||||||
@@ -52,10 +56,7 @@ impl VM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match instr {
|
match instr {
|
||||||
Op::Return => {
|
Op::Return => print!("{:?}", self.pop()?),
|
||||||
print!("{:?}", self.pop()?);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Op::Constant { offset } => self.push(chunk.constants[offset]),
|
Op::Constant { offset } => self.push(chunk.constants[offset]),
|
||||||
Op::Negate => {
|
Op::Negate => {
|
||||||
let new_val = -self.pop()?.val;
|
let new_val = -self.pop()?.val;
|
||||||
@@ -65,13 +66,13 @@ impl VM {
|
|||||||
let b = self.pop()?;
|
let b = self.pop()?;
|
||||||
let a = self.pop()?;
|
let a = self.pop()?;
|
||||||
let r = match instr {
|
let r = match instr {
|
||||||
Op::Add => a.val + b.val,
|
Op::Add => Ok(a.val + b.val),
|
||||||
Op::Subtract => a.val - b.val,
|
Op::Subtract => Ok(a.val - b.val),
|
||||||
Op::Multiply => a.val * b.val,
|
Op::Multiply => Ok(a.val * b.val),
|
||||||
Op::Divide => a.val / b.val,
|
Op::Divide => Ok(a.val / b.val),
|
||||||
_ => unreachable!()
|
_ => Err(self.runtime_err("Op not implemented"))
|
||||||
};
|
}?;
|
||||||
self.push(Value::from(r))
|
self.push(r.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +87,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_arithmetic() {
|
fn simple_arithmetic() {
|
||||||
let mut chunk = Chunk::new("TEST".to_string());
|
let mut chunk = Chunk::new();
|
||||||
chunk.add_constant(Value::from(3.));
|
chunk.add_constant(Value::from(3.));
|
||||||
chunk.add_constant(Value::from(7.));
|
chunk.add_constant(Value::from(7.));
|
||||||
chunk.add_constant(Value::from(11.));
|
chunk.add_constant(Value::from(11.));
|
||||||
|
|||||||
Reference in New Issue
Block a user