Precalculate stack positions for variables
- Parser calculates positions for the variables - This removes the lookup time during runtime - Consistent high performance
This commit is contained in:
parent
d4c6f3d5dc
commit
22634af554
@ -83,7 +83,7 @@ pub enum Expression {
|
|||||||
/// String literal
|
/// String literal
|
||||||
String(Sid),
|
String(Sid),
|
||||||
/// Variable
|
/// Variable
|
||||||
Var(Sid),
|
Var(Sid, usize),
|
||||||
/// Binary operation. Consists of type, left hand side and right hand side
|
/// Binary operation. Consists of type, left hand side and right hand side
|
||||||
BinOp(BinOpType, Box<Expression>, Box<Expression>),
|
BinOp(BinOpType, Box<Expression>, Box<Expression>),
|
||||||
/// Unary operation. Consists of type and operand
|
/// Unary operation. Consists of type and operand
|
||||||
|
|||||||
@ -15,7 +15,7 @@ pub struct Interpreter {
|
|||||||
capture_output: bool,
|
capture_output: bool,
|
||||||
output: Vec<Value>,
|
output: Vec<Value>,
|
||||||
// Variable table stores the runtime values of variables
|
// Variable table stores the runtime values of variables
|
||||||
vartable: Vec<(Sid, Value)>,
|
vartable: Vec<Value>,
|
||||||
|
|
||||||
stringstore: StringStore,
|
stringstore: StringStore,
|
||||||
}
|
}
|
||||||
@ -33,20 +33,12 @@ impl Interpreter {
|
|||||||
&self.output
|
&self.output
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_var(&self, name: Sid) -> Option<Value> {
|
fn get_var(&self, idx: usize) -> Option<Value> {
|
||||||
self.vartable
|
self.vartable.get(idx).cloned()
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.find(|it| it.0 == name)
|
|
||||||
.map(|it| it.1.clone())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_var_mut(&mut self, name: Sid) -> Option<&mut Value> {
|
fn get_var_mut(&mut self, idx: usize) -> Option<&mut Value> {
|
||||||
self.vartable
|
self.vartable.get_mut(idx)
|
||||||
.iter_mut()
|
|
||||||
.rev()
|
|
||||||
.find(|it| it.0 == name)
|
|
||||||
.map(|it| &mut it.1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_str(&mut self, code: &str, print_tokens: bool, print_ast: bool) {
|
pub fn run_str(&mut self, code: &str, print_tokens: bool, print_ast: bool) {
|
||||||
@ -66,7 +58,8 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_block(&mut self, prog: &BlockScope) {
|
pub fn run_block(&mut self, prog: &BlockScope) {
|
||||||
let vartable_len = self.vartable.len();
|
let framepointer = self.vartable.len();
|
||||||
|
|
||||||
for stmt in prog {
|
for stmt in prog {
|
||||||
match stmt {
|
match stmt {
|
||||||
Statement::Expr(expr) => {
|
Statement::Expr(expr) => {
|
||||||
@ -112,7 +105,7 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.vartable.truncate(vartable_len);
|
self.vartable.truncate(framepointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_expr(&mut self, expr: &Expression) -> Value {
|
fn resolve_expr(&mut self, expr: &Expression) -> Value {
|
||||||
@ -121,13 +114,13 @@ impl Interpreter {
|
|||||||
Expression::String(text) => Value::String(text.clone()),
|
Expression::String(text) => Value::String(text.clone()),
|
||||||
Expression::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, lhs, rhs),
|
Expression::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, lhs, rhs),
|
||||||
Expression::UnOp(uo, operand) => self.resolve_unop(uo, operand),
|
Expression::UnOp(uo, operand) => self.resolve_unop(uo, operand),
|
||||||
Expression::Var(name) => self.resolve_var(*name),
|
Expression::Var(name, idx) => self.resolve_var(*name, *idx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_var(&mut self, name: Sid) -> Value {
|
fn resolve_var(&mut self, name: Sid, idx: usize) -> Value {
|
||||||
match self.get_var(name) {
|
match self.get_var(idx) {
|
||||||
Some(val) => val.clone(),
|
Some(val) => val,
|
||||||
None => panic!("Variable '{}' used but not declared", self.stringstore.lookup(name).unwrap()),
|
None => panic!("Variable '{}' used but not declared", self.stringstore.lookup(name).unwrap()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,14 +140,14 @@ impl Interpreter {
|
|||||||
let rhs = self.resolve_expr(rhs);
|
let rhs = self.resolve_expr(rhs);
|
||||||
|
|
||||||
match (&bo, &lhs) {
|
match (&bo, &lhs) {
|
||||||
(BinOpType::Declare, Expression::Var(name)) => {
|
(BinOpType::Declare, Expression::Var(_name, _idx)) => {
|
||||||
self.vartable.push((name.clone(), rhs.clone()));
|
self.vartable.push(rhs.clone());
|
||||||
return rhs;
|
return rhs;
|
||||||
}
|
}
|
||||||
(BinOpType::Assign, Expression::Var(name)) => {
|
(BinOpType::Assign, Expression::Var(name, idx)) => {
|
||||||
match self.get_var_mut(*name) {
|
match self.get_var_mut(*idx) {
|
||||||
Some(val) => *val = rhs.clone(),
|
Some(val) => *val = rhs.clone(),
|
||||||
None => panic!("Runtime Error: Trying to assign value to undeclared variable"),
|
None => panic!("Runtime Error: Trying to assign value to undeclared variable: {:?}", self.stringstore.lookup(*name)),
|
||||||
}
|
}
|
||||||
return rhs;
|
return rhs;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
use crate::stringstore::StringStore;
|
use crate::stringstore::{Sid, StringStore};
|
||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
|
|
||||||
/// Parse the given tokens into an abstract syntax tree
|
/// Parse the given tokens into an abstract syntax tree
|
||||||
@ -13,6 +13,7 @@ pub fn parse<T: Iterator<Item = Token>, A: IntoIterator<IntoIter = T>>(tokens: A
|
|||||||
struct Parser<T: Iterator<Item = Token>> {
|
struct Parser<T: Iterator<Item = Token>> {
|
||||||
tokens: Peekable<T>,
|
tokens: Peekable<T>,
|
||||||
stringstore: StringStore,
|
stringstore: StringStore,
|
||||||
|
varstack: Vec<Sid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Iterator<Item = Token>> Parser<T> {
|
impl<T: Iterator<Item = Token>> Parser<T> {
|
||||||
@ -20,9 +21,11 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
pub fn new<A: IntoIterator<IntoIter = T>>(tokens: A) -> Self {
|
pub fn new<A: IntoIterator<IntoIter = T>>(tokens: A) -> Self {
|
||||||
let tokens = tokens.into_iter().peekable();
|
let tokens = tokens.into_iter().peekable();
|
||||||
let stringstore = StringStore::new();
|
let stringstore = StringStore::new();
|
||||||
|
let varstack = Vec::new();
|
||||||
Self {
|
Self {
|
||||||
tokens,
|
tokens,
|
||||||
stringstore,
|
stringstore,
|
||||||
|
varstack,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +40,7 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
/// Parse tokens into an abstract syntax tree. This will continuously parse statements until
|
/// Parse tokens into an abstract syntax tree. This will continuously parse statements until
|
||||||
/// encountering end-of-file or a block end '}' .
|
/// encountering end-of-file or a block end '}' .
|
||||||
fn parse_scoped_block(&mut self) -> BlockScope {
|
fn parse_scoped_block(&mut self) -> BlockScope {
|
||||||
|
let framepointer = self.varstack.len();
|
||||||
let mut prog = Vec::new();
|
let mut prog = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -51,6 +55,8 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.varstack.truncate(framepointer);
|
||||||
|
|
||||||
prog
|
prog
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +82,20 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
|
|
||||||
// If it is not a loop, try to lex as an expression
|
// If it is not a loop, try to lex as an expression
|
||||||
_ => {
|
_ => {
|
||||||
let stmt = Statement::Expr(self.parse_expr());
|
let mut expr = self.parse_expr();
|
||||||
|
|
||||||
|
match &mut expr {
|
||||||
|
Expression::BinOp(BinOpType::Declare, lhs, _) => match lhs.as_mut() {
|
||||||
|
Expression::Var(sid, sp) => {
|
||||||
|
*sp = self.varstack.len();
|
||||||
|
self.varstack.push(*sid);
|
||||||
|
}
|
||||||
|
_ => panic!("Left hand side of declaration must be variable"),
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
let stmt = Statement::Expr(expr);
|
||||||
|
|
||||||
// After a statement, there must be a semicolon
|
// After a statement, there must be a semicolon
|
||||||
if !matches!(self.next(), Token::Semicolon) {
|
if !matches!(self.next(), Token::Semicolon) {
|
||||||
@ -212,7 +231,17 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
// Literal String
|
// Literal String
|
||||||
Token::String(text) => Expression::String(self.stringstore.intern_or_lookup(&text)),
|
Token::String(text) => Expression::String(self.stringstore.intern_or_lookup(&text)),
|
||||||
|
|
||||||
Token::Ident(name) => Expression::Var(self.stringstore.intern_or_lookup(&name)),
|
Token::Ident(name) => {
|
||||||
|
let sid = self.stringstore.intern_or_lookup(&name);
|
||||||
|
let stackpos = self
|
||||||
|
.varstack
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.position(|it| *it == sid)
|
||||||
|
.map(|it| self.varstack.len() - it - 1)
|
||||||
|
.unwrap_or(usize::MAX);
|
||||||
|
Expression::Var(sid, stackpos)
|
||||||
|
}
|
||||||
|
|
||||||
// Parentheses grouping
|
// Parentheses grouping
|
||||||
Token::LParen => {
|
Token::LParen => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user