From 215b6f2748b438771b8a0c00edc648d366939e76 Mon Sep 17 00:00:00 2001 From: TudbuT Date: Fri, 15 Nov 2024 11:54:54 +0100 Subject: [PATCH] matching --- spl/std.spl | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lexer.rs | 42 +++++++++++++++++++++++++++++ src/runtime.rs | 30 ++++++++++++++++++--- test.spl | 44 ++++++++++++++++++++++++++++++ 4 files changed, 184 insertions(+), 4 deletions(-) diff --git a/spl/std.spl b/spl/std.spl index 66aeb74..abe0e3f 100644 --- a/spl/std.spl +++ b/spl/std.spl @@ -644,6 +644,78 @@ func times { | with amount callable ; } } +func check-match { bool | with input output ; + output gettype "func" eq if { + 1 2 stop + } + input output eq if { + 1 2 stop + } + output gettype "array" eq if { + def i, n + 0 =i + output:len =n + input:len n eq not if { 0 2 stop } + 1 while { i n lt } { + input:get output:get check-match and + i ++ =i + } + 2 stop + } + 0 +} + +func match-unchecked { | with input output ; + output gettype "func" eq if { + input output call + 2 stop + } + output gettype "array" eq if { + def i, n + 0 =i + output:len =n + input:len n eq not if { 2 stop } + while { i n lt } { + input:get output:get match-unchecked + i ++ =i + } + } +} + +func match { bool | with input output ; + output gettype "func" eq if { + input output call + 1 2 stop + } + input output eq if { + 1 2 stop + } + output gettype "array" eq if { + def i, n + 0 =i + output:len =n + input:len n eq not if { 0 2 stop } + 1 while { i n lt } { + input:get output:get check-match and + i ++ =i + } dup if { + 0 =i + while { i n lt } { + input:get output:get match-unchecked + i ++ =i + } + } + 2 stop + } + 0 +} + +func _'match-else-error { | + not if { + "match unsuccessful" throw + } +} + def _'has-been-called 0 =_'has-been-called func _ { | _'has-been-called not if { diff --git a/src/lexer.rs b/src/lexer.rs index 4dfb59e..40bc27a 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -78,6 +78,10 @@ fn read_block_dyn( ); } "def" => { + while let Some(w) = str_words[i + 1].strip_suffix(',') { + words.push(Word::Key(Keyword::Def(w.to_owned()))); + i += 1; + } words.push(Word::Key(Keyword::Def(str_words[i + 1].to_owned()))); i += 1; } @@ -253,6 +257,44 @@ fn read_block_dyn( } words.push(Word::Key(Keyword::With(vars))); } + "=" => { + if str_words[i + 1] == ">" { + i += 1; + let throwing = if str_words[i + 1] == "?" { + i += 1; + false + } else { + true + }; + if str_words[i + 1] == "[" { + i += 1; + let mut block = + read_block_dyn(&str_words[i + 1..], false, "]".to_owned(), compat)?; + i += block.2 + 1; + words.push(Word::Call("[".to_owned(), false, 0)); + words.append(&mut block.1.words); + words.push(Word::Call("]".to_owned(), false, 0)); + } else { + words.append( + &mut read_block_dyn( + &[str_words[i + 1].clone()], + false, + "".to_owned(), + false, + )? + .1 + .words, + ); + i += 1; + } + words.push(Word::Call("match".to_owned(), false, 0)); + if throwing { + words.push(Word::Call("_'match-else-error".to_owned(), false, 0)); + } + } else { + words.push(Word::Call("=".to_owned(), false, 0)); + } + } "inline-callable" => { words.push(Word::Key(Keyword::InlineCallable)); } diff --git a/src/runtime.rs b/src/runtime.rs index 45d0ea4..0fa00a8 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -525,7 +525,7 @@ impl Stack { ret_count: 1, origin: Arc::new(Frame::dummy()), to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| { - stack.push(stack.get_frame().get_var(tmpname.clone(), stack)?); + stack.push(stack.get_var(tmpname.clone())?); Ok(()) }))), run_as_base: false, @@ -541,7 +541,7 @@ impl Stack { origin: Arc::new(Frame::dummy()), to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| { let v = stack.pop(); - stack.get_frame().set_var(tmpname.clone(), v, stack) + stack.set_var(tmpname.clone(), v) }))), run_as_base: false, fname: Some("RUNTIME".to_owned()), @@ -570,11 +570,33 @@ impl Stack { } pub fn set_var(&self, name: String, obj: AMObject) -> OError { - self.get_frame().set_var(name, obj, self) + if let Err(x) = self.get_frame().set_var(name.clone(), obj.clone(), self) { + for i in 1..self.frames.len() { + if self + .peek_frame(i) + .set_var(name.clone(), obj.clone(), self) + .is_ok() + { + return Ok(()); + } + } + return Err(x); + } + Ok(()) } pub fn get_var(&self, name: String) -> Result { - self.get_frame().get_var(name, self) + match self.get_frame().get_var(name.clone(), self) { + Err(x) => { + for i in 1..self.frames.len() { + if let Ok(x) = self.peek_frame(i).get_var(name.clone(), self) { + return Ok(x); + } + } + Err(x) + } + Ok(x) => Ok(x), + } } pub fn push(&mut self, obj: AMObject) { diff --git a/test.spl b/test.spl index 94262f0..4226ac0 100644 --- a/test.spl +++ b/test.spl @@ -285,6 +285,50 @@ func main { int | with args ; 5 list:insert;<5> list:iter:join<", "> println; + + ^ println + "testing match" println + + "a -> b (0): " print "a" "b" match _str println + "a -> a (1): " print "a" "a" match _str println + "[a b] -> [a a] (0): " print [ "a" "b" ] [ "a" "a" ] match _str println + "[a b] -> [a b] (1): " print [ "a" "b" ] [ "a" "b" ] match _str println + + def mtesta, mtestb + + "a => =mtesta (1): " print "a" &=mtesta match _str println + "[a b] => [a =mtesta] (1): " print [ "a" "b" ] [ "a" &=mtesta ] match _str println + "[b a] => [a =mtesta] (0): " print [ "b" "a" ] [ "a" &=mtesta ] match _str println + "[a b] => [=mtesta a] (0): " print [ "b" "a" ] [ "a" &=mtesta ] match _str println + "-> mtesta = (b) " mtesta _str concat println + + "^ok => ^ok (1): " print ^ok =>? ^ok _str println + "^bad => ^ok (0): " print ^bad =>? ^ok _str println + "[^bad a] => [^ok =mtestb] (0): " print [ ^bad "a" ] =>? [ ^ok &=mtestb ] _str println + "[^ok b] => [^ok =mtestb] (1): " print [ ^ok "b" ] =>? [ ^ok &=mtestb ] _str println + + "-> mtestb = (b) " mtestb _str concat println + + def result, val + + [ ^ok "hello, world" ] =result + + result => [ ^ok &=val ] + val println + + [ ^error "bad" ] =result + + catch Custom { + result => [ ^ok &println ] + } { with e ; + e:message "match unsuccessful" eq if { + "err value: " print + result => [ ^error &println ] + } + } + + + 5 :foreach<{ | pop "" println }>