refactored from opcode bytes to enum
This commit is contained in:
parent
719238bd77
commit
cad1f8f1ec
12 changed files with 359 additions and 535 deletions
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
172
src/chunk.rs
172
src/chunk.rs
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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),
|
||||||
|
}
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
76
src/lib.rs
76
src/lib.rs
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
19
src/repl.rs
19
src/repl.rs
|
|
@ -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(®istry.load());
|
let mut vm = Vm::new(®istry.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())?;
|
||||||
|
|
@ -34,7 +35,7 @@ pub fn start(registry: Arc<ArcSwap<HashMap<String, Chunk>>>) -> Result<(), CrudL
|
||||||
|
|
||||||
let tokens = scan(input)?;
|
let tokens = scan(input)?;
|
||||||
|
|
||||||
let ast = match ast_pass::compile(None, tokens, &mut symbol_table){
|
let ast = match ast_pass::compile(None, tokens, &mut symbol_table) {
|
||||||
Ok(ast) => ast,
|
Ok(ast) => ast,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{}", e);
|
println!("{}", e);
|
||||||
|
|
@ -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
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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
262
src/vm.rs
|
|
@ -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(®istry);
|
|
||||||
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(®istry);
|
||||||
.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;
|
|
||||||
Loading…
Add table
Reference in a new issue