refactored from opcode bytes to enum

This commit is contained in:
Shautvast 2025-11-21 16:05:06 +01:00
parent 719238bd77
commit cad1f8f1ec
12 changed files with 359 additions and 535 deletions

View file

@ -17,6 +17,17 @@ macro_rules! mut_list_fn {
}; };
} }
macro_rules! list_fn {
($list:ident, $args:ident => $body:expr) => {
|self_val: Value, $args: Vec<Value>| -> Result<Value, RuntimeError> {
match self_val {
Value::List($list) => $body,
_ => Err(expected_a_list()),
}
}
};
}
pub(crate) fn list_functions() -> FunctionMap { pub(crate) fn list_functions() -> FunctionMap {
let mut list_functions: FunctionMap = HashMap::new(); let mut list_functions: FunctionMap = HashMap::new();
let functions = &mut list_functions; let functions = &mut list_functions;
@ -26,7 +37,7 @@ pub(crate) fn list_functions() -> FunctionMap {
Signature::new( Signature::new(
vec![], vec![],
U64, U64,
mut_list_fn!(mut self_val, mut _args => Ok(u64(self_val.len() as u64))), list_fn!(self_val, _args => Ok(u64(self_val.len() as u64))),
), ),
); );
add( add(

View file

@ -103,7 +103,7 @@ fn string_replace_all(receiver: Value, args: Vec<Value>) -> Result<Value, Runtim
Regex::new(s).map_err(|_| RuntimeError::IllegalArgumentException("Invalid regex".into()))? Regex::new(s).map_err(|_| RuntimeError::IllegalArgumentException("Invalid regex".into()))?
} else { } else {
return Err(RuntimeError::IllegalArgumentException( return Err(RuntimeError::IllegalArgumentException(
format!("Illegal pattern. Expected a string, but got {}", &args[0]).into(), format!("Illegal pattern. Expected a string, but got {}", &args[0]),
)); ));
}; };
let replacement = if let Value::String(repl) = &args[1] { let replacement = if let Value::String(repl) = &args[1] {
@ -114,7 +114,6 @@ fn string_replace_all(receiver: Value, args: Vec<Value>) -> Result<Value, Runtim
"Illegal replacement. Expected a string but got {}", "Illegal replacement. Expected a string but got {}",
&args[1] &args[1]
) )
.into(),
)); ));
}; };
match receiver { match receiver {

View file

@ -1,172 +0,0 @@
use crate::compiler::ast_pass::Parameter;
use crate::compiler::tokens::TokenType;
use crate::value::Value;
use crate::vm::{
OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_F32,
OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_MAP, OP_DEF_STRING, OP_DEFINE,
OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY,
OP_NEGATE, OP_NOT, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,OP_ASSIGN,OP_GOTO_IF
};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Chunk {
pub(crate) name: String,
pub code: Vec<u16>,
pub constants: Vec<Value>,
lines: Vec<usize>,
pub(crate) object_defs: HashMap<String, Vec<Parameter>>,
pub(crate) function_parameters: Vec<Parameter>,
pub vars: Vec<(TokenType, String)>,
}
impl Default for Chunk {
fn default() -> Self {
Chunk::new("")
}
}
impl Chunk {
pub(crate) fn find_constant(&self, p0: &String) -> Option<usize> {
for (i, constant) in self.constants.iter().enumerate() {
if let Value::String(s) = constant
&& s == p0
{
return Some(i);
}
}
None
}
}
impl Chunk {
pub(crate) fn new(name: &str) -> Chunk {
Chunk {
name: name.to_string(),
code: Vec::new(),
constants: vec![],
lines: vec![],
object_defs: HashMap::new(),
function_parameters: vec![],
vars: vec![],
}
}
pub(crate) fn add(&mut self, byte: u16, line: usize) {
self.code.push(byte);
self.lines.push(line);
}
pub(crate) fn add_constant(&mut self, value: impl Into<Value>) -> usize {
self.constants.push(value.into());
self.constants.len() - 1
}
pub(crate) fn add_var(&mut self, var_type: &TokenType, name: &str) -> usize {
self.vars.push((var_type.clone(), name.to_string()));
self.vars.len() - 1
}
pub(crate) fn add_object_def(&mut self, name: &str, fields: &[Parameter]) {
self.object_defs.insert(name.to_string(), fields.to_vec());
}
pub fn disassemble(&self) {
println!("== {} ==", self.name);
let mut offset = 0;
while offset < self.code.len() {
offset = self.disassemble_inst(offset);
}
println!();
}
fn disassemble_inst(&self, offset: usize) -> usize {
print!("{:04} ", offset);
if offset > 0 && self.lines[offset] == self.lines[offset - 1] {
print!(" | ");
} else {
print!("{:04} ", self.lines[offset]);
}
let instruction = self.code[offset];
match instruction {
OP_CONSTANT => self.constant_inst("LDC", offset),
OP_ADD => self.simple_inst("ADD", offset),
OP_SUBTRACT => self.simple_inst("SUB", offset),
OP_MULTIPLY => self.simple_inst("MUL", offset),
OP_DIVIDE => self.simple_inst("DIV", offset),
OP_BITAND => self.simple_inst("BITAND", offset),
OP_BITOR => self.simple_inst("BITOR", offset),
OP_BITXOR => self.simple_inst("BITXOR", offset),
OP_NEGATE => self.simple_inst("NEG", offset),
OP_NOT => self.simple_inst("NOT", offset),
OP_RETURN => self.simple_inst("RET", offset),
OP_SHL => self.simple_inst("SHL", offset),
OP_SHR => self.simple_inst("SHR", offset),
OP_LESS => self.simple_inst("LT", offset),
OP_LESS_EQUAL => self.simple_inst("LTE", offset),
OP_GREATER => self.simple_inst("GT", offset),
OP_GREATER_EQUAL => self.simple_inst("GTE", offset),
OP_EQUAL => self.simple_inst("EQ", offset),
OP_PRINT => self.simple_inst("PRT", offset),
OP_POP => self.simple_inst("POP", offset),
OP_DEFINE => self.constant_inst("DEF", offset),
OP_DEF_STRING => self.constant_inst("DEFSTR", offset),
OP_DEF_I32 => self.constant_inst("DEFI32", offset),
OP_DEF_I64 => self.constant_inst("DEFI64", offset),
OP_DEF_F32 => self.constant_inst("DEFF32", offset),
OP_DEF_F64 => self.constant_inst("DEFF64", offset),
OP_DEF_BOOL => self.constant_inst("DEFBOOL", offset),
OP_CALL => self.call_inst("CALL", offset),
OP_GET => self.assign_inst("GET", offset),
OP_DEF_LIST => self.new_inst("DEFLIST", offset),
OP_DEF_MAP => self.new_inst("DEFMAP", offset),
OP_ASSIGN => self.assign_inst("ASSIGN", offset),
OP_GOTO_IF => self.constant_inst("GOTO_IF", offset),
_ => {
println!("Unknown instruction {}", instruction);
offset + 1
}
}
}
fn simple_inst(&self, op: &str, offset: usize) -> usize {
println!("{}", op);
offset + 1
}
fn assign_inst(&self, op: &str, offset: usize) -> usize {
let constant = self.code[offset + 1];
print!("{} {}:", op, constant);
println!("{}",self.vars.get(constant as usize).unwrap().1);
offset + 2
}
fn call_inst(&self, op: &str, offset: usize) -> usize {
let constant = self.code[offset + 1];
let num_args = self.code[offset + 2];
println!(
"{} {}:{}({}):",
op, constant, &self.constants[constant as usize], num_args
);
offset + 3
}
fn new_inst(&self, op: &str, offset: usize) -> usize {
let constant = self.code[offset + 1];
let len = self.code[offset + 2];
print!("{} len: {}:", op, len);
self.print_value(&self.constants[constant as usize]);
offset + 3
}
fn constant_inst(&self, op: &str, offset: usize) -> usize {
let constant = self.code[offset + 1];
print!("{} {}:", op, constant);
self.print_value(&self.constants[constant as usize]);
offset + 2
}
fn print_value(&self, value: &Value) {
println!("{}", value);
}
}

View file

@ -1,15 +1,14 @@
use crate::builtins::lookup;
use crate::compiler::asm_pass::Op::{Add, And, Assign, BitAnd, BitOr, BitXor, Call, CallBuiltin, Constant, DefList, DefMap, Divide, Dup, Equal, Get, Goto, GotoIf, GotoIfNot, Greater, GreaterEqual, Less, LessEqual, ListGet, Multiply, Negate, Not, NotEqual, Or, Pop, Print, Return, Shr, Subtract};
use crate::compiler::ast_pass::Expression::NamedParameter; use crate::compiler::ast_pass::Expression::NamedParameter;
use crate::compiler::ast_pass::{Expression, Function, Parameter, Statement}; use crate::compiler::ast_pass::{Expression, Function, Parameter, Statement};
use crate::builtins::lookup; use crate::compiler::tokens::TokenType;
use crate::chunk::Chunk; use crate::compiler::tokens::TokenType::Unknown;
use crate::errors::CompilerError::{IncompatibleTypes, UndeclaredVariable}; use crate::errors::CompilerError::{IncompatibleTypes, UndeclaredVariable};
use crate::errors::{CompilerError, CompilerErrorAtLine}; use crate::errors::{CompilerError, CompilerErrorAtLine};
use crate::symbol_builder::{Symbol, calculate_type, infer_type}; use crate::symbol_builder::{Symbol, calculate_type, infer_type};
use crate::compiler::tokens::TokenType;
use crate::compiler::tokens::TokenType::Unknown;
use crate::value::Value; use crate::value::Value;
use crate::vm::{OP_ADD, OP_AND, OP_ASSIGN, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CALL_BUILTIN, OP_CONSTANT, OP_DEF_LIST, OP_DEF_MAP, OP_DIVIDE, OP_DUP, OP_EQUAL, OP_GET, OP_GOTO, OP_GOTO_IF, OP_GOTO_NIF, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_LIST_GET, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT}; use crate::{AsmRegistry, SymbolTable};
use crate::{Registry, SymbolTable};
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Deref; use std::ops::Deref;
@ -17,7 +16,7 @@ pub fn compile(
qualified_name: Option<&str>, qualified_name: Option<&str>,
ast: &Vec<Statement>, ast: &Vec<Statement>,
symbols: &SymbolTable, symbols: &SymbolTable,
registry: &mut Registry, registry: &mut AsmRegistry,
) -> Result<(), CompilerErrorAtLine> { ) -> Result<(), CompilerErrorAtLine> {
compile_in_namespace(ast, qualified_name, symbols, registry) compile_in_namespace(ast, qualified_name, symbols, registry)
} }
@ -25,11 +24,11 @@ pub fn compile(
pub fn compile_function( pub fn compile_function(
function: &Function, function: &Function,
symbols: &SymbolTable, symbols: &SymbolTable,
registry: &mut Registry, registry: &mut AsmRegistry,
namespace: &str, namespace: &str,
) -> Result<Chunk, CompilerErrorAtLine> { ) -> Result<AsmChunk, CompilerErrorAtLine> {
let fn_name = &function.name.lexeme; let fn_name = &function.name.lexeme;
let mut compiler = Compiler::new(fn_name); let mut compiler = AsmPass::new(fn_name);
for parm in &function.parameters { for parm in &function.parameters {
let name = parm.name.lexeme.clone(); let name = parm.name.lexeme.clone();
let var_index = compiler.chunk.add_var(&parm.var_type, &parm.name.lexeme); let var_index = compiler.chunk.add_var(&parm.var_type, &parm.name.lexeme);
@ -45,26 +44,81 @@ pub fn compile_in_namespace(
ast: &Vec<Statement>, ast: &Vec<Statement>,
namespace: Option<&str>, namespace: Option<&str>,
symbols: &SymbolTable, symbols: &SymbolTable,
registry: &mut Registry, registry: &mut AsmRegistry,
) -> Result<(), CompilerErrorAtLine> { ) -> Result<(), CompilerErrorAtLine> {
let name = namespace.unwrap_or("main"); let name = namespace.unwrap_or("main");
let mut compiler = Compiler::new(name); let mut compiler = AsmPass::new(name);
let chunk = compiler.compile(ast, symbols, registry, name)?; let chunk = compiler.compile(ast, symbols, registry, name)?;
registry.insert(name.to_string(), chunk); registry.insert(name.to_string(), chunk);
Ok(()) Ok(())
} }
pub struct Compiler { #[derive(Clone)]
chunk: Chunk, pub struct AsmChunk {
pub(crate) name: String,
pub code: Vec<Op>,
pub constants: Vec<Value>,
lines: Vec<usize>,
pub(crate) object_defs: HashMap<String, Vec<Parameter>>,
pub(crate) function_parameters: Vec<Parameter>,
pub vars: Vec<(TokenType, String)>,
}
impl AsmChunk {
pub(crate) fn new(name: &str) -> Self {
Self {
name: name.to_string(),
code: Vec::new(),
constants: vec![],
lines: vec![],
object_defs: HashMap::new(),
function_parameters: vec![],
vars: vec![],
}
}
pub(crate) fn add(&mut self, op: Op, line: usize) {
self.code.push(op);
self.lines.push(line);
}
pub(crate) fn add_constant(&mut self, value: impl Into<Value>) -> usize {
self.constants.push(value.into());
self.constants.len() - 1
}
pub(crate) fn find_constant(&self, p0: &String) -> Option<usize> {
for (i, constant) in self.constants.iter().enumerate() {
if let Value::String(s) = constant
&& s == p0
{
return Some(i);
}
}
None
}
pub(crate) fn add_var(&mut self, var_type: &TokenType, name: &str) -> usize {
self.vars.push((var_type.clone(), name.to_string()));
self.vars.len() - 1
}
pub(crate) fn add_object_def(&mut self, name: &str, fields: &[Parameter]) {
self.object_defs.insert(name.to_string(), fields.to_vec());
}
}
pub struct AsmPass {
chunk: AsmChunk,
_had_error: bool, _had_error: bool,
current_line: usize, current_line: usize,
vars: HashMap<String, usize>, vars: HashMap<String, usize>,
} }
impl Compiler { impl AsmPass {
pub fn new(name: &str) -> Self { pub fn new(name: &str) -> Self {
Self { Self {
chunk: Chunk::new(name), chunk: AsmChunk::new(name),
_had_error: false, _had_error: false,
current_line: 0, current_line: 0,
vars: HashMap::new(), vars: HashMap::new(),
@ -76,11 +130,11 @@ impl Compiler {
&mut self, &mut self,
ast: &Vec<Statement>, ast: &Vec<Statement>,
symbols: &SymbolTable, symbols: &SymbolTable,
registry: &mut Registry, registry: &mut AsmRegistry,
namespace: &str, namespace: &str,
) -> Result<Chunk, CompilerErrorAtLine> { ) -> Result<AsmChunk, CompilerErrorAtLine> {
self.compile_statements(ast, symbols, registry, namespace)?; self.compile_statements(ast, symbols, registry, namespace)?;
self.emit_byte(OP_RETURN); self.emit(Return);
let chunk = self.chunk.clone(); let chunk = self.chunk.clone();
self.chunk.code.clear(); // in case the compiler is reused, clear it for the next compilation. This is for the REPL self.chunk.code.clear(); // in case the compiler is reused, clear it for the next compilation. This is for the REPL
Ok(chunk) Ok(chunk)
@ -91,7 +145,7 @@ impl Compiler {
&mut self, &mut self,
ast: &Vec<Statement>, ast: &Vec<Statement>,
symbols: &SymbolTable, symbols: &SymbolTable,
registry: &mut Registry, registry: &mut AsmRegistry,
namespace: &str, namespace: &str,
) -> Result<(), CompilerErrorAtLine> { ) -> Result<(), CompilerErrorAtLine> {
for statement in ast { for statement in ast {
@ -105,7 +159,7 @@ impl Compiler {
&mut self, &mut self,
statement: &Statement, statement: &Statement,
symbols: &SymbolTable, symbols: &SymbolTable,
registry: &mut Registry, registry: &mut AsmRegistry,
namespace: &str, namespace: &str,
) -> Result<(), CompilerErrorAtLine> { ) -> Result<(), CompilerErrorAtLine> {
self.current_line = statement.line(); self.current_line = statement.line();
@ -127,7 +181,7 @@ impl Compiler {
let name_index = self.chunk.add_var(var_type, name); let name_index = self.chunk.add_var(var_type, name);
self.vars.insert(name.to_string(), name_index); self.vars.insert(name.to_string(), name_index);
self.compile_expression(namespace, initializer, symbols, registry)?; self.compile_expression(namespace, initializer, symbols, registry)?;
self.emit_bytes(OP_ASSIGN, name_index as u16); self.emit(Assign(name_index));
} else { } else {
return Err(self.raise(UndeclaredVariable(name.to_string()))); return Err(self.raise(UndeclaredVariable(name.to_string())));
} }
@ -135,7 +189,7 @@ impl Compiler {
// replace with function // replace with function
Statement::PrintStmt { value } => { Statement::PrintStmt { value } => {
self.compile_expression(namespace, value, symbols, registry)?; self.compile_expression(namespace, value, symbols, registry)?;
self.emit_byte(OP_PRINT); self.emit(Print);
} }
Statement::ExpressionStmt { expression } => { Statement::ExpressionStmt { expression } => {
self.compile_expression(namespace, expression, symbols, registry)?; self.compile_expression(namespace, expression, symbols, registry)?;
@ -161,30 +215,23 @@ impl Compiler {
} => { } => {
self.compile_expression(namespace, condition, symbols, registry)?; self.compile_expression(namespace, condition, symbols, registry)?;
self.emit_byte(OP_DUP); self.emit(Dup);
self.emit_bytes(OP_GOTO_NIF, 0); self.emit(GotoIfNot(0)); // placeholder
let goto_addr1 = self.chunk.code.len() - 1; let goto_addr1 = self.chunk.code.len() - 1;
self.emit_byte(OP_POP); self.emit(Pop);
self.compile_statements(then_branch, symbols, registry, namespace)?; self.compile_statements(then_branch, symbols, registry, namespace)?;
self.emit_bytes(OP_GOTO, 0); self.emit(Goto(0));
let goto_addr2 = self.chunk.code.len()-1; let goto_addr2 = self.chunk.code.len() - 1;// placeholder
self.chunk.code[goto_addr1] = GotoIfNot(self.chunk.code.len());
if else_branch.is_some() { if else_branch.is_some() {
self.chunk.code[goto_addr1] = self.chunk.code.len() as u16;
self.emit_bytes(OP_GOTO_IF, 0);
let goto_addr3 = self.chunk.code.len() - 1;
self.compile_statements( self.compile_statements(
else_branch.as_ref().unwrap(), else_branch.as_ref().unwrap(),
symbols, symbols,
registry, registry,
namespace, namespace,
)?; )?;
self.chunk.code[goto_addr2] = self.chunk.code.len() as u16; // fill in the placeholder
self.chunk.code[goto_addr3] = self.chunk.code.len() as u16; // fill in the placeholder
} else {
self.chunk.code[goto_addr1] = self.chunk.code.len() as u16;
} }
self.chunk.code[goto_addr2]= Op::Goto(self.chunk.code.len());
} }
Statement::ForStatement { Statement::ForStatement {
loop_var, loop_var,
@ -204,19 +251,19 @@ impl Compiler {
self.vars.insert(name.to_string(), loop_var_name_index); self.vars.insert(name.to_string(), loop_var_name_index);
// 3. start index // 3. start index
self.emit_bytes(OP_CONSTANT, start_index as u16); self.emit(Constant(start_index));
self.emit_bytes(OP_ASSIGN, loop_var_name_index as u16); self.emit(Assign(loop_var_name_index));
let return_addr = self.chunk.code.len(); let return_addr = self.chunk.code.len();
self.compile_statements(body, symbols, registry, namespace)?; self.compile_statements(body, symbols, registry, namespace)?;
self.emit_bytes(OP_GET, loop_var_name_index as u16); self.emit(Get(loop_var_name_index));
self.emit_bytes(OP_CONSTANT, step_const_index); self.emit(Constant(step_const_index));
self.emit_byte(OP_ADD); self.emit(Add);
self.emit_bytes(OP_ASSIGN, loop_var_name_index as u16); self.emit(Assign(loop_var_name_index));
self.emit_bytes(OP_CONSTANT, end_index as u16); self.emit(Constant(end_index));
self.emit_bytes(OP_GET, loop_var_name_index as u16); self.emit(Get(loop_var_name_index));
self.emit_byte(OP_GREATER_EQUAL); self.emit(GreaterEqual);
self.emit_bytes(OP_GOTO_IF, return_addr as u16); self.emit(GotoIf(return_addr));
} }
} }
Ok(()) Ok(())
@ -227,7 +274,7 @@ impl Compiler {
namespace: &str, namespace: &str,
expression: &Expression, expression: &Expression,
symbols: &SymbolTable, symbols: &SymbolTable,
registry: &mut Registry, registry: &mut AsmRegistry,
) -> Result<(), CompilerErrorAtLine> { ) -> Result<(), CompilerErrorAtLine> {
match expression { match expression {
Expression::FunctionCall { Expression::FunctionCall {
@ -244,16 +291,14 @@ impl Compiler {
namespace, symbols, registry, arguments, parameters, namespace, symbols, registry, arguments, parameters,
)?; )?;
self.emit_bytes(OP_CALL, name_index as u16); self.emit(Call(name_index,arguments.len()));
self.emit_byte(arguments.len() as u16);
} }
// constructor function // constructor function
Some(Symbol::Object { fields, .. }) => { Some(Symbol::Object { fields, .. }) => {
self.get_arguments_in_order( self.get_arguments_in_order(
namespace, symbols, registry, arguments, fields, namespace, symbols, registry, arguments, fields,
)?; )?;
self.emit_bytes(OP_CALL, name_index as u16); self.emit(Call(name_index,arguments.len()));
self.emit_byte(arguments.len() as u16);
} }
_ => { _ => {
return Err(self.raise(CompilerError::FunctionNotFound(name.to_string()))); return Err(self.raise(CompilerError::FunctionNotFound(name.to_string())));
@ -293,15 +338,16 @@ impl Compiler {
arguments, arguments,
&signature.parameters, &signature.parameters,
)?; )?;
self.emit_byte(OP_CALL_BUILTIN); self.emit(CallBuiltin(
self.emit_byte(name_index as u16); name_index,
self.emit_byte(type_index as u16); type_index,
self.emit_byte(arguments.len() as u16); arguments.len(),
));
} }
Expression::Variable { name, .. } => { Expression::Variable { name, .. } => {
let name_index = self.vars.get(name); let name_index = self.vars.get(name);
if let Some(name_index) = name_index { if let Some(name_index) = name_index {
self.emit_bytes(OP_GET, *name_index as u16); self.emit(Get(*name_index));
} else { } else {
return Err(self.raise(UndeclaredVariable(name.to_string()))); return Err(self.raise(UndeclaredVariable(name.to_string())));
} }
@ -314,7 +360,7 @@ impl Compiler {
self.compile_expression(namespace, value, symbols, registry)?; self.compile_expression(namespace, value, symbols, registry)?;
let name_index = self.vars.get(variable_name); let name_index = self.vars.get(variable_name);
if let Some(name_index) = name_index { if let Some(name_index) = name_index {
self.emit_bytes(OP_ASSIGN, *name_index as u16); self.emit(Assign(*name_index));
} else { } else {
return Err(self.raise(UndeclaredVariable(variable_name.to_string()))); return Err(self.raise(UndeclaredVariable(variable_name.to_string())));
} }
@ -326,14 +372,14 @@ impl Compiler {
for expr in values { for expr in values {
self.compile_expression(namespace, expr, symbols, registry)?; self.compile_expression(namespace, expr, symbols, registry)?;
} }
self.emit_bytes(OP_DEF_LIST, values.len() as u16); self.emit(DefList(values.len()));
} }
Expression::Map { entries, .. } => { Expression::Map { entries, .. } => {
for (key, value) in entries { for (key, value) in entries {
self.compile_expression(namespace, key, symbols, registry)?; self.compile_expression(namespace, key, symbols, registry)?;
self.compile_expression(namespace, value, symbols, registry)?; self.compile_expression(namespace, value, symbols, registry)?;
} }
self.emit_bytes(OP_DEF_MAP, entries.len() as u16); self.emit(DefMap(entries.len()));
} }
Expression::Grouping { expression, .. } => { Expression::Grouping { expression, .. } => {
self.compile_expression(namespace, expression, symbols, registry)? self.compile_expression(namespace, expression, symbols, registry)?
@ -344,10 +390,10 @@ impl Compiler {
self.compile_expression(namespace, right, symbols, registry)?; self.compile_expression(namespace, right, symbols, registry)?;
match operator.token_type { match operator.token_type {
TokenType::Minus => { TokenType::Minus => {
self.emit_byte(OP_NEGATE); self.emit(Negate);
} }
TokenType::Bang => { TokenType::Bang => {
self.emit_byte(OP_NOT); self.emit(Not);
} }
_ => unimplemented!("unary other than ! and -"), _ => unimplemented!("unary other than ! and -"),
} }
@ -361,31 +407,32 @@ impl Compiler {
self.compile_expression(namespace, left, symbols, registry)?; self.compile_expression(namespace, left, symbols, registry)?;
self.compile_expression(namespace, right, symbols, registry)?; self.compile_expression(namespace, right, symbols, registry)?;
match operator.token_type { match operator.token_type {
TokenType::BitAnd => self.emit_byte(OP_BITAND), TokenType::BitAnd => self.emit(BitAnd),
TokenType::BitXor => self.emit_byte(OP_BITXOR), TokenType::BitXor => self.emit(BitXor),
TokenType::Equal => { TokenType::Equal => {
if let Expression::Variable { name, .. } = left.deref() { if let Expression::Variable { name, .. } = left.deref() {
let index = self.vars.get(name).unwrap(); let index = self.vars.get(name).unwrap();
self.emit_bytes(OP_ASSIGN, *index as u16); self.emit(Assign(*index));
self.emit_byte(OP_POP); self.emit(Pop);
} else { } else {
return Err(self.raise(UndeclaredVariable("".to_string()))); return Err(self.raise(UndeclaredVariable("".to_string())));
} }
} }
TokenType::EqualEqual => self.emit_byte(OP_EQUAL), TokenType::EqualEqual => self.emit(Equal),
TokenType::Greater => self.emit_byte(OP_GREATER), TokenType::BangEqual => self.emit(NotEqual),
TokenType::GreaterEqual => self.emit_byte(OP_GREATER_EQUAL), TokenType::Greater => self.emit(Greater),
TokenType::GreaterGreater => self.emit_byte(OP_SHR), TokenType::GreaterEqual => self.emit(GreaterEqual),
TokenType::Less => self.emit_byte(OP_LESS), TokenType::GreaterGreater => self.emit(Shr),
TokenType::LessEqual => self.emit_byte(OP_LESS_EQUAL), TokenType::Less => self.emit(Less),
TokenType::LessLess => self.emit_byte(OP_SHL), TokenType::LessEqual => self.emit(LessEqual),
TokenType::LogicalAnd => self.emit_byte(OP_AND), TokenType::LessLess => self.emit(Op::Shl),
TokenType::LogicalOr => self.emit_byte(OP_OR), TokenType::LogicalAnd => self.emit(And),
TokenType::Minus => self.emit_byte(OP_SUBTRACT), TokenType::LogicalOr => self.emit(Or),
TokenType::Pipe => self.emit_byte(OP_BITOR), TokenType::Minus => self.emit(Subtract),
TokenType::Plus => self.emit_byte(OP_ADD), TokenType::Pipe => self.emit(BitOr),
TokenType::Slash => self.emit_byte(OP_DIVIDE), TokenType::Plus => self.emit(Add),
TokenType::Star => self.emit_byte(OP_MULTIPLY), TokenType::Slash => self.emit(Divide),
TokenType::Star => self.emit(Multiply),
_ => unimplemented!("binary other than plus, minus, star, slash"), _ => unimplemented!("binary other than plus, minus, star, slash"),
} }
} }
@ -396,7 +443,7 @@ impl Compiler {
Expression::ListGet { index, list } => { Expression::ListGet { index, list } => {
self.compile_expression(namespace, list, symbols, registry)?; self.compile_expression(namespace, list, symbols, registry)?;
self.compile_expression(namespace, index, symbols, registry)?; self.compile_expression(namespace, index, symbols, registry)?;
self.emit_byte(OP_LIST_GET); self.emit(ListGet);
} }
Expression::MapGet { .. } => {} Expression::MapGet { .. } => {}
Expression::FieldGet { .. } => {} Expression::FieldGet { .. } => {}
@ -415,7 +462,7 @@ impl Compiler {
&mut self, &mut self,
namespace: &str, namespace: &str,
symbols: &SymbolTable, symbols: &SymbolTable,
registry: &mut Registry, registry: &mut AsmRegistry,
arguments: &[Expression], arguments: &[Expression],
parameters: &[Parameter], parameters: &[Parameter],
) -> Result<(), CompilerErrorAtLine> { ) -> Result<(), CompilerErrorAtLine> {
@ -441,18 +488,13 @@ impl Compiler {
Ok(()) Ok(())
} }
fn emit_byte(&mut self, byte: u16) { fn emit(&mut self, op: Op) {
self.chunk.add(byte, self.current_line); self.chunk.add(op, self.current_line);
} }
fn emit_bytes(&mut self, b1: u16, b2: u16) { fn emit_constant(&mut self, value: Value) -> usize {
self.emit_byte(b1); let index = self.chunk.add_constant(value);
self.emit_byte(b2); self.emit(Constant(index));
}
fn emit_constant(&mut self, value: Value) -> u16 {
let index = self.chunk.add_constant(value) as u16;
self.emit_bytes(OP_CONSTANT, index);
index index
} }
@ -460,3 +502,41 @@ impl Compiler {
CompilerErrorAtLine::raise(error, self.current_line) CompilerErrorAtLine::raise(error, self.current_line)
} }
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Op {
Constant(usize),
Add,
Subtract,
Multiply,
Divide,
Negate,
Print,
Return,
Call(usize,usize),
And,
Or,
Not,
Equal,
Greater,
Less,
NotEqual,
GreaterEqual,
LessEqual,
BitAnd,
BitOr,
BitXor,
Shr,
Shl,
Pop,
Get(usize),
DefList(usize),
DefMap(usize),
Assign(usize),
ListGet,
CallBuiltin(usize, usize, usize),
Dup,
GotoIf(usize),
GotoIfNot(usize),
Goto(usize),
}

View file

@ -5,8 +5,8 @@ mod tests {
use crate::errors::CrudLangError::{Compiler, Runtime}; use crate::errors::CrudLangError::{Compiler, Runtime};
use crate::errors::RuntimeError::{IllegalArgumentException, IndexOutOfBounds}; use crate::errors::RuntimeError::{IllegalArgumentException, IndexOutOfBounds};
use crate::value::{Value, string}; use crate::value::{Value, string};
use crate::{compile, run};
use chrono::DateTime; use chrono::DateTime;
use crate::compiler::{compile, run};
#[test] #[test]
fn literal_int() { fn literal_int() {

View file

@ -1,5 +1,68 @@
use std::collections::HashMap;
use std::fs;
use walkdir::WalkDir;
use crate::{compiler, symbol_builder, AsmRegistry};
use crate::compiler::asm_pass::AsmChunk;
use crate::errors::CrudLangError;
use crate::errors::CrudLangError::Platform;
mod compiler_tests; mod compiler_tests;
pub mod bytecode_pass;
pub mod scan_pass; pub mod scan_pass;
pub mod ast_pass; pub mod ast_pass;
pub mod tokens; pub mod tokens;
pub mod asm_pass;
pub fn compile_sourcedir(source_dir: &str) -> Result<HashMap<String, AsmChunk>, CrudLangError> {
let mut asm_registry = AsmRegistry::new();
for entry in WalkDir::new(source_dir).into_iter().filter_map(|e| e.ok()) {
let path = entry.path().to_str().unwrap();
if path.ends_with(".crud") {
print!("-- Compiling {} -- ", path);
let source = fs::read_to_string(path).map_err(map_underlying())?;
let tokens = scan_pass::scan(&source)?;
let mut symbol_table = HashMap::new();
match ast_pass::compile(Some(path), tokens, &mut symbol_table) {
Ok(statements) => {
let path = path.strip_prefix(source_dir).unwrap().replace(".crud", "");
symbol_builder::build(&path, &statements, &mut symbol_table);
asm_pass::compile(Some(&path), &statements, &symbol_table, &mut asm_registry)?;
}
Err(e) => {
println!("{}", e);
break;
}
}
}
}
Ok(asm_registry)
}
pub fn map_underlying() -> fn(std::io::Error) -> CrudLangError {
|e| Platform(e.to_string())
}
pub fn compile(src: &str) -> Result<HashMap<String, AsmChunk>, CrudLangError> {
let tokens = compiler::scan_pass::scan(src)?;
let mut asm_registry = HashMap::new();
let mut symbol_table = HashMap::new();
let ast = compiler::ast_pass::compile(None, tokens, &mut symbol_table)?;
symbol_builder::build("", &ast, &mut symbol_table);
asm_pass::compile(None, &ast, &symbol_table, &mut asm_registry)?;
Ok(asm_registry)
}
#[cfg(test)]
pub(crate) fn run(src: &str) -> Result<crate::value::Value, CrudLangError> {
let tokens = compiler::scan_pass::scan(src)?;
let mut symbol_table = HashMap::new();
let ast = compiler::ast_pass::compile(None, tokens, &mut symbol_table)?;
symbol_builder::build("", &ast, &mut symbol_table);
let mut asm_registry = HashMap::new();
asm_pass::compile(None, &ast, &symbol_table, &mut asm_registry)?;
let registry = arc_swap::ArcSwap::from(std::sync::Arc::new(asm_registry));
crate::vm::interpret(registry.load(), "main").map_err(CrudLangError::from)
}

View file

@ -1,5 +1,3 @@
use crate::chunk::Chunk;
use crate::compile_sourcedir;
use notify::{RecursiveMode, Watcher}; use notify::{RecursiveMode, Watcher};
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
@ -9,10 +7,12 @@ use std::thread;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use arc_swap::ArcSwap; use arc_swap::ArcSwap;
use log::info; use log::info;
use crate::compiler::asm_pass::AsmChunk;
use crate::compiler::compile_sourcedir;
const ONE_SEC: Duration = Duration::from_secs(1); const ONE_SEC: Duration = Duration::from_secs(1);
pub fn start_watch_daemon(source: &str, registry: Arc<ArcSwap<HashMap<String, Chunk>>>) { pub fn start_watch_daemon(source: &str, registry: Arc<ArcSwap<HashMap<String, AsmChunk>>>) {
let source = source.to_string(); let source = source.to_string();
let s = source.to_string(); let s = source.to_string();
let (tx, rx) = channel(); let (tx, rx) = channel();

View file

@ -1,15 +1,11 @@
use crate::chunk::Chunk; use crate::compiler::asm_pass::AsmChunk;
use crate::compiler::ast_pass::{Expression, Statement}; use crate::compiler::ast_pass::{Expression, Statement};
use crate::errors::CrudLangError::Platform; use crate::errors::CompilerErrorAtLine;
use crate::errors::{CompilerErrorAtLine, CrudLangError};
use crate::symbol_builder::Symbol; use crate::symbol_builder::Symbol;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs;
use walkdir::WalkDir;
mod builtins; mod builtins;
pub mod chunk; pub mod compiler;
pub(crate) mod compiler;
pub mod errors; pub mod errors;
pub mod file_watch; pub mod file_watch;
mod keywords; mod keywords;
@ -21,68 +17,4 @@ pub mod vm;
pub(crate) type SymbolTable = HashMap<String, Symbol>; pub(crate) type SymbolTable = HashMap<String, Symbol>;
pub(crate) type Expr = Result<Expression, CompilerErrorAtLine>; pub(crate) type Expr = Result<Expression, CompilerErrorAtLine>;
pub(crate) type Stmt = Result<Statement, CompilerErrorAtLine>; pub(crate) type Stmt = Result<Statement, CompilerErrorAtLine>;
pub(crate) type Registry = HashMap<String, Chunk>; pub(crate) type AsmRegistry = HashMap<String, AsmChunk>;
pub fn compile_sourcedir(source_dir: &str) -> Result<HashMap<String, Chunk>, CrudLangError> {
let mut registry = HashMap::new();
for entry in WalkDir::new(source_dir).into_iter().filter_map(|e| e.ok()) {
let path = entry.path().to_str().unwrap();
if path.ends_with(".crud") {
print!("-- Compiling {} -- ", path);
let source = fs::read_to_string(path).map_err(map_underlying())?;
let tokens = compiler::scan_pass::scan(&source)?;
let mut symbol_table = HashMap::new();
match compiler::ast_pass::compile(Some(path), tokens, &mut symbol_table) {
Ok(statements) => {
let path = path.strip_prefix(source_dir).unwrap().replace(".crud", "");
symbol_builder::build(&path, &statements, &mut symbol_table);
compiler::bytecode_pass::compile(Some(&path), &statements, &symbol_table, &mut registry)?;
}
Err(e) => {
println!("{}", e);
break;
}
}
}
}
println!();
Ok(registry)
}
pub fn map_underlying() -> fn(std::io::Error) -> CrudLangError {
|e| Platform(e.to_string())
}
pub fn recompile(src: &str, registry: &mut HashMap<String, Chunk>) -> Result<(), CrudLangError> {
let tokens = compiler::scan_pass::scan(src)?;
let mut symbol_table = HashMap::new();
let ast = compiler::ast_pass::compile(None, tokens, &mut symbol_table)?;
symbol_builder::build("", &ast, &mut symbol_table);
compiler::bytecode_pass::compile(None, &ast, &symbol_table, registry)?;
Ok(())
}
pub fn compile(src: &str) -> Result<HashMap<String, Chunk>, CrudLangError> {
let tokens = compiler::scan_pass::scan(src)?;
let mut registry = HashMap::new();
let mut symbol_table = HashMap::new();
let ast = compiler::ast_pass::compile(None, tokens, &mut symbol_table)?;
symbol_builder::build("", &ast, &mut symbol_table);
compiler::bytecode_pass::compile(None, &ast, &symbol_table, &mut registry)?;
Ok(registry)
}
#[cfg(test)]
pub(crate) fn run(src: &str) -> Result<value::Value, CrudLangError> {
let tokens = compiler::scan_pass::scan(src)?;
let mut symbol_table = HashMap::new();
let ast = compiler::ast_pass::compile(None, tokens, &mut symbol_table)?;
symbol_builder::build("", &ast, &mut symbol_table);
let mut registry = HashMap::new();
compiler::bytecode_pass::compile(None, &ast, &symbol_table, &mut registry)?;
let registry = arc_swap::ArcSwap::from(std::sync::Arc::new(registry));
vm::interpret(registry.load(), "main").map_err(CrudLangError::from)
}

View file

@ -3,14 +3,14 @@ use axum::http::StatusCode;
use axum::routing::any; use axum::routing::any;
use axum::{Json, Router}; use axum::{Json, Router};
use clap::Parser; use clap::Parser;
use tipi_lang::chunk::Chunk;
use tipi_lang::errors::CrudLangError; use tipi_lang::errors::CrudLangError;
use tipi_lang::vm::interpret_async; use tipi_lang::vm::interpret_async;
use tipi_lang::{compile_sourcedir, map_underlying};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use arc_swap::ArcSwap; use arc_swap::ArcSwap;
use log::info; use log::info;
use tipi_lang::compiler::asm_pass::AsmChunk;
use tipi_lang::compiler::{compile_sourcedir, map_underlying};
/// A simple CLI tool to greet users /// A simple CLI tool to greet users
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -72,7 +72,7 @@ async fn main() -> Result<(), CrudLangError> {
#[derive(Clone)] #[derive(Clone)]
struct AppState { struct AppState {
registry: Arc<ArcSwap<HashMap<String, Chunk>>>, registry: Arc<ArcSwap<HashMap<String, AsmChunk>>>,
} }
async fn handle_any( async fn handle_any(

View file

@ -1,8 +1,9 @@
use crate::chunk::Chunk; use crate::compiler::asm_pass::AsmChunk;
use crate::errors::CrudLangError;
use crate::compiler::scan_pass::scan; use crate::compiler::scan_pass::scan;
use crate::compiler::{asm_pass, ast_pass, map_underlying};
use crate::errors::CrudLangError;
use crate::symbol_builder;
use crate::vm::Vm; use crate::vm::Vm;
use crate::{compiler::ast_pass, compiler::bytecode_pass, map_underlying, symbol_builder};
use arc_swap::ArcSwap; use arc_swap::ArcSwap;
use std::collections::HashMap; use std::collections::HashMap;
use std::io; use std::io;
@ -10,12 +11,12 @@ use std::io::Write;
use std::ops::Deref; use std::ops::Deref;
use std::sync::Arc; use std::sync::Arc;
pub fn start(registry: Arc<ArcSwap<HashMap<String, Chunk>>>) -> Result<(), CrudLangError> { pub fn start(registry: Arc<ArcSwap<HashMap<String, AsmChunk>>>) -> Result<(), CrudLangError> {
println!("REPL started -- Type ctrl-c to exit (both the repl and the server)"); println!("REPL started -- Type ctrl-c to exit (both the repl and the server)");
println!(":h for help"); println!(":h for help");
let mut symbol_table = HashMap::new(); let mut symbol_table = HashMap::new();
let mut vm = Vm::new(&registry.load()); let mut vm = Vm::new(&registry.load());
let mut bytecode_compiler = bytecode_pass::Compiler::new("main"); let mut asm_pass = asm_pass::AsmPass::new("main");
loop { loop {
print!(">"); print!(">");
io::stdout().flush().map_err(map_underlying())?; io::stdout().flush().map_err(map_underlying())?;
@ -43,7 +44,7 @@ pub fn start(registry: Arc<ArcSwap<HashMap<String, Chunk>>>) -> Result<(), CrudL
}; };
symbol_builder::build("", &ast, &mut symbol_table); symbol_builder::build("", &ast, &mut symbol_table);
match bytecode_compiler.compile(&ast, &symbol_table, &mut registry_copy, "") { match asm_pass.compile(&ast, &symbol_table, &mut registry_copy, "") {
Ok(chunk) => { Ok(chunk) => {
registry_copy.insert("main".to_string(), chunk); registry_copy.insert("main".to_string(), chunk);
registry.store(Arc::new(registry_copy)); registry.store(Arc::new(registry_copy));
@ -67,7 +68,7 @@ pub fn start(registry: Arc<ArcSwap<HashMap<String, Chunk>>>) -> Result<(), CrudL
} }
} }
fn list_endpoints(registry: Arc<HashMap<String, Chunk>>) { fn list_endpoints(registry: Arc<HashMap<String, AsmChunk>>) {
registry registry
.iter() .iter()
.filter(|(k, _)| k.contains("get")) .filter(|(k, _)| k.contains("get"))
@ -76,7 +77,7 @@ fn list_endpoints(registry: Arc<HashMap<String, Chunk>>) {
}); });
} }
fn list_functions(registry: Arc<HashMap<String, Chunk>>) { fn list_functions(registry: Arc<HashMap<String, AsmChunk>>) {
registry.iter().for_each(|(k, _)| { registry.iter().for_each(|(k, _)| {
println!("{}", k); //number println!("{}", k); //number
}); });

View file

@ -36,7 +36,7 @@ pub(crate) fn string(v: impl Into<String>) -> Value {
Value::String(v.into()) Value::String(v.into())
} }
pub(crate) fn i64(v: impl Into<i64>) -> Value { pub(crate) fn _i64(v: impl Into<i64>) -> Value {
Value::I64(v.into()) Value::I64(v.into())
} }

262
src/vm.rs
View file

@ -1,35 +1,15 @@
use crate::Registry; use crate::compiler::asm_pass::{AsmChunk, Op};
use crate::chunk::Chunk;
use crate::errors::RuntimeError::Something;
use crate::errors::{RuntimeError, ValueError}; use crate::errors::{RuntimeError, ValueError};
use crate::compiler::tokens::TokenType;
use crate::value::{Object, Value}; use crate::value::{Object, Value};
use crate::{AsmRegistry};
use arc_swap::Guard; use arc_swap::Guard;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tracing::debug; use tracing::debug;
use crate::compiler::tokens::TokenType;
pub struct Vm {
ip: usize,
stack: Vec<Value>,
local_vars: HashMap<String, Value>,
error_occurred: bool,
pub(crate) registry: Arc<HashMap<String, Chunk>>,
}
pub fn interpret(
registry: Guard<Arc<HashMap<String, Chunk>>>,
function: &str,
) -> Result<Value, RuntimeError> {
let chunk = registry.get(function).unwrap().clone();
// chunk.disassemble();
let mut vm = Vm::new(&registry);
vm.run(&get_context(function), &chunk)
// Ok(Value::Void)
}
pub async fn interpret_async( pub async fn interpret_async(
registry: Guard<Arc<HashMap<String, Chunk>>>, registry: Guard<Arc<HashMap<String, AsmChunk>>>,
function: &str, function: &str,
uri: &str, uri: &str,
query_params: HashMap<String, String>, query_params: HashMap<String, String>,
@ -50,20 +30,27 @@ pub async fn interpret_async(
} }
} }
fn value_map(strings: HashMap<String, String>) -> HashMap<Value, Value> { pub fn interpret(registry: Guard<Arc<AsmRegistry>>, function: &str) -> Result<Value, RuntimeError> {
strings let chunk = registry.get(function).unwrap().clone();
.into_iter() let mut vm = Vm::new(&registry);
.map(|(k, v)| (Value::String(k.to_string()), Value::String(v.to_string()))) vm.run(&get_context(function), &chunk)
.collect()
} }
pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> Result<Value, RuntimeError> { pub fn interpret_function(chunk: &AsmChunk, args: Vec<Value>) -> Result<Value, RuntimeError> {
let mut vm = Vm::new(&Arc::new(HashMap::new())); let mut vm = Vm::new(&Arc::new(HashMap::new()));
vm.run_function(chunk, args) vm.run_function(chunk, args)
} }
pub(crate) struct Vm {
ip: usize,
stack: Vec<Value>,
local_vars: HashMap<String, Value>,
error_occurred: bool,
pub(crate) registry: Arc<AsmRegistry>,
}
impl Vm { impl Vm {
pub(crate) fn new(registry: &Arc<Registry>) -> Self { pub(crate) fn new(registry: &Arc<AsmRegistry>) -> Self {
Self { Self {
ip: 0, ip: 0,
stack: vec![], stack: vec![],
@ -73,7 +60,7 @@ impl Vm {
} }
} }
fn run_function(&mut self, chunk: &Chunk, mut args: Vec<Value>) -> Result<Value, RuntimeError> { fn run_function(&mut self, chunk: &AsmChunk, mut args: Vec<Value>) -> Result<Value, RuntimeError> {
// arguments -> locals // arguments -> locals
for (_, name) in chunk.vars.iter() { for (_, name) in chunk.vars.iter() {
self.local_vars.insert(name.clone(), args.remove(0)); self.local_vars.insert(name.clone(), args.remove(0));
@ -81,117 +68,101 @@ impl Vm {
self.run("", chunk) self.run("", chunk)
} }
pub(crate) fn run(&mut self, context: &str, chunk: &Chunk) -> Result<Value, RuntimeError> { pub(crate) fn run(&mut self, context: &str, chunk: &AsmChunk) -> Result<Value, RuntimeError> {
self.ip = 0; self.ip = 0;
loop { loop {
if self.error_occurred { let opcode = &chunk.code[self.ip];
return Err(Something);
}
debug!("{:?}", self.stack);
let opcode = chunk.code[self.ip];
self.ip += 1; self.ip += 1;
match opcode { match opcode {
OP_CONSTANT => { Op::Constant(c) => {
let value = &chunk.constants[self.read(chunk)]; let value = &chunk.constants[*c];
self.push(value.clone()); self.push(value.clone());
} }
OP_ADD => binary_op(self, |a, b| a + b), Op::Add => binary_op(self, |a, b| a + b),
OP_SUBTRACT => binary_op(self, |a, b| a - b), Op::Subtract => binary_op(self, |a, b| a - b),
OP_MULTIPLY => binary_op(self, |a, b| a * b), Op::Multiply => binary_op(self, |a, b| a * b),
OP_DIVIDE => binary_op(self, |a, b| a / b), Op::Divide => binary_op(self, |a, b| a / b),
OP_AND => binary_op(self, |a, b| { Op::And => binary_op(self, |a, b| {
if let (Value::Bool(a), Value::Bool(b)) = (a, b) { if let (Value::Bool(a), Value::Bool(b)) = (a, b) {
Ok(Value::Bool(*a && *b)) Ok(Value::Bool(*a && *b))
} else { } else {
Err(ValueError::Some("Cannot and")) Err(ValueError::Some("Cannot and"))
} }
}), }),
OP_OR => binary_op(self, |a, b| { Op::Or => binary_op(self, |a, b| {
if let (Value::Bool(a), Value::Bool(b)) = (a, b) { if let (Value::Bool(a), Value::Bool(b)) = (a, b) {
Ok(Value::Bool(*a || *b)) Ok(Value::Bool(*a || *b))
} else { } else {
Err(ValueError::Some("Cannot compare")) Err(ValueError::Some("Cannot compare"))
} }
}), }),
OP_NOT => unary_op(self, |a| !a), Op::Not => unary_op(self, |a| !a),
OP_BITAND => binary_op(self, |a, b| a & b), Op::BitAnd => binary_op(self, |a, b| a & b),
OP_BITOR => binary_op(self, |a, b| a | b), Op::BitOr => binary_op(self, |a, b| a | b),
OP_BITXOR => binary_op(self, |a, b| a ^ b), Op::BitXor => binary_op(self, |a, b| a ^ b),
OP_NEGATE => unary_op(self, |a| -a), Op::Negate => unary_op(self, |a| -a),
OP_RETURN => { Op::Return => {
debug!("return {:?}", self.stack);
return if self.stack.is_empty() { return if self.stack.is_empty() {
Ok(Value::Void) Ok(Value::Void)
} else { } else {
Ok(self.pop()) Ok(self.pop())
}; };
} }
OP_SHL => binary_op(self, |a, b| a << b), Op::Shl => binary_op(self, |a, b| a << b),
OP_SHR => binary_op(self, |a, b| a >> b), Op::Shr => binary_op(self, |a, b| a >> b),
OP_EQUAL => binary_op(self, |a, b| Ok(Value::Bool(a == b))), Op::Equal => binary_op(self, |a, b| Ok(Value::Bool(a == b))),
OP_GREATER => binary_op(self, |a, b| Ok(Value::Bool(a > b))), Op::Greater => binary_op(self, |a, b| Ok(Value::Bool(a > b))),
OP_GREATER_EQUAL => binary_op(self, |a, b| Ok(Value::Bool(a >= b))), Op::GreaterEqual => binary_op(self, |a, b| Ok(Value::Bool(a >= b))),
OP_LESS => binary_op(self, |a, b| Ok(Value::Bool(a < b))), Op::Less => binary_op(self, |a, b| Ok(Value::Bool(a < b))),
OP_LESS_EQUAL => binary_op(self, |a, b| Ok(Value::Bool(a <= b))), Op::LessEqual => binary_op(self, |a, b| Ok(Value::Bool(a <= b))),
OP_PRINT => { Op::NotEqual => binary_op(self, |a, b| Ok(Value::Bool(a != b))),
Op::Print => {
debug!("print {:?}", self.stack); debug!("print {:?}", self.stack);
let v = self.pop(); let v = self.pop();
println!("{}", v); println!("{}", v);
} }
OP_DEFINE => { Op::DefList(len) => {
let name = self.read_name(chunk);
let value = self.pop();
self.local_vars.insert(name, value);
}
OP_DEF_LIST => {
let len = self.read(chunk);
let mut list = vec![]; let mut list = vec![];
for _ in 0..len { for _ in 0..*len {
let value = self.pop(); let value = self.pop();
list.push(value); list.push(value);
} }
list.reverse(); list.reverse();
self.push(Value::List(list)); self.push(Value::List(list));
} }
OP_ASSIGN => { Op::Assign(var_index) =>{
let index = self.read(chunk); let (var_type, name) = chunk.vars.get(*var_index).unwrap();
let (var_type, name) = chunk.vars.get(index).unwrap();
let value = self.pop(); let value = self.pop();
let value = Self::number(var_type, value)?; let value = number(var_type, value)?;
self.local_vars.insert(name.to_string(), value); //insert or update self.local_vars.insert(name.to_string(), value); //insert or update
} }
OP_DEF_MAP => { Op::DefMap(len) => {
let len = self.read(chunk);
let mut map = HashMap::new(); let mut map = HashMap::new();
for _ in 0..len { for _ in 0..*len {
let value = self.pop(); let value = self.pop();
let key = self.pop(); let key = self.pop();
map.insert(key, value); map.insert(key, value);
} }
self.push(Value::Map(map)); self.push(Value::Map(map));
} }
OP_GET => { Op::Get(var_index) => {
let var_index = self.read(chunk); let (_, name_index) = chunk.vars.get(*var_index).unwrap();
let (_, name_index) = chunk.vars.get(var_index).unwrap();
let value = self.local_vars.get(name_index).unwrap().clone(); let value = self.local_vars.get(name_index).unwrap().clone();
self.push(value); self.push(value);
} }
OP_LIST_GET => { Op::ListGet => {
let index = self.pop(); let index = self.pop();
let list = self.pop(); let list = self.pop();
if let Value::List(list) = list { if let Value::List(list) = list {
self.push(list.get(index.cast_usize()?).cloned().unwrap()) self.push(list.get(index.cast_usize()?).cloned().unwrap())
} }
} }
OP_CALL_BUILTIN => { Op::CallBuiltin(function_name_index, function_type_index, num_args) => {
let function_name_index = self.read(chunk); let function_name = chunk.constants[*function_name_index].to_string();
let function_name = chunk.constants[function_name_index].to_string(); let receiver_type_name = chunk.constants[*function_type_index].to_string();
let function_type_index = self.read(chunk);
let receiver_type_name = chunk.constants[function_type_index].to_string();
let num_args = self.read(chunk);
let mut args = vec![]; let mut args = vec![];
for _ in 0..num_args { for _ in 0..*num_args {
let arg = self.pop(); let arg = self.pop();
args.push(arg); args.push(arg);
} }
@ -201,21 +172,16 @@ impl Vm {
crate::builtins::call(&receiver_type_name, &function_name, receiver, args)?; crate::builtins::call(&receiver_type_name, &function_name, receiver, args)?;
self.push(return_value); self.push(return_value);
} }
OP_POP => { Op::Pop => {self.pop();}
self.pop(); // discards the value Op::Call(function_name_index, num_args) => {
}
OP_CALL => {
let function_name_index = self.read(chunk);
let num_args = self.read(chunk);
let mut args = vec![]; let mut args = vec![];
for _ in 0..num_args { for _ in 0..*num_args {
let arg = self.pop(); let arg = self.pop();
args.push(arg); args.push(arg);
} }
args.reverse(); args.reverse();
let function_name = chunk.constants[function_name_index].to_string(); let function_name = chunk.constants[*function_name_index].to_string();
let function_chunk = self let function_chunk = self
.registry .registry
.get(&function_name) .get(&function_name)
@ -250,55 +216,30 @@ impl Vm {
self.push(result); self.push(result);
} }
} }
OP_GOTO_NIF => { Op::GotoIfNot(goto_addr) => {
let b = self.pop(); let b = self.pop();
let goto_addr = self.read(chunk);
if b == Value::Bool(false) { if b == Value::Bool(false) {
self.ip = goto_addr; self.ip = *goto_addr;
} }
} }
OP_GOTO_IF => { Op::GotoIf(goto_addr) => {
let b = self.pop(); let b = self.pop();
let goto_addr = self.read(chunk);
if b == Value::Bool(true) { if b == Value::Bool(true) {
self.ip = goto_addr; self.ip = *goto_addr;
} }
} }
OP_GOTO => { Op::Goto(goto_addr) => {
let goto_addr = self.read(chunk); self.ip = *goto_addr;
self.ip = goto_addr;
} }
OP_DUP =>{ Op::Dup =>{
let value = self.pop(); let value = self.pop();
self.push(value.clone()); self.push(value.clone());
self.push(value); self.push(value);
} }
_ => {}
} }
} }
} }
fn number(var_type: &TokenType, value: Value) -> Result<Value, RuntimeError> {
let value = match var_type {
TokenType::U32 => value.cast_u32()?,
TokenType::U64 => value.cast_u64()?,
TokenType::F32 => value.cast_f32()?,
TokenType::I32 => value.cast_i32()?,
_ => value,
};
Ok(value)
}
fn read(&mut self, chunk: &Chunk) -> usize {
self.ip += 1;
chunk.code[self.ip - 1] as usize
}
fn read_name(&mut self, chunk: &Chunk) -> String {
let index = self.read(chunk);
chunk.constants[index].to_string() //string??
}
fn push(&mut self, value: Value) { fn push(&mut self, value: Value) {
self.stack.push(value); self.stack.push(value);
} }
@ -341,51 +282,20 @@ pub(crate) fn get_context(path: &str) -> String {
parts.join("/") parts.join("/")
} }
pub const OP_CONSTANT: u16 = 1; fn number(var_type: &TokenType, value: Value) -> Result<Value, RuntimeError> {
pub const OP_ADD: u16 = 2; let value = match var_type {
pub const OP_SUBTRACT: u16 = 3; TokenType::U32 => value.cast_u32()?,
pub const OP_MULTIPLY: u16 = 4; TokenType::U64 => value.cast_u64()?,
pub const OP_DIVIDE: u16 = 5; TokenType::F32 => value.cast_f32()?,
pub const OP_NEGATE: u16 = 6; TokenType::I32 => value.cast_i32()?,
pub const OP_PRINT: u16 = 7; _ => value,
pub const OP_RETURN: u16 = 8; };
pub const OP_CALL: u16 = 9; Ok(value)
pub const OP_DEF_FN: u16 = 10; }
pub const OP_AND: u16 = 11;
pub const OP_OR: u16 = 12; fn value_map(strings: HashMap<String, String>) -> HashMap<Value, Value> {
pub const OP_NOT: u16 = 13; strings
pub const OP_EQUAL: u16 = 14; .into_iter()
pub const OP_GREATER: u16 = 15; .map(|(k, v)| (Value::String(k.to_string()), Value::String(v.to_string())))
pub const OP_LESS: u16 = 16; .collect()
pub const OP_NOT_EQUAL: u16 = 17; }
pub const OP_GREATER_EQUAL: u16 = 18;
pub const OP_LESS_EQUAL: u16 = 19;
pub const OP_BITAND: u16 = 20;
pub const OP_BITOR: u16 = 21;
pub const OP_BITXOR: u16 = 22;
pub const OP_SHR: u16 = 23;
pub const OP_SHL: u16 = 24;
pub const OP_POP: u16 = 25;
pub const OP_DEFINE: u16 = 26; // may be obsolete already
pub const OP_GET: u16 = 27;
pub const OP_DEF_I32: u16 = 28;
pub const OP_DEF_I64: u16 = 29;
pub const OP_DEF_U32: u16 = 30;
pub const OP_DEF_U64: u16 = 31;
pub const OP_DEF_DATE: u16 = 32;
pub const OP_DEF_STRING: u16 = 33;
pub const OP_DEF_CHAR: u16 = 34;
pub const OP_DEF_BOOL: u16 = 35;
pub const OP_DEF_LIST: u16 = 36;
pub const OP_DEF_MAP: u16 = 37;
pub const OP_DEF_STRUCT: u16 = 38;
pub const OP_DEF_F32: u16 = 39;
pub const OP_DEF_F64: u16 = 40;
pub const OP_ASSIGN: u16 = 41;
pub const OP_LIST_GET: u16 = 42;
pub const OP_CALL_BUILTIN: u16 = 43;
pub const OP_DUP: u16 = 44;
// pub const OP_IF_ELSE: u16 = 45;
pub const OP_GOTO_IF: u16 = 46;
pub const OP_GOTO_NIF: u16 = 48;
pub const OP_GOTO: u16 = 47;