interpreter: do enough to get recursive fib working

This commit is contained in:
Ryan Chandler 2022-11-24 01:43:52 +00:00
parent 402814fd95
commit 4501905705
No known key found for this signature in database
GPG Key ID: F113BCADDB3B0CCA
10 changed files with 396 additions and 4 deletions

View File

@ -1,5 +1,6 @@
[workspace]
members = [
"trunk_lexer",
"trunk_parser"
"trunk_parser",
"trunk_interpreter"
]

View File

@ -0,0 +1,11 @@
[package]
name = "trunk_interpreter"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cmder = "0.6.1"
trunk_lexer = { path = "../trunk_lexer" }
trunk_parser = { path = "../trunk_parser" }

View File

@ -0,0 +1,3 @@
mod run;
pub use run::run;

View File

@ -0,0 +1,19 @@
use cmder::ParserMatches;
use trunk_lexer::Lexer;
use trunk_parser::Parser;
use crate::engine::eval;
pub fn run(matches: ParserMatches) {
// FIXME: Better error handling needed.
let file = matches.get_arg("file").unwrap();
let contents = std::fs::read_to_string(file).unwrap();
let mut lexer = Lexer::new(None);
let tokens = lexer.tokenize(&contents).unwrap();
let mut parser = Parser::new(None);
let program = parser.parse(tokens).unwrap();
eval(program);
}

View File

@ -0,0 +1,36 @@
use std::{cell::RefCell, collections::HashMap};
use super::value::Value;
#[derive(Clone, Debug)]
pub struct Environment {
entries: RefCell<HashMap<String, Value>>,
}
impl Environment {
pub fn new() -> Self {
Self {
entries: RefCell::new(HashMap::new()),
}
}
pub fn get(&self, name: &str) -> Option<Value> {
let entries = self.entries.borrow();
if let Some(value) = entries.get(name) {
return Some(value.clone());
}
return None;
}
pub fn set(&mut self, name: &str, value: Value) {
let mut entries = self.entries.borrow_mut();
if let Some(current) = entries.get_mut(name) {
*current = value;
} else {
entries.insert(name.to_owned(), value);
}
}
}

View File

