add compiler + runtime

This commit is contained in:
SimonFJ20 2023-03-17 07:45:38 +01:00
parent 90cb7ebae5
commit ae5ead91da
9 changed files with 1507 additions and 54 deletions

View File

@ -2,7 +2,3 @@
name = "thiselang"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -1,11 +1,21 @@
use crate::tokens::Position;
pub trait AstNode {
fn fancy_string(&self, depth: usize) -> String;
}
#[derive(Debug)]
pub struct Node<T> {
pub struct Node<T: AstNode> {
pub value: T,
pub pos: Position,
}
impl<T: AstNode> Node<T> {
pub fn fancy_string(&self, depth: usize) -> String {
self.value.fancy_string(depth)
}
}
#[derive(Debug)]
pub enum Expr {
Error(String),
@ -148,3 +158,220 @@ pub enum Parameter {
Id { name: String, mutable: bool },
Spread(Box<Node<Parameter>>),
}
fn indent(depth: usize) -> String {
"".repeat(depth)
}
pub fn escape_string(text: &str) -> String {
let mut result = String::new();
for c in text.chars() {
match c {
'\\' => result.push_str("\\\\"),
'\0' => result.push_str("\\0"),
'\t' => result.push_str("\\t"),
'\r' => result.push_str("\\r"),
'\n' => result.push_str("\\n"),
'\'' => result.push_str("\\\'"),
'\"' => result.push_str("\\\""),
c => result.push(c),
};
}
return result;
}
impl AstNode for Expr {
fn fancy_string(&self, depth: usize) -> String {
match self {
Expr::Error(message) => format!("Error({message})"),
Expr::Unit => format!("Unit"),
Expr::Id(v) => format!("Id(`{v}`)"),
Expr::Int(v) => format!("Int({v})"),
Expr::Float(v) => format!("Float({v})"),
Expr::String(v) => format!("String(\"{}\")", escape_string(v)),
Expr::Bool(v) => format!("Bool({v})"),
Expr::Array(values) => format!(
"Array\n{}",
values
.iter()
.map(|v| format!(
"{}{}\n",
indent(depth + 1),
(*v).value.fancy_string(depth + 1)
))
.reduce(|mut acc, v| {
acc.push_str(&v);
acc
})
.unwrap_or("".to_string()),
),
Expr::Object(_) => format!("Object <...>"),
Expr::Tuple(_) => format!("Tuple <...>"),
Expr::If {
condition,
truthy,
falsy,
} => format!(
"If\n{}condition: {}\n{}truthy: {}\n{}falsy: {}",
indent(depth + 1),
condition.fancy_string(depth + 1),
indent(depth + 1),
truthy.fancy_string(depth + 1),
indent(depth + 1),
match falsy {
Some(expr) => expr.fancy_string(depth + 1),
None => "None".to_string(),
},
),
Expr::FunctionValue { parameters, body } => format!("FunctionValue <...>"),
Expr::Member { subject, value } => format!("Member <...>"),
Expr::Index { subject, value } => format!("Index <...>"),
Expr::Call { subject, arguments } => format!(
"Call\n{}subject: {}\n{}arguments:\n{}",
indent(depth + 1),
subject.fancy_string(depth + 1),
indent(depth + 1),
arguments
.iter()
.map(|v| format!(
"{}{}\n",
indent(depth + 2),
(*v).value.fancy_string(depth + 2)
))
.reduce(|mut acc, v| {
acc.push_str(&v);
acc
})
.unwrap_or("".to_string()),
),
Expr::Unary {
unary_type,
subject,
} => format!(
"Unary\n{}unary_type: {:?}\n{}subject: {}",
indent(depth + 1),
unary_type,
indent(depth + 1),
subject.fancy_string(depth + 1),
),
Expr::Binary {
binary_type,
left,
right,
} => format!(
"Binary\n{}binary_type: {:?}\n{}left: {}\n{}right: {}",
indent(depth + 1),
binary_type,
indent(depth + 1),
left.fancy_string(depth + 1),
indent(depth + 1),
right.fancy_string(depth + 1),
),
Expr::RangeExclusive { begin, end } => format!("RangeExclusive <...>"),
Expr::RangeInclusive { begin, end } => format!("RangeInclusive <...>"),
Expr::Assign {
assign_type,
subject,
value,
} => format!("Assign <...>"),
Expr::Let { subject, value } => format!(
"Let {{\n{}subject: {}\n{}value: {}\n{}}}",
indent(depth + 1),
subject.fancy_string(depth + 1),
indent(depth + 1),
match value {
Some(expr) => expr.fancy_string(depth + 1),
None => "None".to_string(),
},
indent(depth),
),
Expr::Continue => format!("Continue"),
Expr::Break => format!("Break"),
Expr::While { condition, body } => format!("While <...>"),
Expr::For {
subject,
value,
body,
} => format!("For <...>"),
Expr::Return(value) => format!(
"Return{}",
match value {
Some(v) => format!(
"\n{}value: {}",
indent(depth + 1),
v.fancy_string(depth + 1)
),
None => "".to_string(),
}
),
Expr::Function {
name,
parameters,
body,
} => format!(
"Function\n{}name: {}\n{}parameters:\n{}{}body: {}",
indent(depth + 1),
name,
indent(depth + 1),
parameters
.iter()
.map(|v| format!(
"{}{}\n",
indent(depth + 2),
(*v).value.fancy_string(depth + 2)
))
.reduce(|mut acc, v| {
acc.push_str(&v);
acc
})
.unwrap_or("".to_string()),
indent(depth + 1),
body.fancy_string(depth + 1)
),
Expr::Block { statements, value } => format!(
"Block\n{}statements:\n{}{}value: {}",
indent(depth + 1),
statements
.iter()
.map(|v| format!(
"{}{}\n",
indent(depth + 2),
(*v).value.fancy_string(depth + 2)
))
.reduce(|mut acc, v| {
acc.push_str(&v);
acc
})
.unwrap_or("".to_string()),
indent(depth + 1),
match value {
Some(expr) => expr.fancy_string(depth + 1),
None => "None".to_string(),
},
),
Expr::Spread(_) => format!("Spread <...>"),
}
}
}
impl AstNode for ObjectEntry {
fn fancy_string(&self, depth: usize) -> String {
match self {
ObjectEntry::Error(message) => format!("Error({message})"),
ObjectEntry::Pair { key, value } => format!("Pair <...>"),
ObjectEntry::Spread(_) => format!("Spread <...>"),
}
}
}
impl AstNode for Parameter {
fn fancy_string(&self, depth: usize) -> String {
match self {
Parameter::Error(message) => format!("Error({message})"),
Parameter::Id { name, mutable } => {
format!("Id(`{name}`{})", if *mutable { ", mutable" } else { "" })
}
Parameter::Spread(_) => format!("Spread <...>"),
}
}
}

25
src/builtins.rs Normal file
View File

@ -0,0 +1,25 @@
use crate::runtime::{RuntimeError, Value};
pub fn builtin_print(stack: &mut Vec<Value>) -> Result<Value, RuntimeError> {
print!(
"{}",
match stack.pop().ok_or_else(|| RuntimeError {
message: "expected 1 argument".to_string()
})? {
Value::Unit => format!("()"),
Value::Int(value) => format!("{value}"),
Value::Float(value) => format!("{value}"),
Value::String(value) => format!("{value}"),
Value::Bool(value) => format!("{value}"),
Value::FunctionReference(addr) => format!("<function reference {addr}>"),
Value::HeapReference(addr, gen) => format!("<heap reference {addr}, {gen}>"),
},
);
Ok(Value::Unit)
}
pub fn builtin_println(stack: &mut Vec<Value>) -> Result<Value, RuntimeError> {
builtin_print(stack)?;
println!("");
Ok(Value::Unit)
}

View File

@ -1,25 +1,29 @@
#[derive(Debug)]
pub enum Instructions {
pub enum Instruction {
Duplicate,
Pop,
Jump(usize),
JumpIf(usize),
Call,
Return,
Reserve(usize),
Load(usize),
Store(usize),
LoadLocal(usize),
StoreLocal(usize),
HeapLoad,
HeapStore,
HeapAlloc,
PushUnit,
PushInt(i64),
PushFloat(i64),
PushFloat(f64),
PushString(String),
PushBool(bool),
PushFunctionReference(usize),
Not,
Negate,
Reference,
Dereference,
Add,
Subtract,
@ -38,3 +42,66 @@ pub enum Instructions {
In,
NotIn,
}
#[derive(Debug)]
pub struct Module {
pub functions: Vec<Function>,
pub entry_function: usize,
}
#[derive(Debug)]
pub enum Function {
BuiltIn {
arguments: usize,
},
UserDefined {
instructions: Vec<Instruction>,
arguments: usize,
locals_reserve: usize,
},
}
impl Module {
pub fn fancy_string(&self) -> String {
format!(
"Module\n{}",
self.functions
.iter()
.map(|f| f.fancy_string())
.reduce(|mut acc, v| {
acc.push_str(&v);
acc
})
.unwrap_or("".to_string())
)
}
}
impl Function {
pub fn instructions(&self) -> &Vec<Instruction> {
match self {
Self::UserDefined { instructions, .. } => instructions,
_ => panic!("expected user defined function"),
}
}
pub fn arguments(&self) -> usize {
match self {
Self::BuiltIn { arguments } |
Self::UserDefined { arguments, .. } => *arguments,
}
}
pub fn locals_reserve(&self) -> usize {
match self {
Self::UserDefined { locals_reserve, .. } => *locals_reserve,
_ => panic!("expected user defined function"),
}
}
pub fn fancy_string(&self) -> String {
match self {
Self::BuiltIn { arguments } => format!(" - BuiltIn Function\n arguments: {}", arguments),
Self::UserDefined { instructions, arguments, locals_reserve } =>
format!(" - Function\n arguments: {}\n locals_reserve: {}\n instructions:\n{}", arguments, locals_reserve, instructions.iter().map(|i| format!(" - {:?}\n", i)).reduce(|mut acc, v|{acc.push_str(&v); acc}).unwrap_or("".to_string()))
}
}
}

View File

@ -1 +1,352 @@
use std::collections::HashMap;
use crate::{
ast::{BinaryType, Expr, Node, Parameter, UnaryType},
bytecode::{Function, Instruction, Module},
tokens::Position,
};
#[derive(Debug)]
pub struct CompileError {
message: String,
pos: Position,
}
pub struct ModuleContext {
functions: Vec<Function>,
global_functions: HashMap<String, Local>,
}
pub fn compile_module(ast: Node<Expr>) -> Result<Module, CompileError> {
let mut context = ModuleContext {
functions: vec![],
global_functions: HashMap::new(),
};
context.functions.push(Function::BuiltIn { arguments: 1 });
context.global_functions.insert(
"print".to_string(),
Local {
address: 0,
mutable: false,
},
);
context.functions.push(Function::BuiltIn { arguments: 1 });
context.global_functions.insert(
"println".to_string(),
Local {
address: 1,
mutable: false,
},
);
let start_function = FunctionCompiler::new(&mut context).compile(Vec::new(), ast)?;
let start_function_index = context.functions.len();
context.functions.push(start_function);
Ok(Module {
functions: context.functions,
entry_function: start_function_index,
})
}
struct Local {
address: usize,
mutable: bool,
}
pub struct FunctionCompiler<'a> {
instructions: Vec<Instruction>,
context: &'a mut ModuleContext,
locals: Vec<HashMap<String, Local>>,
local_counter: usize,
}
impl<'a> FunctionCompiler<'a> {
pub fn new(context: &'a mut ModuleContext) -> Self {
Self {
instructions: Vec::new(),
context,
locals: vec![HashMap::new()],
local_counter: 0,
}
}
pub fn compile(
mut self,
parameters: Vec<Node<Parameter>>,
body: Node<Expr>,
) -> Result<Function, CompileError> {
let mut param_stores = Vec::<Instruction>::new();
for param in &parameters {
match &param.value {
Parameter::Error(_) => todo!(),
Parameter::Id { name, mutable } => {
let addr = match self.define_local(name.to_string(), *mutable) {
Ok(addr) => addr,
Err(error) => {
return Err(CompileError {
message: error,
pos: param.pos.clone(),
})
}
};
param_stores.push(Instruction::StoreLocal(addr));
}
Parameter::Spread(_) => todo!(),
}
}
for store in param_stores.into_iter().rev() {
self.push(store);
}
self.compile_expr(body)?;
self.push(Instruction::Return);
Ok(Function::UserDefined {
arguments: parameters.len(),
locals_reserve: self.local_counter,
instructions: self.instructions,
})
}
fn compile_expr(&mut self, expr: Node<Expr>) -> Result<(), CompileError> {
match expr.value {
Expr::Error(message) => Err(CompileError {
message,
pos: expr.pos,
}),
Expr::Unit => todo!(),
Expr::Id(name) => match self.get_local(&name) {
Some(local) => {
self.push(Instruction::LoadLocal(local.address));
Ok(())
}
None => {
return Err(CompileError {
message: format!("id \"{}\" not defined", name),
pos: expr.pos,
})
}
},
Expr::Int(value) => {
self.push(Instruction::PushInt(value));
Ok(())
}
Expr::Float(value) => {
self.push(Instruction::PushFloat(value));
Ok(())
}
Expr::String(value) => {
self.push(Instruction::PushString(value));
Ok(())
}
Expr::Bool(value) => {
self.push(Instruction::PushBool(value));
Ok(())
}
Expr::Array(_) => todo!(),
Expr::Object(_) => todo!(),
Expr::Tuple(_) => todo!(),
Expr::If {
condition,
truthy,
falsy,
} => {
self.compile_expr(*condition)?;
self.push(Instruction::Not);
let from_then_to_else = self.instructions.len();
self.push(Instruction::JumpIf(0));
self.compile_expr(*truthy)?;
if let Some(expr) = falsy {
self.instructions[from_then_to_else] =
Instruction::JumpIf(self.instructions.len() + 1);
let from_else_to_end = self.instructions.len();
self.push(Instruction::Jump(0));
self.compile_expr(*expr)?;
self.instructions[from_else_to_end] =
Instruction::Jump(self.instructions.len());
} else {
self.instructions[from_then_to_else] =
Instruction::JumpIf(self.instructions.len());
}
Ok(())
}
Expr::FunctionValue { parameters, body } => todo!(),
Expr::Member { subject, value } => todo!(),
Expr::Index { subject, value } => todo!(),
Expr::Call { subject, arguments } => {
for arg in arguments {
self.compile_expr(arg)?;
}
self.compile_expr(*subject)?;
self.push(Instruction::Call);
Ok(())
}
Expr::Unary {
unary_type,
subject,
} => {
self.compile_expr(*subject)?;
self.push(match unary_type {
UnaryType::Not => Instruction::Not,
UnaryType::Negate => Instruction::Negate,
UnaryType::Reference => todo!(),
UnaryType::ReferenceMut => todo!(),
UnaryType::Dereference => todo!(),
});
Ok(())
}
Expr::Binary {
binary_type,
left,
right,
} => {
self.compile_expr(*left)?;
self.compile_expr(*right)?;
self.push(match binary_type {
BinaryType::Exponentiate => Instruction::Exponentiate,
BinaryType::Multiply => Instruction::Multiply,
BinaryType::Divide => Instruction::Divide,
BinaryType::Modulo => Instruction::Modulo,
BinaryType::Add => Instruction::Add,
BinaryType::Subtract => Instruction::Subtract,
BinaryType::LT => Instruction::LT,
BinaryType::LTE => Instruction::LTE,
BinaryType::GT => Instruction::GT,
BinaryType::GTE => Instruction::GTE,
BinaryType::In => Instruction::In,
BinaryType::NotIn => Instruction::NotIn,
BinaryType::Equal => Instruction::Equal,
BinaryType::Inequal => Instruction::Inequal,
BinaryType::And => Instruction::And,
BinaryType::Or => Instruction::Or,
});
Ok(())
}
Expr::RangeExclusive { begin, end } => todo!(),
Expr::RangeInclusive { begin, end } => todo!(),
Expr::Assign {
assign_type,
subject,
value,
} => todo!(),
Expr::Let { subject, value } => match subject.value {
Parameter::Error(_) => todo!(),
Parameter::Id { name, mutable } => {
let addr = match self.define_local(name, mutable) {
Ok(addr) => addr,
Err(error) => {
return Err(CompileError {
message: error,
pos: expr.pos,
})
}
};
match value {
Some(expr) => {
self.compile_expr(*expr)?;
self.push(Instruction::StoreLocal(addr));
}
None => {}
}
self.push(Instruction::PushUnit);
Ok(())
}
Parameter::Spread(_) => todo!(),
},
Expr::Continue => todo!(),
Expr::Break => todo!(),
Expr::While { condition, body } => todo!(),
Expr::For {
subject,
value,
body,
} => todo!(),
Expr::Return(value) => {
if let Some(value) = value {
self.compile_expr(*value)?;
} else {
self.push(Instruction::PushUnit);
}
self.push(Instruction::Return);
Ok(())
}
Expr::Function {
name,
parameters,
body,
} => {
let function_compiler = FunctionCompiler::new(self.context);
let function = function_compiler.compile(parameters, *body)?;
let function_addr = self.context.functions.len();
self.context.functions.push(function);
let local_addr = match self.define_local(name, false) {
Ok(addr) => addr,
Err(message) => {
return Err(CompileError {
message,
pos: expr.pos,
})
}
};
self.push(Instruction::PushFunctionReference(function_addr));
self.push(Instruction::StoreLocal(local_addr));
self.push(Instruction::PushUnit);
Ok(())
}
Expr::Block { statements, value } => {
self.enter_scope();
for s in statements {
self.compile_expr(s)?;
self.instructions.pop().expect("pushed");
}
if let Some(value) = value {
self.compile_expr(*value)?;
} else {
self.push(Instruction::PushUnit);
};
self.leave_scope();
Ok(())
}
Expr::Spread(_) => todo!(),
}
}
fn enter_scope(&mut self) {
self.locals.push(HashMap::new())
}
fn leave_scope(&mut self) {
self.locals.pop().unwrap();
}
fn define_local(&mut self, name: String, mutable: bool) -> Result<usize, String> {
if self.locals.last().unwrap().contains_key(&name) {
Err(format!("identifier \"{}\" already defined", name))
} else {
let address = self.local_counter;
self.local_counter += 1;
self.locals
.last_mut()
.unwrap()
.insert(name, Local { address, mutable });
Ok(address)
}
}
fn get_local(&self, name: &str) -> Option<&Local> {
for table in self.locals.iter().rev() {
if table.contains_key(name) {
return table.get(name);
}
}
if self.context.global_functions.contains_key(name) {
if let Some(local) = self.context.global_functions.get(name) {
return Some(local);
}
}
None
}
fn push(&mut self, instruction: Instruction) {
self.instructions.push(instruction);
}
}

View File

@ -1,44 +1,51 @@
#![allow(dead_code)]
mod ast;
mod builtins;
mod bytecode;
mod compiler;
mod lexer;
mod parser;
mod runtime;
mod tokens;
use crate::ast::AstNode;
use crate::compiler::compile_module;
use crate::lexer::Lexer;
use crate::parser::Parser;
use crate::runtime::Runtime;
fn main() {
// println!("tokens = [");
// let text = "** 3.14 \"foo\" false true ( ) + += /* 1 /* 2 */ 3 */ // 4 \n 5";
// let lexer = Lexer::new(text);
// lexer.for_each(|token| {
// println!(
// " {:?} = `{}`,",
// token.token_type,
// &text[token.pos.index..token.pos.index + token.length]
// );
// });
// println!("]");
let text = r#"
{
fn myfunc(a, b) {
return a + b;
}
myfunc(1, 2);
println("hello world");
let text2 = "[1, 1..=2, ...a]";
let lexer2 = Lexer::new(text2);
println!("tokens = [");
lexer2.for_each(|token| {
println!(
" ({:?}, `{}`, [{}], {}, {}:{}),",
token.token_type,
&text2[token.pos.index..token.pos.index + token.length],
token.pos.index,
token.length,
token.pos.line,
token.pos.col,
);
});
println!("]");
let mut parser = Parser::new(text2, Lexer::new(text2));
};
"#;
println!("=== text ===\n {}", text.replace("\n", " \n"));
println!("=== tokenizing... ===");
let lexer = Lexer::new(text);
println!("=== tokens ===");
lexer.for_each(|token| println!(" {}", token.fancy_string(text)));
println!("=== parsing... ===");
let mut parser = Parser::new(text, Lexer::new(text));
let expr = parser.parse_expr();
println!("ast = {:#?}", expr);
println!("=== ast ===\n{}", expr.value.fancy_string(0));
println!("=== compiling... ===");
let module = match compile_module(expr) {
Ok(module) => module,
Err(error) => return println!("{:#?}", error),
};
println!("=== module ===\n{}", module.fancy_string());
println!("=== executing... ===");
let runtime = Runtime::new(module);
match runtime.run() {
Ok(value) => println!("=== result ===\n{:?}", value),
Err(error) => println!("{:#?}", error),
}
}

View File

@ -1,7 +1,7 @@
use std::iter::Peekable;
use std::str::Chars;
use crate::ast::{AssignType, BinaryType, Expr, Node, ObjectEntry, Parameter, UnaryType};
use crate::ast::{AssignType, AstNode, BinaryType, Expr, Node, ObjectEntry, Parameter, UnaryType};
use crate::tokens::{PositionKnowing, Token, TokenType};
pub struct Parser<'a, Tokens>
@ -25,7 +25,7 @@ where
}
}
pub fn parse_top_level_statements(&mut self) -> Vec<Node<Expr>> {
pub fn parse(mut self) -> Vec<Node<Expr>> {
let mut statements = Vec::<Node<Expr>>::new();
while !self.done() {
statements.push(self.parse_statement());
@ -45,6 +45,8 @@ where
}
if self.done() || !self.current_is(TokenType::RBrace) {
statements.push(self.error("expected '}'"));
} else {
self.step();
}
self.node(Expr::Block {
statements,
@ -58,13 +60,53 @@ where
}
match self.current().token_type {
TokenType::Fn => self.parse_function(),
TokenType::Return => self.parse_return(),
TokenType::Return => {
let statement = self.parse_return();
if self.done() || !self.current_is(TokenType::Semicolon) {
self.error("expected ';'")
} else {
self.step();
statement
}
}
TokenType::For => self.parse_for(),
TokenType::While => self.parse_while(),
TokenType::Break => self.parse_break(),
TokenType::Continue => self.parse_continue(),
TokenType::Let => self.parse_let(),
_ => self.parse_assign(),
TokenType::Break => {
let statement = self.parse_break();
if self.done() || !self.current_is(TokenType::Semicolon) {
self.error("expected ';'")
} else {
self.step();
statement
}
}
TokenType::Continue => {
let statement = self.parse_continue();
if self.done() || !self.current_is(TokenType::Semicolon) {
self.error("expected ';'")
} else {
self.step();
statement
}
}
TokenType::Let => {
let statement = self.parse_let();
if self.done() || !self.current_is(TokenType::Semicolon) {
self.error("expected ';'")
} else {
self.step();
statement
}
}
_ => {
let statement = self.parse_assign();
if self.done() || !self.current_is(TokenType::Semicolon) {
self.error("expected ';'")
} else {
self.step();
statement
}
}
}
}
@ -91,7 +133,7 @@ where
return Err(self.error("expected '('"));
}
self.step();
let mut parameters = self.parse_function_parameters();
let parameters = self.parse_function_parameters();
if self.done() || !self.current_is(TokenType::RParen) {
return Err(self.error("expected ')'"));
}
@ -102,7 +144,7 @@ where
self.step();
self.parse_expr()
} else {
return Err(self.error("expected '{'"));
return Err(self.error("expected '{' or '=>'"));
};
Ok((parameters, body))
}
@ -684,7 +726,7 @@ where
}
self.step();
let mut values = Vec::<Node<ObjectEntry>>::new();
if !self.done() && !self.current_is(TokenType::RBracket) {
if !self.done() && !self.current_is(TokenType::RBrace) {
if self.current_is(TokenType::DotDotDot) {
values.push(self.parse_object_entry());
} else {
@ -702,8 +744,8 @@ where
}
}
}
if self.done() || !self.current_is(TokenType::RBracket) {
return self.error("expected ']'");
if self.done() || !self.current_is(TokenType::RBrace) {
return self.error("expected '}'");
}
self.step_and_node(Expr::Object(values))
}
@ -798,11 +840,11 @@ where
self.text[token.pos.index..token.pos.index + token.length].to_string()
}
fn step_and_node<T>(&mut self, value: T) -> Node<T> {
fn step_and_node<T: AstNode>(&mut self, value: T) -> Node<T> {
self.step_and(self.node(value))
}
fn node<T>(&self, value: T) -> Node<T> {
fn node<T: AstNode>(&self, value: T) -> Node<T> {
Node {
value,
pos: self.tokens.pos(),

722
src/runtime.rs Normal file
View File

@ -0,0 +1,722 @@
use std::collections::VecDeque;
use crate::{
builtins::{builtin_print, builtin_println},
bytecode::{Function, Instruction, Module},
};
#[derive(Debug, Clone)]
pub enum Value {
Unit,
Int(i64),
Float(f64),
String(String),
Bool(bool),
FunctionReference(usize),
HeapReference(usize, usize),
}
#[derive(Debug)]
pub struct RuntimeError {
pub message: String,
}
struct Call {
return_addr: usize,
instruction_ptr: usize,
stack_base: usize,
locals_base: usize,
}
struct HeapEntry {
value: Value,
generation: usize,
gc_marker: bool,
}
pub struct Runtime {
functions: Vec<Function>,
call_stack: Vec<Call>,
stack: Vec<Value>,
stack_base: usize,
locals: Vec<Value>,
locals_base: usize,
heap: Vec<Option<HeapEntry>>,
heap_generation: usize,
deallocated_addrs: VecDeque<usize>,
function_ptr: usize,
instruction_ptr: usize,
}
impl Runtime {
pub fn new(module: Module) -> Self {
Self {
functions: module.functions,
call_stack: Vec::new(),
stack: Vec::new(),
stack_base: 0,
locals: Vec::new(),
locals_base: 0,
heap: Vec::new(),
heap_generation: 0,
deallocated_addrs: VecDeque::new(),
function_ptr: module.entry_function,
instruction_ptr: 0,
}
}
pub fn run(mut self) -> Result<Value, RuntimeError> {
self.call_stack.push(Call {
return_addr: 0,
instruction_ptr: 0,
stack_base: 0,
locals_base: 0,
});
for _ in 0..self.functions[self.function_ptr].locals_reserve() {
self.locals.push(Value::Unit);
}
while self.call_stack.len() > 0 {
let debug = false;
if debug {
println!("function_ptr = {}", self.function_ptr);
println!("instruction_ptr = {}", self.instruction_ptr);
println!("ins = {:?}", self.current_instruction());
println!("stack before = {:?}", self.stack);
println!("locals before = {:?}", self.locals);
}
self.run_instruction()?;
if debug {
println!("stack after = {:?}", self.stack);
println!("locals after = {:?}", self.locals);
println!("\n");
}
}
self.stack
.pop()
.ok_or_else(|| self.error_value("stack empty"))
}
fn run_instruction(&mut self) -> Result<(), RuntimeError> {
if self.instruction_ptr >= self.current_function().instructions().len() {
return self.error("exceeded function end");
}
match &self.functions[self.function_ptr].instructions()[self.instruction_ptr] {
Instruction::Duplicate => {
self.assert_stack_amount(1)?;
self.stack
.push(self.stack.last().expect("stack size asserted").clone());
self.step_and_ok()
}
Instruction::Pop => {
self.assert_stack_amount(1)?;
if self.stack.len() == self.stack_base {}
let _ = self.stack.pop().expect("stack size asserted");
self.step_and_ok()
}
Instruction::Jump(addr) => {
if !(0..self.current_function().instructions().len()).contains(&addr) {
return self.error("address outside function");
}
self.instruction_ptr = *addr;
Ok(())
}
Instruction::JumpIf(addr) => {
self.assert_stack_amount(1)?;
let value = self.stack.pop().expect("stack size asserted");
let should_jump = match value {
Value::Bool(should_jump) => should_jump,
_ => return self.error("expected bool value"),
};
if !(0..self.current_function().instructions().len()).contains(&addr) {
return self.error("instruction address out of range");
}
if should_jump {
self.instruction_ptr = *addr;
} else {
self.step();
}
Ok(())
}
Instruction::Call => {
self.assert_stack_amount(1)?;
let value = self.stack.pop().expect("stack size asserted");
let addr = match value {
Value::FunctionReference(addr) => addr,
_ => return self.error("expected function reference"),
};
if !(0..self.functions.len()).contains(&addr) {
return self.error("function address out of range");
}
let function = &self.functions[addr];
if let Function::BuiltIn { .. } = function {
let value = match addr {
0 => builtin_print(&mut self.stack)?,
1 => builtin_println(&mut self.stack)?,
_ => return self.error("unknown builtin function"),
};
self.stack.push(value);
return Ok(());
}
if self.stack.len() < self.stack_base + function.arguments() {
return self.error(&format!(
"function expects {} arguments, {} present",
function.arguments(),
self.stack.len() - self.stack_base
));
}
self.call_stack.push(Call {
return_addr: self.function_ptr,
instruction_ptr: self.instruction_ptr + 1,
stack_base: self.stack_base,
locals_base: self.locals_base,
});
self.stack_base = self.stack.len() - function.arguments();
self.locals_base = self.locals.len();
self.locals
.reserve(self.locals_base + function.locals_reserve());
for _ in 0..function.locals_reserve() {
self.locals.push(Value::Unit);
}
self.function_ptr = addr;
self.instruction_ptr = 0;
Ok(())
}
Instruction::Return => {
let call = self
.call_stack
.pop()
.ok_or_else(|| self.error_value("call stack underflow"))?;
if self.stack.len() != self.stack_base + 1 {
return self.error(&format!(
"expected 1 value on stack on return, got {}",
self.stack.len() - self.stack_base
));
}
self.stack_base = call.stack_base;
for _ in 0..self.locals.len() - self.locals_base {
self.locals.pop().expect("size asserted");
}
self.locals_base = call.locals_base;
self.function_ptr = call.return_addr;
self.instruction_ptr = call.instruction_ptr;
Ok(())
}
Instruction::LoadLocal(addr) => {
if *addr == 0 {
self.stack.push(Value::FunctionReference(0));
return self.step_and_ok();
} else if *addr == 1 {
self.stack.push(Value::FunctionReference(1));
return self.step_and_ok();
}
if !(0..self.locals.len() - self.locals_base).contains(&addr) {
return self.error("local address out of range");
}
self.stack
.push(self.locals[self.locals_base + addr].clone());
self.step_and_ok()
}
Instruction::StoreLocal(addr) => {
self.assert_stack_amount(1)?;
if !(0..self.locals.len() - self.locals_base).contains(&addr) {
return self.error("local address out of range");
}
self.locals[self.locals_base + *addr] = self.stack.pop().expect("value");
self.step_and_ok()
}
Instruction::HeapLoad => {
self.assert_stack_amount(1)?;
let (addr, generation) = match self.stack.pop() {
Some(Value::HeapReference(addr, generation)) => (addr, generation),
_ => return self.error("expected heap address"),
};
if !(0..self.heap.len()).contains(&addr) {
return self.error("heap address out of range");
};
let entry = match &self.heap[addr] {
Some(entry) if entry.generation == generation => entry,
_ => return self.error("invalid heap address"),
};
self.stack.push(entry.value.clone());
self.step_and_ok()
}
Instruction::HeapStore => {
self.assert_stack_amount(2)?;
let (addr, generation) = match self.stack.pop() {
Some(Value::HeapReference(addr, generation)) => (addr, generation),
_ => return self.error("expected heap address"),
};
if !(0..self.heap.len()).contains(&addr) {
return self.error("heap address out of range");
};
let entry = match &mut self.heap[addr] {
Some(entry) if entry.generation == generation => entry,
_ => return self.error("invalid heap address"),
};
entry.value = self.stack.pop().expect("stack size asserted");
Ok(())
}
Instruction::HeapAlloc => {
let addr = if let Some(addr) = self.deallocated_addrs.pop_front() {
self.heap[addr] = Some(HeapEntry {
value: Value::Unit,
generation: self.heap_generation,
gc_marker: false,
});
addr
} else {
let addr = self.heap.len();
self.heap.push(Some(HeapEntry {
value: Value::Unit,
generation: self.heap_generation,
gc_marker: false,
}));
addr
};
self.stack
.push(Value::HeapReference(addr, self.heap_generation));
self.heap_generation = self.heap_generation.wrapping_add(1);
self.step_and_ok()
}
Instruction::PushUnit => {
self.stack.push(Value::Unit);
self.step_and_ok()
}
Instruction::PushInt(value) => {
self.stack.push(Value::Int(*value));
self.step_and_ok()
}
Instruction::PushFloat(value) => {
self.stack.push(Value::Float(*value));
self.step_and_ok()
}
Instruction::PushString(value) => {
self.stack.push(Value::String(value.clone()));
self.step_and_ok()
}
Instruction::PushBool(value) => {
self.stack.push(Value::Bool(*value));
self.step_and_ok()
}
Instruction::PushFunctionReference(value) => {
self.stack.push(Value::FunctionReference(*value));
self.step_and_ok()
}
Instruction::Not => {
self.assert_stack_amount(1)?;
match self.stack.pop().expect("stack size asserted") {
Value::Bool(value) => {
self.stack.push(Value::Bool(!value));
self.step_and_ok()
}
_ => self.error("invalid type for 'not', expected bool"),
}
}
Instruction::Negate => {
self.assert_stack_amount(1)?;
match self.stack.pop().expect("stack size asserted") {
Value::Int(value) => {
self.stack.push(Value::Int(-value));
self.step_and_ok()
}
Value::Float(value) => {
self.stack.push(Value::Float(-value));
self.step_and_ok()
}
_ => self.error("invalid type for 'negate', expected int or float"),
}
}
Instruction::Add => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Int(right), Value::Int(left)) => {
self.stack.push(Value::Int(left + right));
self.step_and_ok()
}
(Value::Int(right), Value::Float(left)) => {
self.stack.push(Value::Float(left + right as f64));
self.step_and_ok()
}
(Value::Float(right), Value::Int(left)) => {
self.stack.push(Value::Float((left as f64) + right));
self.step_and_ok()
}
(Value::Float(right), Value::Float(left)) => {
self.stack.push(Value::Float(left + right));
self.step_and_ok()
}
_ => self.error("invalid type for 'add', expected (int|float, int|float)"),
}
}
Instruction::Subtract => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Int(right), Value::Int(left)) => {
self.stack.push(Value::Int(left - right));
self.step_and_ok()
}
(Value::Int(right), Value::Float(left)) => {
self.stack.push(Value::Float(left - right as f64));
self.step_and_ok()
}
(Value::Float(right), Value::Int(left)) => {
self.stack.push(Value::Float((left as f64) - right));
self.step_and_ok()
}
(Value::Float(right), Value::Float(left)) => {
self.stack.push(Value::Float(left - right));
self.step_and_ok()
}
_ => self.error("invalid type for 'subtract', expected (int|float, int|float)"),
}
}
Instruction::Multiply => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Int(right), Value::Int(left)) => {
self.stack.push(Value::Int(left * right));
self.step_and_ok()
}
(Value::Int(right), Value::Float(left)) => {
self.stack.push(Value::Float(left * right as f64));
self.step_and_ok()
}
(Value::Float(right), Value::Int(left)) => {
self.stack.push(Value::Float((left as f64) * right));
self.step_and_ok()
}
(Value::Float(right), Value::Float(left)) => {
self.stack.push(Value::Float(left * right));
self.step_and_ok()
}
_ => self.error("invalid type for 'multiply', expected (int|float, int|float)"),
}
}
Instruction::Divide => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Int(right), Value::Int(left)) => {
self.stack.push(Value::Int(left / right));
self.step_and_ok()
}
(Value::Int(right), Value::Float(left)) => {
self.stack.push(Value::Float(left / right as f64));
self.step_and_ok()
}
(Value::Float(right), Value::Int(left)) => {
self.stack.push(Value::Float((left as f64) / right));
self.step_and_ok()
}
(Value::Float(right), Value::Float(left)) => {
self.stack.push(Value::Float(left / right));
self.step_and_ok()
}
_ => self.error("invalid type for 'divide', expected (int|float, int|float)"),
}
}
Instruction::Modulo => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Int(right), Value::Int(left)) => {
self.stack.push(Value::Int(left % right));
self.step_and_ok()
}
(Value::Int(right), Value::Float(left)) => {
self.stack.push(Value::Float(left % right as f64));
self.step_and_ok()
}
(Value::Float(right), Value::Int(left)) => {
self.stack.push(Value::Float((left as f64) % right));
self.step_and_ok()
}
(Value::Float(right), Value::Float(left)) => {
self.stack.push(Value::Float(left % right));
self.step_and_ok()
}
_ => self.error("invalid type for 'modulo', expected (int|float, int|float)"),
}
}
Instruction::Exponentiate => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Int(right), Value::Int(left)) => {
self.stack.push(Value::Int(left.pow(right as u32)));
self.step_and_ok()
}
(Value::Int(right), Value::Float(left)) => {
self.stack.push(Value::Float(left.powf(right as f64)));
self.step_and_ok()
}
(Value::Float(right), Value::Int(left)) => {
self.stack.push(Value::Float((left as f64).powf(right)));
self.step_and_ok()
}
(Value::Float(right), Value::Float(left)) => {
self.stack.push(Value::Float(left.powf(right)));
self.step_and_ok()
}
_ => self
.error("invalid type for 'exponentiate', expected (int|float, int|float)"),
}
}
Instruction::LT => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Int(right), Value::Int(left)) => {
self.stack.push(Value::Bool(left < right));
self.step_and_ok()
}
(Value::Int(right), Value::Float(left)) => {
self.stack.push(Value::Bool(left < right as f64));
self.step_and_ok()
}
(Value::Float(right), Value::Int(left)) => {
self.stack.push(Value::Bool((left as f64) < right));
self.step_and_ok()
}
(Value::Float(right), Value::Float(left)) => {
self.stack.push(Value::Bool(left < right));
self.step_and_ok()
}
_ => {
self.error("invalid type for 'less than', expected (int|float, int|float)")
}
}
}
Instruction::LTE => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Int(right), Value::Int(left)) => {
self.stack.push(Value::Bool(left <= right));
self.step_and_ok()
}
(Value::Int(right), Value::Float(left)) => {
self.stack.push(Value::Bool(left <= right as f64));
self.step_and_ok()
}
(Value::Float(right), Value::Int(left)) => {
self.stack.push(Value::Bool((left as f64) <= right));
self.step_and_ok()
}
(Value::Float(right), Value::Float(left)) => {
self.stack.push(Value::Bool(left <= right));
self.step_and_ok()
}
_ => self.error(
"invalid type for 'less than or equal', expected (int|float, int|float)",
),
}
}
Instruction::GT => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Int(right), Value::Int(left)) => {
self.stack.push(Value::Bool(left > right));
self.step_and_ok()
}
(Value::Int(right), Value::Float(left)) => {
self.stack.push(Value::Bool(left > right as f64));
self.step_and_ok()
}
(Value::Float(right), Value::Int(left)) => {
self.stack.push(Value::Bool((left as f64) > right));
self.step_and_ok()
}
(Value::Float(right), Value::Float(left)) => {
self.stack.push(Value::Bool(left > right));
self.step_and_ok()
}
_ => self
.error("invalid type for 'greater than', expected (int|float, int|float)"),
}
}
Instruction::GTE => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Int(right), Value::Int(left)) => {
self.stack.push(Value::Bool(left >= right));
self.step_and_ok()
}
(Value::Int(right), Value::Float(left)) => {
self.stack.push(Value::Bool(left >= right as f64));
self.step_and_ok()
}
(Value::Float(right), Value::Int(left)) => {
self.stack.push(Value::Bool((left as f64) >= right));
self.step_and_ok()
}
(Value::Float(right), Value::Float(left)) => {
self.stack.push(Value::Bool(left >= right));
self.step_and_ok()
}
_ => self.error(
"invalid type for 'greater than or equal', expected (int|float, int|float)",
),
}
}
Instruction::Equal => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Unit, Value::Unit) => {
self.stack.push(Value::Bool(true));
self.step_and_ok()
}
(Value::Int(right), Value::Int(left)) => {
self.stack.push(Value::Bool(left == right));
self.step_and_ok()
}
(Value::Float(right), Value::Float(left)) => {
self.stack.push(Value::Bool(left == right));
self.step_and_ok()
}
(Value::String(right), Value::String(left)) => {
self.stack.push(Value::Bool(left == right));
self.step_and_ok()
}
(Value::Bool(right), Value::Bool(left)) => {
self.stack.push(Value::Bool(left == right));
self.step_and_ok()
}
_ => {
self.error(
"invalid type for 'equal', expected (unit, unit), (int, int), (float, float), (string, string) or (bool, bool)"
)
}
}
}
Instruction::Inequal => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Unit, Value::Unit) => {
self.stack.push(Value::Bool(false));
self.step_and_ok()
}
(Value::Int(right), Value::Int(left)) => {
self.stack.push(Value::Bool(left != right));
self.step_and_ok()
}
(Value::Float(right), Value::Float(left)) => {
self.stack.push(Value::Bool(left != right));
self.step_and_ok()
}
(Value::String(right), Value::String(left)) => {
self.stack.push(Value::Bool(left != right));
self.step_and_ok()
}
(Value::Bool(right), Value::Bool(left)) => {
self.stack.push(Value::Bool(left != right));
self.step_and_ok()
}
_ => {
self.error(
"invalid type for 'inequal', expected (unit, unit), (int, int), (float, float), (string, string) or (bool, bool)"
)
}
}
}
Instruction::And => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Bool(right), Value::Bool(left)) => {
self.stack.push(Value::Bool(left && right));
self.step_and_ok()
}
_ => self.error("invalid type for 'and', expected (bool, bool)"),
}
}
Instruction::Or => {
self.assert_stack_amount(2)?;
match (
self.stack.pop().expect("stack size asserted"),
self.stack.pop().expect("stack size asserted"),
) {
(Value::Bool(right), Value::Bool(left)) => {
self.stack.push(Value::Bool(left || right));
self.step_and_ok()
}
_ => self.error("invalid type for 'or', expected (bool, bool)"),
}
}
Instruction::In => self.error("'not in' not implemented"),
Instruction::NotIn => self.error("'not in' not implemented"),
}
}
fn error(&self, message: &str) -> Result<(), RuntimeError> {
Err(self.error_value(message))
}
fn error_value(&self, message: &str) -> RuntimeError {
RuntimeError {
message: message.to_string(),
}
}
fn assert_stack_amount(&self, amount: usize) -> Result<(), RuntimeError> {
if self.stack.len() < self.stack_base + amount {
self.error(&format!(
"expected {} value(s) on the stack, {} present",
amount,
self.stack.len() - self.stack_base
))
} else {
Ok(())
}
}
fn step_and_ok(&mut self) -> Result<(), RuntimeError> {
self.step();
Ok(())
}
fn step(&mut self) {
self.instruction_ptr += 1;
}
fn current_instruction(&self) -> &Instruction {
&self.current_function().instructions()[self.instruction_ptr]
}
fn current_function(&self) -> &Function {
&self.functions[self.function_ptr]
}
}

View File

@ -1,3 +1,5 @@
use crate::ast::escape_string;
#[derive(Debug, Clone)]
pub struct Position {
pub index: usize,
@ -90,3 +92,17 @@ pub struct Token {
pub trait PositionKnowing {
fn pos(&self) -> Position;
}
impl Token {
pub fn fancy_string(&self, text: &str) -> String {
format!(
"[{}:{}]\t{}:{}\t{:?}(`{}`)",
self.pos.index,
self.length,
self.pos.line,
self.pos.col,
self.token_type,
escape_string(&text[self.pos.index..self.pos.index + self.length]),
)
}
}