diff --git a/werbolg-compile/src/compile.rs b/werbolg-compile/src/compile.rs index 5b980e4..4d147da 100644 --- a/werbolg-compile/src/compile.rs +++ b/werbolg-compile/src/compile.rs @@ -218,8 +218,23 @@ fn generate_expression_code<'a, L: Clone + Eq + core::hash::Hash>( } Ok(false) } - ir::Expr::List(_span, _l) => { - todo!("list ?") + ir::Expr::Sequence(span, l) => { + let call_arity = l + .len() + .try_into() + .map_err(|sz| CompilationError::CallTooManyArguments(span.clone(), sz))?; + for e in l { + let _: bool = generate_expression_code(state, local, FunPos::NotRoot, e)?; + } + match &state.params.sequence_constructor { + None => return Err(CompilationError::SequenceNotSupported(span)), + Some(nifid) => { + state + .write_code() + .push(Instruction::CallNif(*nifid, call_arity)); + Ok(true) + } + } } ir::Expr::Let(binder, body, in_expr) => { let x = body.clone(); @@ -280,22 +295,25 @@ fn generate_expression_code<'a, L: Clone + Eq + core::hash::Hash>( Ok(false) } - ir::Expr::Call(_span, args) => { + ir::Expr::Call(span, args) => { assert!(args.len() > 0); let len = args.len() - 1; for arg in args { let _: bool = generate_expression_code(state, local, FunPos::NotRoot, arg)?; () } + let call_arity = len + .try_into() + .map_err(|sz| CompilationError::CallTooManyArguments(span, sz))?; if funpos == FunPos::Root { state .write_code() - .push(Instruction::Call(TailCall::Yes, CallArity(len as u8))); + .push(Instruction::Call(TailCall::Yes, call_arity)); Ok(true) } else { state .write_code() - .push(Instruction::Call(TailCall::No, CallArity(len as u8))); + .push(Instruction::Call(TailCall::No, call_arity)); Ok(false) } } diff --git a/werbolg-compile/src/errors.rs b/werbolg-compile/src/errors.rs index ff10d21..71ab00f 100644 --- a/werbolg-compile/src/errors.rs +++ b/werbolg-compile/src/errors.rs @@ -16,12 +16,16 @@ pub enum CompilationError { FunctionParamsMoreThanLimit(Span, usize), /// Core's Literal is not supported by this compiler LiteralNotSupported(Span, Literal), + /// Core's Sequence is not supported by this compiler + SequenceNotSupported(Span), /// The constructor specified is a not a structure, but trying to access inner field ConstructorNotStructure(Span, Path), /// The structure specified doesn't have a field of the right name StructureFieldNotExistant(Span, Path, Ident), /// Namespace Error NamespaceError(NamespaceError), + /// Too Many argument to call + CallTooManyArguments(Span, usize), /// A recursive compilation with some context added Context(String, Box), } @@ -35,9 +39,11 @@ impl CompilationError { CompilationError::MissingConstructor(span, _) => span.clone(), CompilationError::FunctionParamsMoreThanLimit(span, _) => span.clone(), CompilationError::LiteralNotSupported(span, _) => span.clone(), + CompilationError::SequenceNotSupported(span) => span.clone(), CompilationError::ConstructorNotStructure(span, _) => span.clone(), CompilationError::StructureFieldNotExistant(span, _, _) => span.clone(), CompilationError::NamespaceError(_) => todo!(), + CompilationError::CallTooManyArguments(span, _) => span.clone(), CompilationError::Context(_, e) => e.span(), } } diff --git a/werbolg-compile/src/instructions.rs b/werbolg-compile/src/instructions.rs index 552eec4..317bb7f 100644 --- a/werbolg-compile/src/instructions.rs +++ b/werbolg-compile/src/instructions.rs @@ -26,6 +26,10 @@ pub enum Instruction { /// /// expecting N+1 value on the value stack Call(TailCall, CallArity), + /// Call the Nif function on the stack with the N value in arguments. + /// + /// expecting N value on the value stack, as the NifId is embedded in the instruction + CallNif(NifId, CallArity), /// Jump by N instructions Jump(InstructionDiff), /// Jump by N instructions if stack\[top\] is true @@ -64,3 +68,20 @@ pub struct StructFieldIndex(pub u8); /// This is limited (arbitrarily) to a maximum of 255 #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CallArity(pub u8); + +impl TryFrom for CallArity { + type Error = usize; + fn try_from(value: usize) -> Result { + value.try_into().map(|v| CallArity(v)).map_err(|_| value) + } +} + +impl TryFrom for CallArity { + type Error = usize; + fn try_from(value: u32) -> Result { + value + .try_into() + .map(|v| CallArity(v)) + .map_err(|_| value as usize) + } +} diff --git a/werbolg-compile/src/params.rs b/werbolg-compile/src/params.rs index 92e8c52..cd1ff6d 100644 --- a/werbolg-compile/src/params.rs +++ b/werbolg-compile/src/params.rs @@ -1,10 +1,13 @@ use super::CompilationError; -use werbolg_core::Literal; -use werbolg_core::Span; +use werbolg_core::{Literal, NifId, Span}; /// User driven compilation parameters #[derive(Clone)] pub struct CompilationParams { /// Map a werbolg-literal into a L type that will be used during execution pub literal_mapper: fn(Span, Literal) -> Result, + + /// Constructor for a possible sequence of expression (list or array), that + /// take usize argument from the stack + pub sequence_constructor: Option, } diff --git a/werbolg-core/src/ir.rs b/werbolg-core/src/ir.rs index 9fe2cf6..55a8dbc 100644 --- a/werbolg-core/src/ir.rs +++ b/werbolg-core/src/ir.rs @@ -160,8 +160,8 @@ pub enum Expr { /// either by disambiguating at the frontend level by adding explicit struct name /// or by other methods Field(Box, Spanned, Spanned), - /// A List expression - List(Span, Vec), + /// A Sequence of expressions + Sequence(Span, Vec), /// A Let binding of the form `let $binder = $expr in $expr` Let(Binder, Box, Box), /// An anonymous function definition expression, e.g. `|a| ...` or `\x -> ...` diff --git a/werbolg-example1/src/main.rs b/werbolg-example1/src/main.rs index 9f494db..01a9a06 100644 --- a/werbolg-example1/src/main.rs +++ b/werbolg-example1/src/main.rs @@ -207,7 +207,10 @@ fn main() -> Result<(), ()> { add_pure_nif!(env, "table_new", nif_hashtable); add_pure_nif!(env, "table_get", nif_hashtable_get); - let compilation_params = werbolg_compile::CompilationParams { literal_mapper }; + let compilation_params = werbolg_compile::CompilationParams { + literal_mapper, + sequence_constructor: None, + }; let exec_module = compile(&compilation_params, modules, &mut env).expect("no compilation error"); diff --git a/werbolg-exec/src/exec.rs b/werbolg-exec/src/exec.rs index fa347c0..6d04081 100644 --- a/werbolg-exec/src/exec.rs +++ b/werbolg-exec/src/exec.rs @@ -63,7 +63,7 @@ use super::allocator::WAllocator; use super::{ExecutionError, ExecutionMachine}; use werbolg_compile::{CallArity, Instruction, InstructionAddress, LocalStackSize, TailCall}; use werbolg_core as ir; -use werbolg_core::ValueFun; +use werbolg_core::{NifId, ValueFun}; /// Native Implemented Function pub struct NIF<'m, 'e, A, L, T, V> { @@ -224,6 +224,12 @@ pub fn step<'m, 'e, A: WAllocator, L, T, V: Valuable>( let _ = em.stack.pop_value(); em.ip_next(); } + Instruction::CallNif(nif, arity) => { + let nif_value = process_nif_call(em, *nif, *arity)?; + em.stack.pop_call_nofun(*arity); + em.stack.push_value(nif_value); + em.ip_next() + } Instruction::Call(tc, arity) => { let val = process_call(em, *arity)?; match val { @@ -323,13 +329,7 @@ fn process_call<'m, 'e, A: WAllocator, L, T, V: Valuable>( match fun { ValueFun::Native(nifid) => { - let res = match &em.environ.nifs[nifid].call { - NIFCall::Pure(nif) => { - let (_first, args) = em.stack.get_call_and_args(arity); - nif(&em.allocator, args)? - } - NIFCall::Raw(nif) => nif(em)?, - }; + let res = process_nif_call(em, nifid, arity)?; Ok(CallResult::Value(res)) } ValueFun::Fun(funid) => { @@ -345,3 +345,18 @@ fn process_call<'m, 'e, A: WAllocator, L, T, V: Valuable>( } } } + +fn process_nif_call<'m, 'e, A: WAllocator, L, T, V: Valuable>( + em: &mut ExecutionMachine<'m, 'e, A, L, T, V>, + nifid: NifId, + arity: CallArity, +) -> Result { + let res = match &em.environ.nifs[nifid].call { + NIFCall::Pure(nif) => { + let args = em.stack.get_call_args(arity); + nif(&em.allocator, args)? + } + NIFCall::Raw(nif) => nif(em)?, + }; + Ok(res) +} diff --git a/werbolg-exec/src/lib.rs b/werbolg-exec/src/lib.rs index 3fe4921..cbfec97 100644 --- a/werbolg-exec/src/lib.rs +++ b/werbolg-exec/src/lib.rs @@ -130,6 +130,13 @@ impl ValueStack { self.values.extend_from_slice(args); } + /// Pop a call from the stack + pub fn pop_call_nofun(&mut self, arity: CallArity) { + for _ in 0..(arity.0 as usize) + 1 { + self.values.pop(); + } + } + /// Pop a call from the stack pub fn pop_call(&mut self, arity: CallArity) { for _ in 0..(arity.0 as usize) + 1 { @@ -187,6 +194,12 @@ impl ValueStack { ) } + /// Get the associated arguments with a call + pub fn get_call_args(&self, arity: CallArity) -> &[V] { + let top = self.values.len(); + &self.values[top - (arity.0 as usize)..top] + } + /// Iterate over all values in the stack, starting from the bottom, towards the end pub fn iter_pos(&self) -> impl Iterator { self.values diff --git a/werbolg-lang-lispy/src/lib.rs b/werbolg-lang-lispy/src/lib.rs index ec11ed0..6867b98 100644 --- a/werbolg-lang-lispy/src/lib.rs +++ b/werbolg-lang-lispy/src/lib.rs @@ -293,12 +293,12 @@ fn exprs(span: Span, exprs: Vec>) -> Result { .collect::, _>>()?; if build_list { - Ok(ir::Expr::List(span, params)) + Ok(ir::Expr::Sequence(span, params)) } else { let params = params .into_iter() .filter(|e| match e { - ir::Expr::List(_, e) if e.is_empty() => false, + ir::Expr::Sequence(_, e) if e.is_empty() => false, _ => true, }) .collect(); diff --git a/werbolg-lang-rusty/src/lib.rs b/werbolg-lang-rusty/src/lib.rs index 7a8e877..91fe3f7 100644 --- a/werbolg-lang-rusty/src/lib.rs +++ b/werbolg-lang-rusty/src/lib.rs @@ -47,7 +47,7 @@ fn rewrite_expr(span_expr: &(parse::Expr, parse::Span)) -> ir::Expr { match &span_expr.0 { parse::Expr::Error => todo!(), parse::Expr::Literal(lit) => ir::Expr::Literal(span_expr.1.clone(), lit.clone()), - parse::Expr::List(list) => ir::Expr::List( + parse::Expr::List(list) => ir::Expr::Sequence( span_expr.1.clone(), list.iter().map(|se| rewrite_expr(se)).collect::>(), ), diff --git a/werbolg-tales/src/exec.rs b/werbolg-tales/src/exec.rs index 39aaa50..66418de 100644 --- a/werbolg-tales/src/exec.rs +++ b/werbolg-tales/src/exec.rs @@ -73,6 +73,7 @@ pub fn run_compile<'m, 'e, A>( let compilation_params = werbolg_compile::CompilationParams { literal_mapper: environ::literal_mapper, + sequence_constructor: None, }; let exec_module = match compile(&compilation_params, modules, env) { diff --git a/werbolg-tests/src/lib.rs b/werbolg-tests/src/lib.rs index 0ffd623..b44aaea 100644 --- a/werbolg-tests/src/lib.rs +++ b/werbolg-tests/src/lib.rs @@ -98,7 +98,10 @@ pub fn execute(mod1: werbolg_core::Module) -> Result { add_pure_nif!(environ, "bool_eq", nif_bool_eq); add_pure_nif!(environ, "expect_int", nif_expect_int_eq); add_pure_nif!(environ, "int_eq", nif_int_eq); - let compilation_params = werbolg_compile::CompilationParams { literal_mapper }; + let compilation_params = werbolg_compile::CompilationParams { + literal_mapper, + sequence_constructor: None, + }; let exec_module = comp(&compilation_params, modules, &mut environ).expect("no compilation error"); let ee = ExecutionEnviron::from_compile_environment(environ.finalize());