From f820d6397209cbd1c3565679c7c89fe5e490a233 Mon Sep 17 00:00:00 2001 From: Vincent Hanquez Date: Mon, 18 Dec 2023 16:48:30 +0800 Subject: [PATCH] push value bye ! --- Cargo.toml | 4 +- src/main.rs | 92 ++++++++++++++++++----------- src/value.rs | 83 ++++++++++++++++++++++++++ werbolg-compile/src/compile.rs | 26 ++++---- werbolg-compile/src/lib.rs | 16 +++-- werbolg-compile/src/params.rs | 6 ++ werbolg-core/Cargo.toml | 5 -- werbolg-core/src/basic.rs | 17 ++++-- werbolg-core/src/ir.rs | 37 ++++++++++++ werbolg-core/src/lib.rs | 4 +- werbolg-exec/src/exec.rs | 76 +++++++++++++----------- werbolg-exec/src/lib.rs | 74 ++++++++++++++--------- werbolg-exec/src/valuable.rs | 26 ++++++++ werbolg-exec/src/value.rs | 101 +++++++++++--------------------- werbolg-interpret/src/lib.rs | 16 +++-- werbolg-interpret/src/value.rs | 71 ++++------------------ werbolg-lang-lispy/src/lib.rs | 6 +- werbolg-lang-rusty/src/parse.rs | 14 ++--- 18 files changed, 407 insertions(+), 267 deletions(-) create mode 100644 src/value.rs create mode 100644 werbolg-compile/src/params.rs create mode 100644 werbolg-exec/src/valuable.rs diff --git a/Cargo.toml b/Cargo.toml index c2f5b59..5ef3ac9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,11 +7,9 @@ description = "a multi language high level dynamic platform to execute in a safe # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["lang-lispy", "lang-rusty", "backend-bignum"] +default = ["lang-lispy", "lang-rusty"] lang-lispy = ["werbolg-lang-lispy"] lang-rusty = ["werbolg-lang-rusty"] -backend-smallnum = ["werbolg-core/backend-smallnum"] -backend-bignum = ["werbolg-core/backend-bignum"] [profile.release] panic = 'abort' diff --git a/src/main.rs b/src/main.rs index 306a3d4..7cdeb2a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,43 +1,47 @@ mod lang; +mod value; use hashbrown::HashMap; +use value::Value; use werbolg_compile::{code_dump, compile, symbols::IdVec, Environment}; -use werbolg_core::{Ident, NifId, Number}; -use werbolg_exec::{ExecutionEnviron, ExecutionError, ExecutionMachine, NIFCall, Value, NIF}; +use werbolg_core::{ConstrId, Ident, Literal, NifId, ValueFun}; +use werbolg_exec::{ + ExecutionEnviron, ExecutionError, ExecutionMachine, ExecutionParams, NIFCall, Valuable, NIF, +}; use werbolg_lang_common::FileUnit; fn nif_plus(args: &[Value]) -> Result { - let n1 = args[0].number()?; - let n2 = args[1].number()?; + let n1 = args[0].int()?; + let n2 = args[1].int()?; - let ret = Number::new(n1.as_ref() + n2.as_ref()); + let ret = Value::Integral(n1 + n2); - Ok(Value::Number(ret)) + Ok(ret) } fn nif_sub(args: &[Value]) -> Result { - let n1 = args[0].number()?; - let n2 = args[1].number()?; + let n1 = args[0].int()?; + let n2 = args[1].int()?; - let ret = Number::new(n1.as_ref() - n2.as_ref()); + let ret = Value::Integral(n1 - n2); - Ok(Value::Number(ret)) + Ok(ret) } fn nif_mul(args: &[Value]) -> Result { - let n1 = args[0].number()?; - let n2 = args[1].number()?; + let n1 = args[0].int()?; + let n2 = args[1].int()?; - let ret = Number::new(n1.as_ref() * n2.as_ref()); + let ret = Value::Integral(n1 * n2); - Ok(Value::Number(ret)) + Ok(ret) } fn nif_eq(args: &[Value]) -> Result { - let n1 = args[0].number()?; - let n2 = args[1].number()?; + let n1 = args[0].int()?; + let n2 = args[1].int()?; - let ret = n1.0 == n2.0; + let ret = n1 == n2; Ok(Value::Bool(ret)) } @@ -46,24 +50,43 @@ fn nif_hashtable(_args: &[Value]) -> Result { let mut h = HashMap::::new(); h.insert(10, 20); h.insert(20, 40); - Ok(Value::make_opaque(h)) + Ok(Value::HashMap(h)) } fn nif_hashtable_get(args: &[Value]) -> Result { - let h: &HashMap = args[0].opaque()?; - let index_bignum = args[1].number()?; + let Value::HashMap(h) = &args[0] else { + return Err(ExecutionError::ValueKindUnexpected { + value_expected: Value::Integral(0).descriptor(), + value_got: Value::Integral(0).descriptor(), + }); + }; + let index_bignum = args[1].int()?; let index: u32 = index_bignum .try_into() - .map_err(|()| ExecutionError::UserPanic { - message: String::from("cannot convert number to u64"), + .map_err(|_| ExecutionError::UserPanic { + message: String::from("cannot convert Integral to u32"), })?; match h.get(&index) { None => Ok(Value::Unit), - Some(value) => Ok(Value::Number(Number::from_u64(*value))), + Some(value) => Ok(Value::Integral(*value)), + } +} + +fn literal_to_value(lit: &Literal) -> Value { + match lit { + Literal::Bool(b) => Value::Bool(true), + Literal::String(_) => Value::Unit, + Literal::Number(_) => Value::Integral(0), + Literal::Decimal(_) => Value::Unit, + Literal::Bytes(_) => Value::Unit, } } +fn literal_mapper(lit: Literal) -> Literal { + lit +} + fn get_content(args: &[String]) -> Result<(FileUnit, lang::Lang), ()> { let path = std::path::PathBuf::from(&args[1]); @@ -112,13 +135,13 @@ fn main() -> Result<(), ()> { let module = lang::parse(lang, &fileunit).expect("no parse error"); - pub struct Env<'m, T> { + pub struct Env<'m, L, T, V> { environment: Environment, - nifs: IdVec>, + nifs: IdVec>, nifs_binds: werbolg_interpret::Bindings, } - impl<'m, T> Env<'m, T> { + impl<'m, L, T, V: Valuable> Env<'m, L, T, V> { pub fn new() -> Self { Self { environment: Environment::new(), @@ -126,7 +149,7 @@ fn main() -> Result<(), ()> { nifs_binds: werbolg_interpret::Bindings::new(), } } - pub fn add_native_call(&mut self, ident: &'static str, f: NIFCall<'m, T>) { + pub fn add_native_call(&mut self, ident: &'static str, f: NIFCall<'m, L, T, V>) { let id = self.environment.add(werbolg_core::Ident::from(ident)); let id2 = self.nifs.push(NIF { name: ident, @@ -140,7 +163,7 @@ fn main() -> Result<(), ()> { pub fn add_native_mut_fun( &mut self, ident: &'static str, - f: fn(&mut ExecutionMachine<'m, T>, &[Value]) -> Result, + f: fn(&mut ExecutionMachine<'m, L, T, V>, &[V]) -> Result, ) { self.add_native_call(ident, NIFCall::Mut(f)) } @@ -148,13 +171,13 @@ fn main() -> Result<(), ()> { pub fn add_native_pure_fun( &mut self, ident: &'static str, - f: fn(&[Value]) -> Result, + f: fn(&[V]) -> Result, ) { self.add_native_call(ident, NIFCall::Pure(f)) } - pub fn finalize(self) -> ExecutionEnviron<'m, T> { - let globals = self.environment.global.remap(|f| Value::Fun(f)); + pub fn finalize(self) -> ExecutionEnviron<'m, L, T, V> { + let globals = self.environment.global.remap(|f| V::make_fun(f)); werbolg_exec::ExecutionEnviron { nifs: self.nifs, @@ -172,7 +195,9 @@ fn main() -> Result<(), ()> { env.add_native_pure_fun("table_get", nif_hashtable_get); //environment.add(ident); - let exec_module = compile(module, &mut env.environment).expect("no compilation error"); + let compilation_params = werbolg_compile::CompilationParams { literal_mapper }; + let exec_module = + compile(&compilation_params, module, &mut env.environment).expect("no compilation error"); //exec_module.print(); code_dump(&exec_module.code, &exec_module.funs); @@ -185,7 +210,8 @@ fn main() -> Result<(), ()> { .get(&Ident::from("main")) .expect("existing function as entry point"); - let mut em = ExecutionMachine::new(&exec_module, ee, ()); + let execution_params = ExecutionParams { literal_to_value }; + let mut em = ExecutionMachine::new(&exec_module, ee, execution_params, ()); //let val = werbolg_exec::exec(&mut em, Ident::from("main"), &[]).expect("no execution error"); diff --git a/src/value.rs b/src/value.rs new file mode 100644 index 0000000..89c6e5c --- /dev/null +++ b/src/value.rs @@ -0,0 +1,83 @@ +use hashbrown::HashMap; +use werbolg_core::{ConstrId, ValueFun}; +use werbolg_exec::{ExecutionError, Valuable, ValueKind}; + +#[derive(Clone, Debug)] +pub enum Value { + Unit, + // Simple values + Bool(bool), + Integral(u64), + Binary(Box<[u8]>), + HashMap(HashMap), + // Composite + List(Box<[Value]>), + Struct(ConstrId, Box<[Value]>), + Enum(u32, Box<[Value]>), + // Functions + Fun(ValueFun), +} + +impl Value { + fn desc(&self) -> ValueKind { + match self { + Value::Unit => b" unit", + Value::Bool(_) => b" bool", + Value::HashMap(_) => b" hashmap", + Value::Integral(_) => b" int", + Value::Binary(_) => b" binary", + Value::List(_) => b" list", + Value::Struct(_, _) => b" struct", + Value::Enum(_, _) => b" enum", + Value::Fun(_) => b" fun", + } + } +} + +impl Valuable for Value { + fn descriptor(&self) -> werbolg_exec::ValueKind { + self.desc() + } + + fn conditional(&self) -> Option { + match self { + Value::Bool(b) => Some(*b), + _ => None, + } + } + + fn fun(&self) -> Option { + match self { + Self::Fun(valuefun) => Some(*valuefun), + _ => None, + } + } + + fn structure(&self) -> Option<(ConstrId, &[Self])> { + todo!() + } + + fn index(&self, index: usize) -> Option<&Self> { + todo!() + } + + fn make_fun(fun: ValueFun) -> Self { + Value::Fun(fun) + } + + fn make_dummy() -> Self { + Value::Unit + } +} + +impl Value { + pub fn int(&self) -> Result { + match self { + Value::Integral(o) => Ok(*o), + _ => Err(ExecutionError::ValueKindUnexpected { + value_expected: Value::Integral(0).descriptor(), + value_got: self.descriptor(), + }), + } + } +} diff --git a/werbolg-compile/src/compile.rs b/werbolg-compile/src/compile.rs index 1146bdc..9f6bc56 100644 --- a/werbolg-compile/src/compile.rs +++ b/werbolg-compile/src/compile.rs @@ -4,14 +4,16 @@ use super::defs::*; use super::instructions::*; use super::symbols::*; use super::CompilationError; +use super::CompilationParams; use werbolg_core as ir; -use werbolg_core::{ConstrId, FunId, GlobalId, Ident, LitId, Literal, NifId, Span}; +use werbolg_core::{ConstrId, FunId, GlobalId, Ident, LitId, NifId, Span}; -pub(crate) struct RewriteState { +pub(crate) struct RewriteState<'a, L: Clone + Eq + core::hash::Hash> { + pub(crate) params: &'a CompilationParams, pub(crate) funs_tbl: SymbolsTable, pub(crate) funs_vec: IdVec, pub(crate) constrs: SymbolsTableData, - pub(crate) lits: UniqueTableBuilder, + pub(crate) lits: UniqueTableBuilder, pub(crate) main_code: Code, pub(crate) lambdas: IdVecAfter, pub(crate) lambdas_code: Code, @@ -83,13 +85,15 @@ pub enum CodeState { InLambda, } -impl RewriteState { +impl<'a, L: Clone + Eq + core::hash::Hash> RewriteState<'a, L> { pub fn new( + params: &'a CompilationParams, funs_tbl: SymbolsTable, lambdas: IdVecAfter, bindings: BindingsStack, ) -> Self { Self { + params, funs_tbl, funs_vec: IdVec::new(), main_code: Code::new(), @@ -156,8 +160,8 @@ pub(crate) fn alloc_struct( .ok_or_else(|| CompilationError::DuplicateSymbol(name)) } -pub(crate) fn rewrite_fun( - state: &mut RewriteState, +pub(crate) fn rewrite_fun<'a, L: Clone + Eq + core::hash::Hash>( + state: &mut RewriteState<'a, L>, fundef: ir::FunDef, ) -> Result { let ir::FunDef { name, vars, body } = fundef; @@ -191,14 +195,14 @@ pub(crate) fn rewrite_fun( }) } -fn rewrite_expr2( - state: &mut RewriteState, +fn rewrite_expr2<'a, L: Clone + Eq + core::hash::Hash>( + state: &mut RewriteState<'a, L>, local: &mut LocalBindings, expr: ir::Expr, ) -> Result<(), CompilationError> { match expr { ir::Expr::Literal(_span, lit) => { - let lit_id = state.lits.add(lit); + let lit_id = state.lits.add((state.params.literal_mapper)(lit)); state.write_code().push(Instruction::PushLiteral(lit_id)); Ok(()) } @@ -304,8 +308,8 @@ fn rewrite_expr2( } } -fn fetch_ident( - state: &RewriteState, +fn fetch_ident<'a, L: Clone + Eq + core::hash::Hash>( + state: &RewriteState<'a, L>, local: &LocalBindings, span: Span, ident: Ident, diff --git a/werbolg-compile/src/lib.rs b/werbolg-compile/src/lib.rs index 5c2ea70..5e43285 100644 --- a/werbolg-compile/src/lib.rs +++ b/werbolg-compile/src/lib.rs @@ -6,15 +6,17 @@ mod compile; mod defs; mod environ; mod instructions; +mod params; pub mod symbols; pub use code::{InstructionAddress, InstructionDiff}; pub use instructions::{CallArity, Instruction, LocalBindIndex, ParamBindIndex, StructFieldIndex}; +pub use params::CompilationParams; use compile::*; pub use defs::*; use werbolg_core as ir; -use werbolg_core::{ConstrId, FunId, Ident, LitId, Literal, Span}; +use werbolg_core::{ConstrId, FunId, Ident, LitId, Span}; use bindings::BindingsStack; pub use environ::Environment; @@ -27,8 +29,8 @@ pub enum CompilationError { FunctionParamsMoreThanLimit(usize), } -pub struct CompilationUnit { - pub lits: IdVec, +pub struct CompilationUnit { + pub lits: IdVec, pub constrs: SymbolsTableData, pub funs_tbl: SymbolsTable, pub funs: IdVec, @@ -36,10 +38,11 @@ pub struct CompilationUnit { } /// Compile a IR Module into an optimised-for-execution LIR Module -pub fn compile( +pub fn compile<'a, L: Clone + Eq + core::hash::Hash>( + params: &'a CompilationParams, module: ir::Module, environ: &mut Environment, -) -> Result { +) -> Result, CompilationError> { let mut funs = SymbolsTableData::new(); let mut constrs = SymbolsTableData::new(); @@ -66,7 +69,8 @@ pub fn compile( bindings.add(ident.clone(), BindingType::Fun(fun_id)) } - let mut state = compile::RewriteState::new(table, IdVecAfter::new(vecdata.next_id()), bindings); + let mut state = + compile::RewriteState::new(params, table, IdVecAfter::new(vecdata.next_id()), bindings); for (funid, fundef) in vecdata.into_iter() { let lirdef = compile::rewrite_fun(&mut state, fundef)?; diff --git a/werbolg-compile/src/params.rs b/werbolg-compile/src/params.rs new file mode 100644 index 0000000..f84638b --- /dev/null +++ b/werbolg-compile/src/params.rs @@ -0,0 +1,6 @@ +use werbolg_core::Literal; + +pub struct CompilationParams { + /// Map a werbolg-literal into a L type that will be used during execution + pub literal_mapper: fn(Literal) -> L, +} diff --git a/werbolg-core/Cargo.toml b/werbolg-core/Cargo.toml index 82d5d46..c72b969 100644 --- a/werbolg-core/Cargo.toml +++ b/werbolg-core/Cargo.toml @@ -7,11 +7,6 @@ edition = "2021" [features] default = [] -backend-smallnum = [] -backend-bignum = ["num-bigint", "num-traits", "bigdecimal"] [dependencies] -num-bigint = { version = "0.4", optional = true } -num-traits = { version = "0.2", optional = true } -bigdecimal = { version = "0.4", optional = true } hashbrown = "0.14" diff --git a/werbolg-core/src/basic.rs b/werbolg-core/src/basic.rs index 9f6b2aa..5f9b464 100644 --- a/werbolg-core/src/basic.rs +++ b/werbolg-core/src/basic.rs @@ -29,23 +29,27 @@ impl Ident { #[derive(Clone, Hash, PartialEq, Eq)] pub enum Literal { - String(String), - Number(Number), - Decimal(Decimal), + Bool(Box), + String(Box), + Number(Box), + Decimal(Box), Bytes(Box<[u8]>), } impl core::fmt::Debug for Literal { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { + Literal::Bool(s) => { + write!(f, "\"{}\"", s) + } Literal::String(s) => { write!(f, "\"{}\"", s) } Literal::Number(n) => { - write!(f, "{:?}", n) + write!(f, "{}", n) } Literal::Decimal(d) => { - write!(f, "{:?}", d) + write!(f, "{}", d) } Literal::Bytes(bytes) => { write!(f, "#")?; @@ -58,6 +62,7 @@ impl core::fmt::Debug for Literal { } } +/* #[cfg(feature = "backend-bignum")] use num_traits::{Num, ToPrimitive}; @@ -229,3 +234,5 @@ impl Decimal { } } } + +*/ diff --git a/werbolg-core/src/ir.rs b/werbolg-core/src/ir.rs index 97d2f4f..7ee32fd 100644 --- a/werbolg-core/src/ir.rs +++ b/werbolg-core/src/ir.rs @@ -1,13 +1,26 @@ +//! AST for werbolg +//! +//! This try to remain generic to allow multiple different language (existing or new) +//! to target werbolg and be interpreted through it + use super::basic::*; use super::location::*; use alloc::{boxed::Box, vec::Vec}; +/// AST for a module / source code unit #[derive(Clone, Debug)] pub struct Module { pub statements: Vec, } +/// AST for a high level statement +/// +/// Current known types are: +/// +/// * Function definition +/// * Struct definition +/// * Naked expression #[derive(Clone, Debug)] pub enum Statement { Function(Span, FunDef), @@ -15,6 +28,14 @@ pub enum Statement { Expr(Expr), } +/// AST for function definition +/// +/// Function definitions are something like: +/// +/// ```text +/// function $name ( $vars ) { $body } +/// ``` +/// #[derive(Clone, Debug)] pub struct FunDef { pub name: Option, @@ -22,12 +43,28 @@ pub struct FunDef { pub body: Expr, } +/// AST for Structure definition +/// +/// Structure definitions are something like +/// +/// ```text +/// struct $name { $fields } +/// ``` +/// #[derive(Clone, Debug)] pub struct StructDef { pub name: Spanned, pub fields: Vec>, } +/// AST for Enum definition +/// +/// Enum definitions are something like +/// +/// ```text +/// enum $name { $variants } +/// ``` +/// #[derive(Clone, Debug)] pub struct EnumDef { pub name: Spanned, diff --git a/werbolg-core/src/lib.rs b/werbolg-core/src/lib.rs index 7d96721..dd33117 100644 --- a/werbolg-core/src/lib.rs +++ b/werbolg-core/src/lib.rs @@ -1,11 +1,11 @@ -//#![no_std] +#![no_std] extern crate alloc; mod basic; pub mod id; pub mod idvec; -mod ir; +pub mod ir; mod location; pub use basic::*; diff --git a/werbolg-exec/src/exec.rs b/werbolg-exec/src/exec.rs index 98c2fbd..0b3ce77 100644 --- a/werbolg-exec/src/exec.rs +++ b/werbolg-exec/src/exec.rs @@ -1,21 +1,23 @@ +use crate::Valuable; + use super::NIFCall; -use super::{ExecutionError, ExecutionMachine, Value, ValueKind}; +use super::{ExecutionError, ExecutionMachine}; use werbolg_compile::{CallArity, Instruction, InstructionAddress, LocalStackSize}; use werbolg_core as ir; use werbolg_core::ValueFun; -pub fn exec<'module, T>( - em: &mut ExecutionMachine<'module, T>, +pub fn exec<'module, L, T, V: Valuable>( + em: &mut ExecutionMachine<'module, L, T, V>, call: ir::FunId, - args: &[Value], -) -> Result { + args: &[V], +) -> Result { let arity = args .len() .try_into() .map(CallArity) .map_err(|_| ExecutionError::ArityOverflow { got: args.len() })?; - em.stack.push_call(Value::Fun(ValueFun::Fun(call)), args); + em.stack.push_call(V::make_fun(ValueFun::Fun(call)), args); match process_call(em, arity)? { CallResult::Jump(ip, local) => { @@ -25,20 +27,25 @@ pub fn exec<'module, T>( CallResult::Value(value) => return Ok(value), }; - println!("===== initial ====="); - em.debug_state(); - println!("==================="); + //println!("===== initial ====="); + //em.debug_state(); + //println!("==================="); exec_loop(em) } -pub fn exec_continue<'m, T>(em: &mut ExecutionMachine<'m, T>) -> Result { + +pub fn exec_continue<'m, L, T, V: Valuable>( + em: &mut ExecutionMachine<'m, L, T, V>, +) -> Result { if em.rets.is_empty() { return Err(ExecutionError::ExecutionFinished); } exec_loop(em) } -fn exec_loop<'m, T>(em: &mut ExecutionMachine<'m, T>) -> Result { +fn exec_loop<'m, L, T, V: Valuable>( + em: &mut ExecutionMachine<'m, L, T, V>, +) -> Result { loop { if em.aborted() { return Err(ExecutionError::Abort); @@ -50,7 +57,7 @@ fn exec_loop<'m, T>(em: &mut ExecutionMachine<'m, T>) -> Result, ExecutionError>; +type StepResult = Result, ExecutionError>; /// Step through 1 single instruction, and returning a Step Result which is either: /// @@ -58,7 +65,7 @@ type StepResult = Result, ExecutionError>; /// * not an error : Either no value or a value if the execution of the program is finished /// /// The step function need to update the execution IP -pub fn step<'m, T>(em: &mut ExecutionMachine<'m, T>) -> StepResult { +pub fn step<'m, L, T, V: Valuable>(em: &mut ExecutionMachine<'m, L, T, V>) -> StepResult { let instr = &em.module.code[em.ip]; /* print!( @@ -72,7 +79,7 @@ pub fn step<'m, T>(em: &mut ExecutionMachine<'m, T>) -> StepResult { match instr { Instruction::PushLiteral(lit) => { let literal = &em.module.lits[*lit]; - em.stack.push_value(Value::from(literal)); + em.stack.push_value((em.params.literal_to_value)(literal)); em.ip_next(); } Instruction::FetchGlobal(global_id) => { @@ -80,11 +87,11 @@ pub fn step<'m, T>(em: &mut ExecutionMachine<'m, T>) -> StepResult { em.ip_next(); } Instruction::FetchNif(nif_id) => { - em.stack.push_value(Value::Fun(ValueFun::Native(*nif_id))); + em.stack.push_value(V::make_fun(ValueFun::Native(*nif_id))); em.ip_next(); } Instruction::FetchFun(fun_id) => { - em.stack.push_value(Value::Fun(ValueFun::Fun(*fun_id))); + em.stack.push_value(V::make_fun(ValueFun::Fun(*fun_id))); em.ip_next(); } Instruction::FetchStackLocal(local_bind) => { @@ -97,10 +104,9 @@ pub fn step<'m, T>(em: &mut ExecutionMachine<'m, T>) -> StepResult { } Instruction::AccessField(expected_cid, idx) => { let val = em.stack.pop_value(); - let Value::Struct(got_cid, inner) = val else { - return Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::Struct, - value_got: (&val).into(), + let Some((got_cid, inner)) = val.structure() else { + return Err(ExecutionError::ValueNotStruct { + value_is: val.descriptor(), }); }; @@ -148,7 +154,11 @@ pub fn step<'m, T>(em: &mut ExecutionMachine<'m, T>) -> StepResult { Instruction::Jump(d) => em.ip_jump(*d), Instruction::CondJump(d) => { let val = em.stack.pop_value(); - let b = val.bool()?; + let Some(b) = val.conditional() else { + return Err(ExecutionError::ValueNotConditional { + value_is: val.descriptor(), + }); + }; if b { em.ip_next() } else { @@ -174,28 +184,26 @@ pub fn step<'m, T>(em: &mut ExecutionMachine<'m, T>) -> StepResult { Ok(None) } -enum CallResult { +enum CallResult { Jump(InstructionAddress, LocalStackSize), - Value(Value), + Value(V), } -fn process_call<'m, T>( - em: &mut ExecutionMachine<'m, T>, +fn process_call<'m, L, T, V: Valuable>( + em: &mut ExecutionMachine<'m, L, T, V>, arity: CallArity, -) -> Result { +) -> Result, ExecutionError> { let first = em.stack.get_call(arity); - let Value::Fun(fun) = first else { - em.debug_state(); - let kind = first.into(); - return Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::Fun, - value_got: kind, + let Some(fun) = first.fun() else { + //em.debug_state(); + return Err(ExecutionError::CallingNotFunc { + value_is: first.descriptor(), }); }; match fun { ValueFun::Native(nifid) => { - let res = match &em.nifs[*nifid].call { + let res = match &em.nifs[nifid].call { NIFCall::Pure(nif) => { let (_first, args) = em.stack.get_call_and_args(arity); nif(args)? @@ -207,7 +215,7 @@ fn process_call<'m, T>( Ok(CallResult::Value(res)) } ValueFun::Fun(funid) => { - let call_def = &em.module.funs[*funid]; + let call_def = &em.module.funs[funid]; Ok(CallResult::Jump(call_def.code_pos, call_def.stack_size)) } } diff --git a/werbolg-exec/src/lib.rs b/werbolg-exec/src/lib.rs index 380a136..68e6e05 100644 --- a/werbolg-exec/src/lib.rs +++ b/werbolg-exec/src/lib.rs @@ -12,38 +12,46 @@ use werbolg_compile::{CompilationUnit, InstructionAddress, InstructionDiff}; use werbolg_core as ir; mod exec; +mod valuable; mod value; use alloc::{string::String, vec::Vec}; -pub use value::{NIFCall, Value, ValueKind, NIF}; +pub use valuable::{Valuable, ValueKind}; +pub use value::{NIFCall, NIF}; pub use exec::{exec, exec_continue, step}; -pub struct ExecutionEnviron<'m, T> { - pub nifs: IdVec>, - pub globals: IdVec, +pub struct ExecutionEnviron<'m, L, T, V> { + pub nifs: IdVec>, + pub globals: IdVec, } -pub struct ExecutionMachine<'m, T> { - pub nifs: IdVec>, - pub globals: IdVec, - pub module: &'m CompilationUnit, +#[derive(Clone)] +pub struct ExecutionParams { + pub literal_to_value: fn(&L) -> V, +} + +pub struct ExecutionMachine<'m, L, T, V> { + pub nifs: IdVec>, + pub globals: IdVec, + pub module: &'m CompilationUnit, pub rets: Vec<(InstructionAddress, StackPointer, LocalStackSize, CallArity)>, - pub stack: ValueStack, + pub stack: ValueStack, pub ip: InstructionAddress, pub sp: StackPointer, pub current_stack_size: LocalStackSize, + pub params: ExecutionParams, pub userdata: T, } #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)] pub struct StackPointer(usize); -pub struct ValueStack { - values: Vec, +pub struct ValueStack { + values: Vec, } -impl ValueStack { +impl ValueStack { pub fn new() -> Self { Self { values: Vec::new() } } @@ -52,7 +60,7 @@ impl ValueStack { StackPointer(self.values.len()) } - pub fn push_call(&mut self, call: Value, args: &[Value]) { + pub fn push_call(&mut self, call: V, args: &[V]) { self.values.push(call); self.values.extend_from_slice(args); } @@ -67,12 +75,12 @@ impl ValueStack { self.values.truncate(n) } - pub fn get_call(&self, arity: CallArity) -> &Value { + pub fn get_call(&self, arity: CallArity) -> &V { let top = self.values.len(); &self.values[top - (arity.0 as usize) - 1] } - pub fn set_at(&mut self, index: usize, value: Value) { + pub fn set_at(&mut self, index: usize, value: V) { self.values[index] = value; } @@ -81,15 +89,15 @@ impl ValueStack { self.push_value(value) } - pub fn push_value(&mut self, arg: Value) { + pub fn push_value(&mut self, arg: V) { self.values.push(arg); } - pub fn pop_value(&mut self) -> Value { + pub fn pop_value(&mut self) -> V { self.values.pop().expect("can be popped") } - pub fn get_call_and_args(&self, arity: CallArity) -> (&Value, &[Value]) { + pub fn get_call_and_args(&self, arity: CallArity) -> (&V, &[V]) { let top = self.values.len(); ( &self.values[top - (arity.0 as usize) - 1], @@ -97,7 +105,7 @@ impl ValueStack { ) } - pub fn iter_pos(&self) -> impl Iterator { + pub fn iter_pos(&self) -> impl Iterator { self.values .iter() .enumerate() @@ -105,10 +113,13 @@ impl ValueStack { } } -pub type BindingValue = Value; - -impl<'m, T> ExecutionMachine<'m, T> { - pub fn new(module: &'m CompilationUnit, env: ExecutionEnviron<'m, T>, userdata: T) -> Self { +impl<'m, L, T, V: Valuable> ExecutionMachine<'m, L, T, V> { + pub fn new( + module: &'m CompilationUnit, + env: ExecutionEnviron<'m, L, T, V>, + params: ExecutionParams, + userdata: T, + ) -> Self { Self { nifs: env.nifs, globals: env.globals, @@ -118,6 +129,7 @@ impl<'m, T> ExecutionMachine<'m, T> { userdata, ip: InstructionAddress::default(), sp: StackPointer::default(), + params, current_stack_size: LocalStackSize(0), } } @@ -155,7 +167,7 @@ impl<'m, T> ExecutionMachine<'m, T> { } #[inline] - pub fn sp_set_value_at(&mut self, bind_index: LocalBindIndex, value: Value) { + pub fn sp_set_value_at(&mut self, bind_index: LocalBindIndex, value: V) { let index = self.sp.0 + bind_index.0 as usize; self.stack.set_at(index, value); } @@ -182,12 +194,14 @@ impl<'m, T> ExecutionMachine<'m, T> { pub fn sp_set(&mut self, local_stack_size: LocalStackSize) { self.sp = self.stack.top(); for _ in 0..local_stack_size.0 { - self.stack.push_value(Value::Unit); + self.stack.push_value(V::make_dummy()); } self.current_stack_size = local_stack_size; //println!("SP={} local={}", self.sp.0, local_stack_size.0) } +} +impl<'m, L, T, V: Valuable + core::fmt::Debug> ExecutionMachine<'m, L, T, V> { pub fn debug_state(&self) { println!("ip={} sp={:?}", self.ip, self.sp.0); @@ -219,8 +233,8 @@ pub enum ExecutionError { ArityOverflow { got: usize, }, - AccessingInexistentField(ir::Ident, ir::Ident), - AccessingFieldNotAStruct(ir::Ident, ValueKind), + //AccessingInexistentField(ir::Ident, ir::Ident), + //AccessingFieldNotAStruct(ir::Ident, ValueKind), MissingBinding(ir::Ident), InternalErrorFunc(ir::FunId), StructMismatch { @@ -235,6 +249,12 @@ pub enum ExecutionError { CallingNotFunc { value_is: ValueKind, }, + ValueNotStruct { + value_is: ValueKind, + }, + ValueNotConditional { + value_is: ValueKind, + }, ValueKindUnexpected { value_expected: ValueKind, value_got: ValueKind, diff --git a/werbolg-exec/src/valuable.rs b/werbolg-exec/src/valuable.rs new file mode 100644 index 0000000..f2d917c --- /dev/null +++ b/werbolg-exec/src/valuable.rs @@ -0,0 +1,26 @@ +use werbolg_core::{ConstrId, ValueFun}; + +pub type ValueKind = &'static [u8; 8]; + +pub trait Valuable: Clone { + /// Get the descriptor of the Valuable object + fn descriptor(&self) -> ValueKind; + + /// Get the boolean value of a conditional value, or None if not valid + fn conditional(&self) -> Option; + + /// Get the a function value from a Valuable object, or None if not valid + fn fun(&self) -> Option; + + /// Get a structure out of a Valuable object, or None if not valid + fn structure(&self) -> Option<(ConstrId, &[Self])>; + + /// Get the elements #index of a Valuable object, or None if not valid + fn index(&self, index: usize) -> Option<&Self>; + + /// Create a Fun valuable object + fn make_fun(fun: ValueFun) -> Self; + + /// Create a dummy parameter to push on the stack. + fn make_dummy() -> Self; +} diff --git a/werbolg-exec/src/value.rs b/werbolg-exec/src/value.rs index 0ad4b66..5a45236 100644 --- a/werbolg-exec/src/value.rs +++ b/werbolg-exec/src/value.rs @@ -1,21 +1,18 @@ //! Execution machine value - define the Value type use super::{ExecutionError, ExecutionMachine}; -use alloc::{boxed::Box, rc::Rc}; +use alloc::rc::Rc; use core::any::Any; use core::cell::RefCell; -use werbolg_core::{self as ir, ConstrId, Decimal, FunId, Literal, NifId, Number, ValueFun}; +/* /// Execution Machine Value #[derive(Clone, Debug)] pub enum Value { Unit, // Simple values - Bool(bool), - Number(Number), - Decimal(Decimal), - String(Box), - Bytes(Box<[u8]>), + Small(u64), + Binary(Box<[u8]>), Opaque(Opaque), OpaqueMut(OpaqueMut), // Composite @@ -29,11 +26,8 @@ pub enum Value { #[derive(Debug, Clone)] pub enum ValueKind { Unit, - Bool, - Number, - Decimal, - String, - Bytes, + Small, + Binary, Opaque, OpaqueMut, List, @@ -58,11 +52,8 @@ impl<'a> From<&'a Value> for ValueKind { fn from(value: &'a Value) -> Self { match value { Value::Unit => ValueKind::Unit, - Value::Bool(_) => ValueKind::Bool, - Value::Number(_) => ValueKind::Number, - Value::Decimal(_) => ValueKind::Decimal, - Value::String(_) => ValueKind::String, - Value::Bytes(_) => ValueKind::Bytes, + Value::Small(_) => ValueKind::Small, + Value::Binary(_) => ValueKind::Binary, Value::Opaque(_) => ValueKind::Opaque, Value::OpaqueMut(_) => ValueKind::OpaqueMut, Value::List(_) => ValueKind::List, @@ -72,22 +63,24 @@ impl<'a> From<&'a Value> for ValueKind { } } } +*/ /// Native Implemented Function -pub struct NIF<'m, T> { +pub struct NIF<'m, L, T, V> { pub name: &'static str, - pub call: NIFCall<'m, T>, + pub call: NIFCall<'m, L, T, V>, } /// 2 Variants of Native calls /// /// * "Pure" function that don't have access to the execution machine /// * "Mut" function that have access to the execution machine and have more power / responsability. -pub enum NIFCall<'m, T> { - Pure(fn(&[Value]) -> Result), - Mut(fn(&mut ExecutionMachine<'m, T>, &[Value]) -> Result), +pub enum NIFCall<'m, L, T, V> { + Pure(fn(&[V]) -> Result), + Mut(fn(&mut ExecutionMachine<'m, L, T, V>, &[V]) -> Result), } +/* #[derive(Clone)] pub struct Opaque(Rc); @@ -145,9 +138,11 @@ impl core::fmt::Debug for OpaqueMut { } } +/* impl<'a> From<&'a Literal> for Value { fn from(literal: &'a Literal) -> Value { match literal { + Literal::Bool(b) => Literal::String(s) => Value::String(s.clone().into_boxed_str()), Literal::Number(n) => Value::Number(n.clone()), Literal::Decimal(d) => Value::Decimal(d.clone()), @@ -155,6 +150,7 @@ impl<'a> From<&'a Literal> for Value { } } } +*/ impl Value { pub fn make_opaque(t: T) -> Self { @@ -165,81 +161,51 @@ impl Value { Value::OpaqueMut(OpaqueMut::new(t)) } - pub fn opaque(&self) -> Result<&T, ExecutionError> { - match self { - Value::Opaque(o) => o.downcast_ref(), - _ => Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::Opaque, - value_got: self.into(), - }), - } - } - - pub fn fun(&self) -> Result { + pub fn small(&self) -> Result { match self { - Value::Fun(valuefun) => Ok(*valuefun), + Value::Small(o) => Ok(*o), _ => Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::Fun, + value_expected: ValueKind::Small, value_got: self.into(), }), } } - pub fn unit(&self) -> Result<(), ExecutionError> { + pub fn binary(&self) -> Result, ExecutionError> { match self { - Value::Unit => Ok(()), + Value::Binary(o) => Ok(*o), _ => Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::Unit, + value_expected: ValueKind::Small, value_got: self.into(), }), } } - pub fn bool(&self) -> Result { - match self { - Value::Bool(v) => Ok(*v), - _ => Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::Bool, - value_got: self.into(), - }), - } - } - - pub fn number(&self) -> Result<&ir::Number, ExecutionError> { - match self { - Value::Number(v) => Ok(v), - _ => Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::Number, - value_got: self.into(), - }), - } - } - - pub fn decimal(&self) -> Result<&ir::Decimal, ExecutionError> { + pub fn opaque(&self) -> Result<&T, ExecutionError> { match self { - Value::Decimal(v) => Ok(v), + Value::Opaque(o) => o.downcast_ref(), _ => Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::Decimal, + value_expected: ValueKind::Opaque, value_got: self.into(), }), } } - pub fn string(&self) -> Result<&str, ExecutionError> { + pub fn fun(&self) -> Result { match self { - Value::String(v) => Ok(v), + Value::Fun(valuefun) => Ok(*valuefun), _ => Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::String, + value_expected: ValueKind::Fun, value_got: self.into(), }), } } - pub fn bytes(&self) -> Result<&Box<[u8]>, ExecutionError> { + pub fn unit(&self) -> Result<(), ExecutionError> { match self { - Value::Bytes(v) => Ok(v), + Value::Unit => Ok(()), _ => Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::Bytes, + value_expected: ValueKind::Unit, value_got: self.into(), }), } @@ -255,3 +221,4 @@ impl Value { } } } +*/ diff --git a/werbolg-interpret/src/lib.rs b/werbolg-interpret/src/lib.rs index 40e80ab..5693010 100644 --- a/werbolg-interpret/src/lib.rs +++ b/werbolg-interpret/src/lib.rs @@ -4,7 +4,7 @@ extern crate alloc; use werbolg_core as ir; -use werbolg_core::{idvec::IdVec, FunDef, FunId, NifId, ValueFun}; +use werbolg_core::{idvec::IdVec, FunDef, FunId, Literal, NifId, ValueFun}; mod bindings; mod location; @@ -17,6 +17,12 @@ pub use location::Location; use stack::{ExecutionAtom, ExecutionNext, ExecutionStack}; pub use value::{NIFCall, Value, ValueKind, NIF}; +#[derive(Clone)] +pub struct ExecutionParams { + pub literal_to_value: fn(&Literal) -> Value, + pub literal_to_condition: fn(&Literal) -> bool, +} + pub struct ExecutionMachine<'m, T> { //pub nifs_binds: Bindings, pub nifs: IdVec>, @@ -25,13 +31,14 @@ pub struct ExecutionMachine<'m, T> { pub module: &'m ir::Module, pub local: BindingsStack, pub stack: ExecutionStack<'m>, + pub params: ExecutionParams, pub userdata: T, } pub type BindingValue = Value; impl<'m, T> ExecutionMachine<'m, T> { - pub fn new(module: &'m ir::Module, userdata: T) -> Self { + pub fn new(params: ExecutionParams, module: &'m ir::Module, userdata: T) -> Self { let mut b = Bindings::new(); let mut funs = IdVec::new(); for stat in module.statements.iter() { @@ -49,6 +56,7 @@ impl<'m, T> ExecutionMachine<'m, T> { } } Self { + params, nifs: IdVec::new(), funs, global: b, @@ -191,7 +199,7 @@ pub fn exec_continue<'m, T>(em: &mut ExecutionMachine<'m, T>) -> Result(em: &mut ExecutionMachine<'m, T>, e: &'m ir::Expr) -> Result<(), ExecutionError> { match e { - ir::Expr::Literal(_, lit) => em.stack.push_value(Value::from(lit)), + ir::Expr::Literal(_, lit) => em.stack.push_value((em.params.literal_to_value)(lit)), ir::Expr::Ident(_, ident) => em.stack.push_value(em.get_binding(ident)?), ir::Expr::List(_, l) => { if l.is_empty() { @@ -247,7 +255,7 @@ fn eval<'m, T>( } ExecutionAtom::ThenElse(then_expr, else_expr) => { let cond_val = args.into_iter().next().unwrap(); - let cond_bool = cond_val.bool()?; + let cond_bool = (em.params.literal_to_condition)(cond_val.literal()?); if cond_bool { work(em, &then_expr)? diff --git a/werbolg-interpret/src/value.rs b/werbolg-interpret/src/value.rs index 0ad4b66..3f58519 100644 --- a/werbolg-interpret/src/value.rs +++ b/werbolg-interpret/src/value.rs @@ -4,18 +4,14 @@ use super::{ExecutionError, ExecutionMachine}; use alloc::{boxed::Box, rc::Rc}; use core::any::Any; use core::cell::RefCell; -use werbolg_core::{self as ir, ConstrId, Decimal, FunId, Literal, NifId, Number, ValueFun}; +use werbolg_core::{ConstrId, FunId, Literal, NifId, ValueFun}; /// Execution Machine Value #[derive(Clone, Debug)] pub enum Value { Unit, // Simple values - Bool(bool), - Number(Number), - Decimal(Decimal), - String(Box), - Bytes(Box<[u8]>), + Literal(Literal), Opaque(Opaque), OpaqueMut(OpaqueMut), // Composite @@ -29,11 +25,7 @@ pub enum Value { #[derive(Debug, Clone)] pub enum ValueKind { Unit, - Bool, - Number, - Decimal, - String, - Bytes, + Literal, Opaque, OpaqueMut, List, @@ -58,11 +50,7 @@ impl<'a> From<&'a Value> for ValueKind { fn from(value: &'a Value) -> Self { match value { Value::Unit => ValueKind::Unit, - Value::Bool(_) => ValueKind::Bool, - Value::Number(_) => ValueKind::Number, - Value::Decimal(_) => ValueKind::Decimal, - Value::String(_) => ValueKind::String, - Value::Bytes(_) => ValueKind::Bytes, + Value::Literal(_) => ValueKind::Literal, Value::Opaque(_) => ValueKind::Opaque, Value::OpaqueMut(_) => ValueKind::OpaqueMut, Value::List(_) => ValueKind::List, @@ -145,16 +133,19 @@ impl core::fmt::Debug for OpaqueMut { } } +/* impl<'a> From<&'a Literal> for Value { fn from(literal: &'a Literal) -> Value { match literal { - Literal::String(s) => Value::String(s.clone().into_boxed_str()), + Literal::Bool(s) => Value::Bool(s), + Literal::String(s) => Value::String(s.clone()), Literal::Number(n) => Value::Number(n.clone()), Literal::Decimal(d) => Value::Decimal(d.clone()), Literal::Bytes(b) => Value::Bytes(b.clone()), } } } +*/ impl Value { pub fn make_opaque(t: T) -> Self { @@ -195,51 +186,11 @@ impl Value { } } - pub fn bool(&self) -> Result { + pub fn literal(&self) -> Result<&Literal, ExecutionError> { match self { - Value::Bool(v) => Ok(*v), + Value::Literal(v) => Ok(v), _ => Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::Bool, - value_got: self.into(), - }), - } - } - - pub fn number(&self) -> Result<&ir::Number, ExecutionError> { - match self { - Value::Number(v) => Ok(v), - _ => Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::Number, - value_got: self.into(), - }), - } - } - - pub fn decimal(&self) -> Result<&ir::Decimal, ExecutionError> { - match self { - Value::Decimal(v) => Ok(v), - _ => Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::Decimal, - value_got: self.into(), - }), - } - } - - pub fn string(&self) -> Result<&str, ExecutionError> { - match self { - Value::String(v) => Ok(v), - _ => Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::String, - value_got: self.into(), - }), - } - } - - pub fn bytes(&self) -> Result<&Box<[u8]>, ExecutionError> { - match self { - Value::Bytes(v) => Ok(v), - _ => Err(ExecutionError::ValueKindUnexpected { - value_expected: ValueKind::Bytes, + value_expected: ValueKind::Literal, value_got: self.into(), }), } diff --git a/werbolg-lang-lispy/src/lib.rs b/werbolg-lang-lispy/src/lib.rs index c43fd3b..e01deae 100644 --- a/werbolg-lang-lispy/src/lib.rs +++ b/werbolg-lang-lispy/src/lib.rs @@ -210,9 +210,9 @@ fn exprs(span: Span, exprs: Vec>) -> Result { fn literal(lit: ast::Literal) -> ir::Literal { match lit { - ast::Literal::Bytes(b) => ir::Literal::Bytes(b.into()), - ast::Literal::Number(n) => ir::Literal::Number(ir::Number::from_str_radix(&n, 10).unwrap()), - ast::Literal::String(s) => ir::Literal::String(s), + ast::Literal::Bytes(b) => ir::Literal::Bytes(b.into_boxed_slice()), + ast::Literal::Number(n) => ir::Literal::Number(n.into_boxed_str()), + ast::Literal::String(s) => ir::Literal::String(s.into_boxed_str()), } } diff --git a/werbolg-lang-rusty/src/parse.rs b/werbolg-lang-rusty/src/parse.rs index dd17759..375c66d 100644 --- a/werbolg-lang-rusty/src/parse.rs +++ b/werbolg-lang-rusty/src/parse.rs @@ -5,14 +5,14 @@ use ariadne::{Color, Fmt, Label, Report, ReportKind, Source}; use chumsky::{prelude::*, stream::Stream}; use core::fmt; -use werbolg_core::{self as ir, Literal, Number, Variable}; +use werbolg_core::{self as ir, Literal, Variable}; pub type Span = core::ops::Range; #[derive(Clone, Debug, PartialEq, Eq, Hash)] enum Token { Null, - Bool(bool), + Bool(String), Num(String), Str(String), Op(String), @@ -72,8 +72,8 @@ fn lexer() -> impl Parser, Error = Simple> { "let" => Token::Let, "if" => Token::If, "else" => Token::Else, - "true" => Token::Bool(true), - "false" => Token::Bool(false), + "true" => Token::Bool(ident), + "false" => Token::Bool(ident), "null" => Token::Null, _ => Token::Ident(ident), }); @@ -146,9 +146,9 @@ fn expr_parser() -> impl Parser, Error = Simple> + C let raw_expr = recursive(|raw_expr| { let val = select! { //Token::Null => todo!(), - //Token::Bool(x) => todo!(), //Expr::Literal(Value::Bool(x)), - Token::Num(n) => Expr::Literal(Literal::Number(Number::from_str_radix(&n, 10).unwrap())), - Token::Str(s) => Expr::Literal(Literal::String(s)), + Token::Bool(s) => Expr::Literal(Literal::Bool(s.into_boxed_str())), + Token::Num(s) => Expr::Literal(Literal::Number(s.into_boxed_str())), + Token::Str(s) => Expr::Literal(Literal::String(s.into_boxed_str())), } .labelled("value");