diff --git a/crsn/src/asm/data/literal.rs b/crsn/src/asm/data/literal.rs index 7bf98e5..54bc73d 100644 --- a/crsn/src/asm/data/literal.rs +++ b/crsn/src/asm/data/literal.rs @@ -103,25 +103,13 @@ impl From for Label { } } -/// Routine name +/// Routine name, including the routine's arity #[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct RoutineName(pub String); +pub struct RoutineName { pub name: String, pub arity: u8 } impl Display for RoutineName { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl From<&str> for RoutineName { - fn from(n: &str) -> Self { - Self(n.to_string()) - } -} - -impl From for RoutineName { - fn from(n: String) -> Self { - Self(n) + write!(f, "{}/{}", self.name, self.arity) } } diff --git a/crsn/src/asm/error.rs b/crsn/src/asm/error.rs index 8ee60d4..53077b8 100644 --- a/crsn/src/asm/error.rs +++ b/crsn/src/asm/error.rs @@ -5,6 +5,7 @@ use thiserror::Error; use crate::asm::data::{Mask, Register}; use crate::asm::data::literal::Label; use crate::asm::instr::Cond; +use std::num::ParseIntError; /// csn_asm unified error type #[derive(Error, Debug)] @@ -23,6 +24,12 @@ pub enum CrsnError { Other(#[from] anyhow::Error), } +impl From for CrsnError { + fn from(e: ParseIntError) -> Self { + CrsnError::Other(anyhow::anyhow!(e)) + } +} + /// Error from the assembler stage (after parsing S-expressions and basic validation) #[derive(Error, Debug)] pub enum AsmError { diff --git a/crsn/src/asm/parse/parse_routine.rs b/crsn/src/asm/parse/parse_routine.rs index c2a5be2..7ead555 100644 --- a/crsn/src/asm/parse/parse_routine.rs +++ b/crsn/src/asm/parse/parse_routine.rs @@ -8,11 +8,23 @@ use crate::asm::patches::SexpIsA; use crate::asm::parse::parse_data::parse_reg_alias; use crate::asm::data::Register; use crate::asm::data::literal::RoutineName; +use crate::builtin::parse::parse_routine_name; pub fn parse_routine(mut toki: impl Iterator + Clone, pcx: &ParserContext) -> Result, CrsnError> { let name = expect_string_atom(toki.next())?; + let mut name = parse_routine_name(name)?; let arg_tokens = TokenParser::new(toki.clone().take_while(|e| e.is_atom()).collect(), pcx); + + // If arity is explicitly given, then either no named argument must be provided, + // or their count must match the arity. If no arity is given, then arity is determined + // by the number of named arguments. + if name.arity == 0 && arg_tokens.len() != 0 { + name.arity = arg_tokens.len() as u8; + } else if arg_tokens.len() != 0 && name.arity as usize != arg_tokens.len() { + return Err(CrsnError::Parse(format!("arity mismatch in routine {}", name.name).into())); + } + let toki = toki.skip_while(|e| e.is_atom()); { @@ -41,7 +53,7 @@ pub fn parse_routine(mut toki: impl Iterator + Clone, pcx: &ParserCon } return Ok(Box::new(Routine { - name: RoutineName(name), + name, body, })); } diff --git a/crsn/src/builtin/parse.rs b/crsn/src/builtin/parse.rs index 6880f48..aec0d67 100644 --- a/crsn/src/builtin/parse.rs +++ b/crsn/src/builtin/parse.rs @@ -120,7 +120,7 @@ impl CrsnExtension for BuiltinOps { } "call" => { - let dest = RoutineName(args.next_string()?); + let dest = RoutineName { name: args.next_string()?, arity: args.len() as u8 }; let mut call_args = vec![]; for t in args { @@ -138,8 +138,8 @@ impl CrsnExtension for BuiltinOps { } "routine" => { - let dest = RoutineName(args.next_string()?); - BuiltinOp::Routine(dest) + let name = args.next_string()?; + BuiltinOp::Routine(parse_routine_name(name)?) } "sk" => { @@ -227,3 +227,16 @@ impl CrsnExtension for BuiltinOps { }))) } } + +pub(crate) fn parse_routine_name(name : String) -> Result { + let (name, arity) = if let Some(n) = name.find('/') { + ( + (&name[0..n]).to_string(), + (&name[(n+1)..]).parse::()? + ) + } else { + (name, 0) + }; + + Ok(RoutineName { name, arity }) +} diff --git a/examples/arities.csn b/examples/arities.csn new file mode 100644 index 0000000..bc23999 --- /dev/null +++ b/examples/arities.csn @@ -0,0 +1,48 @@ +( + (call slash 1) + (cmp res0 1 (ne? (fault))) + + (call named 2) + (cmp res0 2 (ne? (fault))) + + (call both 3) + (cmp res0 3 (ne? (fault))) + + (call slash 1 2) + (cmp res0 1 (ne? (fault))) + (cmp res1 2 (ne? (fault))) + + (call named 2 3) + (cmp res0 2 (ne? (fault))) + (cmp res1 3 (ne? (fault))) + + (call both 3 4) + (cmp res0 3 (ne? (fault))) + (cmp res1 4 (ne? (fault))) + + (halt) + + (proc slash/1 + (ret arg0) + ) + + (proc slash/2 + (ret arg0 arg1) + ) + + (proc named a + (ret a) + ) + + (proc named a b + (ret a b) + ) + + (proc both/1 a + (ret a) + ) + + (proc both/2 a b + (ret a b) + ) +) diff --git a/examples/factorial.csn b/examples/factorial.csn index 27073a1..3f92d35 100644 --- a/examples/factorial.csn +++ b/examples/factorial.csn @@ -3,7 +3,7 @@ (ld r0 res0) (halt) - (proc fac + (proc fac/1 (cmp arg0 2 (eq? (ret 2))) (sub r0 arg0 1)