@ -0,0 +1,242 @@
use std::collections::HashMap;
use trunk_parser::{Statement, Param, Expression, InfixOp, CastKind};
use self::environment::Environment;
use self::value::Value;
mod environment;
mod value;
pub struct Function {
pub(crate) params: Vec<Param>,
pub(crate) body: Vec<Statement>,
}
pub struct Engine {
pub(crate) global_environment: Environment,
pub(crate) function_table: HashMap<String, Function>,
pub(crate) scopes: Vec<Environment>,
}
impl Engine {
pub fn new() -> Self {
Self {
global_environment: Environment::new(),
function_table: HashMap::default(),
scopes: Vec::new(),
}
}
}
pub fn eval(program: Vec<Statement>) -> Result<(), Escape> {
let mut engine = Engine::new();
for statement in program {
eval_statement(&mut engine, statement)?;
}
Ok(())
}
pub enum Escape {
Return(Value),
}
pub fn eval_statement(engine: &mut Engine, statement: Statement) -> Result<(), Escape> {
match statement {
Statement::Function { .. } => eval_function(engine, statement)?,
Statement::Echo { .. } => eval_echo(engine, statement)?,
Statement::If { .. } => eval_if(engine, statement)?,
Statement::Return { .. } => return Err(Escape::Return(eval_return(engine, statement))),
_ => unimplemented!("{:?}", statement)
};
Ok(())
}
fn eval_function(engine: &mut Engine, statement: Statement) -> Result<(), Escape> {
let (name, params, body, return_type, by_ref) = match statement {
Statement::Function { name, params, body, return_type, by_ref } => (name, params, body, return_type, by_ref),
_ => unreachable!(),
};
let func = Function {
params,
body
};
engine.function_table.insert(name.name.into(), func);
Ok(())
}
fn eval_echo(engine: &mut Engine, statement: Statement) -> Result<(), Escape> {
let values = match statement {
Statement::Echo { values } => values,
_ => unreachable!()
};
for value in values {
let value = eval_expression(engine, value);
print!("{}", value);
}
Ok(())
}
fn eval_if(engine: &mut Engine, statement: Statement) -> Result<(), Escape> {
let (condition, then, ..) = match statement {
Statement::If { condition, then, else_ifs, r#else } => (condition, then, else_ifs, r#else),
_ => unreachable!()
};
let condition = eval_expression(engine, condition);
if condition.is_truthy() {
for statement in then {
eval_statement(engine, statement)?;
}
}
Ok(())
}
fn eval_return(engine: &mut Engine, statement: Statement) -> Value {
let (value) = match statement {
Statement::Return { value } => value,
_ => unreachable!(),
};
if let Some(value) = value {
return eval_expression(engine, value);
}
return Value::Null;
}
fn eval_expression(engine: &mut Engine, expression: Expression) -> Value {
match expression {
Expression::Infix { .. } => eval_infix_expression(engine, expression),
Expression::Call { .. } => eval_call_expression(engine, expression),
Expression::Variable { .. } => eval_variable_expression(engine, expression),
Expression::Cast { .. } => eval_cast_expression(engine, expression),
Expression::Int { i } => Value::Int(i),
Expression::ConstantString { value } => Value::String(value.into()),
_ => panic!("unhandled expression: {:?}", expression)
}
}
fn eval_infix_expression(engine: &mut Engine, expression: Expression) -> Value {
let (lhs, op, rhs) = match expression {
Expression::Infix { lhs, op, rhs } => (lhs, op, rhs),
_ => unreachable!(),
};
let lhs = eval_expression(engine, *lhs);
let rhs = eval_expression(engine, *rhs);
match op {
InfixOp::Add => lhs + rhs,
InfixOp::Sub => lhs - rhs,
InfixOp::LessThan => Value::Bool(lhs < rhs),
InfixOp::Concat => {
match (lhs, rhs) {
(Value::String(a), Value::String(b)) => {
let mut s = String::with_capacity(a.len() + b.len());
s.push_str(&a);
s.push_str(&b);
Value::String(s)
},
_ => todo!()
}
},
_ => todo!("infix: {:?}", op)
}
}
fn eval_call_expression(engine: &mut Engine, expression: Expression) -> Value {
let (target, args) = match expression {
Expression::Call { target, args } => (target, args),
_ => unreachable!()
};
let target: String = match *target {
Expression::Identifier { name } => name.into(),
_ => unreachable!(),
};
if !engine.function_table.contains_key(&target) {
panic!("undefined function: {}", target);
}
let mut arg_values = Vec::new();
for arg in args {
let value = eval_expression(engine, arg.value);
arg_values.push(value);
}
let func = engine.function_table.get(&target).unwrap().clone();
let mut environment = Environment::new();
for (i, param) in func.params.clone().into_iter().enumerate() {
let name: String = match param.name {
Expression::Variable { name } => name.into(),
_ => todo!()
};
environment.set(&name, arg_values.get(i).unwrap().clone());
}
engine.scopes.push(environment);
let mut return_value = Value::Null;
for statement in func.body.clone() {
match eval_statement(engine, statement) {
Err(Escape::Return(value)) => {
return_value = value.clone();
break;
},
_ => {},
}
}
engine.scopes.pop();
return_value
}
fn eval_variable_expression(engine: &mut Engine, expression: Expression) -> Value {
let name: String = match expression {
Expression::Variable { name } => name.into(),
_ => unreachable!(),
};
if let Some(scope) = engine.scopes.last() {
if let Some(value) = scope.get(&name) {
return value.clone();
} else {
panic!("undefined variable: {}", name);
}
} else {
if let Some(value) = engine.global_environment.get(&name) {
return value.clone();
} else {
panic!("undefined variable: {}", name);
}
}
}
fn eval_cast_expression(engine: &mut Engine, expression: Expression) -> Value {
let (kind, value) = match expression {
Expression::Cast { kind, value } => (kind, value),
_ => unreachable!()
};
let value = eval_expression(engine, *value);
match (kind, &value) {
(CastKind::String, Value::Int(i)) => Value::String(i.to_string()),
_ => value,
}
}

View File

@ -0,0 +1,52 @@
use std::{ops::{Add, Sub}, fmt::Display};
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub enum Value {
Null,
Int(i64),
Float(f64),
Bool(bool),
String(String),
}
impl Value {
pub fn is_truthy(&self) -> bool {
match self {
Value::Null => false,
Value::Bool(false) => false,
Value::Bool(true) => true,
_ => todo!(),
}
}
}
impl Add<Value> for Value {
type Output = Value;
fn add(self, rhs: Value) -> Self::Output {
match (self, rhs) {
(Value::Int(a), Value::Int(b)) => Value::Int(a + b),
_ => todo!(),
}
}
}
impl Sub<Value> for Value {
type Output = Value;
fn sub(self, rhs: Value) -> Self::Output {
match (self, rhs) {
(Value::Int(a), Value::Int(b)) => Value::Int(a - b),
_ => todo!(),
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::String(s) => write!(f, "{}", s),
_ => todo!(),
}
}
}

View File

@ -0,0 +1,23 @@
use cmder::{Program};
mod cmd;
mod engine;
fn main() {
let mut program = Program::new();
program
.bin_name("trunk")
.author("Ryan Chandler <support@ryangjchandler.co.uk>")
.description("An alternative interpreter and runtime for PHP.");
let run_cmd = program.subcommand("run");
run_cmd
.description("Run a PHP file through Trunk.")
.alias("r")
.argument("file", "The file you'd like to run.")
.action(cmd::run);
program.parse();
}

View File

@ -1,6 +1,7 @@
use std::cmp::{Eq, PartialEq};
use std::fmt::{Debug, Formatter, Result};
use std::ops::Deref;
use std::str::from_utf8;
/// A wrapper for Vec<u8> that provides a human-readable Debug impl and
/// a few other conveniences.
@ -68,6 +69,12 @@ impl From<String> for ByteString {
}
}
impl Into<String> for ByteString {
fn into(self) -> String {
String::from(from_utf8(&self.0).unwrap())
}
}
impl Deref for ByteString {
type Target = Vec<u8>;

View File

@ -2,8 +2,6 @@ mod ast;
mod parser;
mod traverser;
pub use ast::{
Block, Case, Catch, Expression, Identifier, InfixOp, MatchArm, Param, Program, Statement, Type,
};
pub use ast::*;
pub use parser::{ParseError, Parser};
pub use traverser::*;