diff --git a/benchmark.spl b/benchmark.spl new file mode 100644 index 0000000..9edd017 --- /dev/null +++ b/benchmark.spl @@ -0,0 +1,61 @@ +"#time.spl" import + +func main { mega | with args ; + def begin + def end + + "[-] spl benchmark v0.1.0" println + "[0] benchmarking while loop with variable 0..100000" println + "==> bgin at " print unixms dup =begin println + def i 0 =i + while { i 100000 lt } { + i ++ =i + } + "==> done at " print unixms dup =end println + "==> in " print end begin - println + + "[1] benchmarking foreach on 100000" println + "==> bgin at " print unixms dup =begin println + { | pop } 100000 :foreach + "==> done at " print unixms dup =end println + "==> in " print end begin - println + + "[2] benchmarking fast foreach on 100000" println + "==> bgin at " print unixms dup =begin println + { | pop } 100000 :fforeach + "==> done at " print unixms dup =end println + "==> in " print end begin - println + + "[3] benchmarking foreach on Range 100000..200000" println + "==> bgin at " print unixms dup =begin println + 100000 200000 Range:new + :iter + :foreach <{ | with i ; }> + "==> done at " print unixms dup =end println + "==> in " print end begin - println + + "[4] benchmarking manual multiply of 100000 x 5" println + "==> bgin at " print unixms dup =begin println + def i 0 =i + def n 0 =n + while { i 100000 lt } { + i ++ =i + n 5 + =n + } + " -> n = " print n println + "==> done at " print unixms dup =end println + "==> in " print end begin - println + + "[5] benchmarking 10000 array adds" println + "==> bgin at " print unixms dup =begin println + def i 0 =i + def arr 0 anew =arr + while { i 10000 lt } { + i awrap arr aadd =arr + i ++ =i + } + "==> done at " print unixms dup =end println + "==> in " print end begin - println + + 0 +} diff --git a/isbpl.spl b/isbpl.spl index c640f21..4e4c420 100644 --- a/isbpl.spl +++ b/isbpl.spl @@ -1,3 +1,4 @@ +"#time.spl" import native engage-compatibility-mode func # { pop } func puts { print } @@ -5,6 +6,8 @@ func fcall { call } func strconcat { concat } func aget { swap :get } func stoi { _mega } +func stol { _mega } +func _int { _mega } func itos { _str } def argv'pre-isbplmod &argv =argv'pre-isbplmod func argv { @@ -13,6 +16,17 @@ func argv { args nargs 2 0 nargs:len acopy nargs } +func eputs { + print +} +func isbplmod'import-transform { with s ; + "spl: [compat] transforming import " s concat println + s:_char "#" :_char eq if { + "#nop.spl" =s + } + "spl: [compat] transformed to " s concat println + s +} func inc { "isbpl.spl: ISBPL code tried to call inc, an untranslatable function" println pop @@ -21,3 +35,8 @@ func dec { "isbpl.spl: ISBPL code tried to call inc, an untranslatable function" println pop } +func getms { + 0 time +} + +func main { } diff --git a/iter.spl b/iter.spl index 341c3ea..a91720f 100644 --- a/iter.spl +++ b/iter.spl @@ -11,10 +11,11 @@ construct _Iter { arr } foreach { | with callable this ; - def itm - while { this:next dup =itm null eq not } { - itm callable call + !!- + while { !!- this:next dup null eq not } { + !!- callable call } + pop } collect { array | with this ; [ { any | } this:foreach ] diff --git a/src/lexer.rs b/src/lexer.rs index 6e0116a..c72fd19 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -3,7 +3,7 @@ mod compat; use std::{mem, sync::Arc}; use crate::runtime::*; -use compat::match_compat; +use compat::{match_compat, transform_compat}; use readformat::*; #[derive(Debug, PartialEq, Eq)] @@ -57,7 +57,11 @@ fn read_block_dyn( rem = Some(r as u32); } while i < str_words.len() { - let word = str_words[i].to_owned(); + let word = if !compat { + str_words[i].to_owned() + } else { + transform_compat(&str_words[i]) + }; if compat && match_compat(word.to_owned(), str_words, &mut words, &mut i) { continue; } @@ -236,6 +240,15 @@ fn read_block_dyn( } words.push(Word::Key(Keyword::With(vars))); } + "inline-callable" => { + words.push(Word::Key(Keyword::InlineCallable)); + } + "!!-" => { + words.push(Word::Key(Keyword::InlineStart)); + } + "-!!" => { + words.push(Word::Key(Keyword::InlineEnd)); + } x if x == endword => { break; } diff --git a/src/lexer/compat.rs b/src/lexer/compat.rs index ee210a4..d103a39 100644 --- a/src/lexer/compat.rs +++ b/src/lexer/compat.rs @@ -1,5 +1,12 @@ use crate::Word; +pub(crate) fn transform_compat(word: &str) -> String { + match word { + "try" => "catch".to_owned(), + _ => word.to_owned(), + } +} + pub(crate) fn match_compat( word: String, str_words: &[String], @@ -15,6 +22,7 @@ pub(crate) fn match_compat( true } "dec" => { + eprintln!("spl: [compat] transforming dec call"); eprintln!("spl: [compat] transforming dec call"); words.push(Word::Call("--".to_owned(), false, 0)); words.push(Word::Call("=".to_owned() + &str_words[*i - 1], false, 0)); @@ -22,8 +30,9 @@ pub(crate) fn match_compat( true } "include" => { - // TODO: translate some stdlib components? + words.push(Word::Call("isbplmod'import-transform".to_owned(), false, 0)); words.push(Word::Call("import".to_owned(), false, 0)); + *i += 1; true } _ => false, diff --git a/src/runtime.rs b/src/runtime.rs index 7004303..2133ae8 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -470,6 +470,11 @@ impl Stack { r } + pub fn fast_call(&mut self, func: &AFunc) -> OError { + let r = func.to_call.call(self); + r + } + pub fn get_func(&self, name: String) -> Result { let mut frame = self.frames.last().unwrap(); loop { @@ -649,6 +654,21 @@ pub enum Keyword { /// equivalent to dyn-__dump /// example: func main { int | "Hello, world!" dyn-__dump pop 0 } Dump, + /// inline-call + /// + /// Inlines a callable into the current function + /// equivalent to call + InlineCallable, + /// !!- + /// + /// Makes future calls inline, not adding a new stack frame + /// no equivalent + InlineStart, + /// -!! + /// + /// Stops making calls inline + /// no equivalent + InlineEnd, /// def /// /// Defines a variable. @@ -1144,20 +1164,35 @@ impl Words { /// Executes the words. This does *not* create a new frame on the stack. Use [Stack::call] to /// call and create a new frame. pub fn exec(&self, stack: &mut Stack) -> OError { - for word in self.words.clone() { + let mut inline_calls = false; + for word in self.words.iter() { match word { Word::Key(x) => match x { Keyword::Dump => println!("{stack}"), - Keyword::Def(x) => stack.define_var(x), + Keyword::InlineCallable => { + let Value::Func(f) = stack.pop().lock_ro().native.clone() else { + return Err( + stack.error(ErrorKind::InvalidCall("inline-callable".to_owned())) + ); + }; + stack.fast_call(&f)?; + } + Keyword::InlineStart => { + inline_calls = true; + } + Keyword::InlineEnd => { + inline_calls = false; + } + Keyword::Def(x) => stack.define_var(x.to_owned()), Keyword::Func(name, rem, words) => stack.define_func( name.clone(), Arc::new(Func { - ret_count: rem, - to_call: FuncImpl::SPL(words), + ret_count: *rem, + to_call: FuncImpl::SPL(words.to_owned()), origin: stack.get_frame(), run_as_base: false, fname: None, - name, + name: name.to_owned(), }), ), Keyword::Construct(name, fields, methods, is_namespace) => { @@ -1168,14 +1203,14 @@ impl Words { let t = runtime_mut(|mut rt| { rt.make_type(name.clone(), |mut t| { for field in fields { - t.add_property(field, origin.clone())?; + t.add_property(field.to_owned(), origin.clone())?; } t.functions.extend(methods.into_iter().map(|(k, v)| { ( k.clone(), Arc::new(Func { ret_count: v.0, - to_call: FuncImpl::SPL(v.1), + to_call: FuncImpl::SPL(v.1.to_owned()), origin: origin.clone(), run_as_base: false, fname: None, @@ -1187,7 +1222,7 @@ impl Words { }) })?; - let to_set: Object = if is_namespace { + let to_set: Object = if *is_namespace { let mut obj: Object = Value::Null.into(); obj.kind = t.clone(); t.lock_ro().write_into(&mut obj); @@ -1216,13 +1251,14 @@ impl Words { let rstack = &stack; runtime(move |rt| { rt.get_type_by_name(&tb) - .ok_or_else(|| rstack.error(ErrorKind::TypeNotFound(tb)))? + .ok_or_else(|| { + rstack.error(ErrorKind::TypeNotFound(tb.to_owned())) + })? .lock() .parents - .push( - rt.get_type_by_name(&ta) - .ok_or_else(|| rstack.error(ErrorKind::TypeNotFound(ta)))?, - ); + .push(rt.get_type_by_name(&ta).ok_or_else(|| { + rstack.error(ErrorKind::TypeNotFound(ta.to_owned())) + })?); Ok(()) })?; } @@ -1271,7 +1307,7 @@ impl Words { for var in vars.into_iter().rev() { stack.define_var(var.clone()); let obj = stack.pop(); - stack.set_var(var, obj)?; + stack.set_var(var.to_owned(), obj)?; } } Keyword::ObjPush => { @@ -1295,7 +1331,7 @@ impl Words { origin: stack.get_frame(), run_as_base: false, fname: None, - name, + name: name.to_owned(), }), ) }), @@ -1307,6 +1343,7 @@ impl Words { stack.push(x.clone().ensure_init(stack).spl()) } Word::Call(x, rem, ra) => { + let ra = *ra; if option_env!("SPLDEBUG").is_some() { println!("CALL({}) {x}", stack.len()); } @@ -1333,8 +1370,12 @@ impl Words { } stack.push(f.spl()); } else { - stack.call(&f)?; - if rem { + if inline_calls { + stack.fast_call(&f)?; + } else { + stack.call(&f)?; + } + if *rem { for _ in 0..f.ret_count { stack.pop(); } @@ -1342,6 +1383,7 @@ impl Words { } } Word::ObjCall(x, rem, ra) => { + let ra = *ra; let o = stack.peek(); let o = o.lock_ro(); let f0 = o.kind.lock_ro(); @@ -1379,8 +1421,12 @@ impl Words { stack.pop(); stack.push(f.spl()) } else { - stack.call(&f)?; - if rem { + if inline_calls { + stack.fast_call(&f)?; + } else { + stack.call(&f)?; + } + if *rem { for _ in 0..f.ret_count { stack.pop(); } diff --git a/src/sasm.rs b/src/sasm.rs index a62b977..c293fb0 100644 --- a/src/sasm.rs +++ b/src/sasm.rs @@ -24,6 +24,9 @@ fn sasm_parse<'a>(line: &str, words: &mut Vec, lines: &mut impl Iterator = line.split(" ").collect(); match line[0] { "dump" => words.push(Word::Key(Keyword::Dump)), + "inline-callable" => words.push(Word::Key(Keyword::InlineCallable)), + "inline-start" => words.push(Word::Key(Keyword::InlineStart)), + "inline-end" => words.push(Word::Key(Keyword::InlineEnd)), "def" => words.push(Word::Key(Keyword::Def(line[1].to_owned()))), "func" => words.push(Word::Key(Keyword::Func( line[1].to_owned(), @@ -223,6 +226,9 @@ fn sasm_write_func(words: Words) -> String { Keyword::Dump => { output += "dump\n"; } + Keyword::InlineCallable => output += "inline-callable\n", + Keyword::InlineStart => output += "inline-start\n", + Keyword::InlineEnd => output += "inline-end\n", Keyword::Def(x) => { output += "def "; output += &x; diff --git a/src/std_fns.rs b/src/std_fns.rs index d1ccaeb..5c87e79 100644 --- a/src/std_fns.rs +++ b/src/std_fns.rs @@ -8,6 +8,7 @@ use std::{ process::{self, Stdio}, sync::Arc, thread, + time::{Duration, SystemTime}, }; use crate::{dyn_fns, mutex::Mut, runtime::*, sasm::sasm_write, *}; @@ -51,6 +52,13 @@ pub fn dup(stack: &mut Stack) -> OError { Ok(()) } +pub fn dup2(stack: &mut Stack) -> OError { + let o = stack.peek(); + stack.push(o.clone()); + stack.push(o); + Ok(()) +} + pub fn pop(stack: &mut Stack) -> OError { stack.pop(); Ok(()) @@ -854,11 +862,27 @@ pub fn fork(stack: &mut Stack) -> OError { Ok(()) } +pub fn time(stack: &mut Stack) -> OError { + require_on_stack!(sleep_ms, Mega, stack, "time"); + if sleep_ms != 0 { + thread::sleep(Duration::from_millis(sleep_ms as u64)); + } + stack.push( + (SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_millis() as i128) + .spl(), + ); + Ok(()) +} + pub fn register(r: &mut Stack, o: Arc) { type Fn = fn(&mut Stack) -> OError; - let fns: [(&str, Fn, u32); 54] = [ + let fns: [(&str, Fn, u32); 56] = [ ("pop", pop, 0), ("dup", dup, 2), + ("dup2", dup2, 3), ("clone", clone, 1), ("swap", swap, 2), ("mswap", mswap, 2), @@ -911,6 +935,7 @@ pub fn register(r: &mut Stack, o: Arc) { ("write-sasm", write_sasm, 1), ("write-file-sasm", write_file_sasm, 1), ("fork", fork, 0), + ("time", time, 0), ]; for f in fns { r.define_func( diff --git a/src/stdlib.rs b/src/stdlib.rs index 6a1e8a7..3ceedf3 100644 --- a/src/stdlib.rs +++ b/src/stdlib.rs @@ -11,6 +11,8 @@ pub const ASSEMBLE: &str = include_str!("../assemble.spl"); pub const ISBPL: &str = include_str!("../isbpl.spl"); pub const REPL: &str = include_str!("../repl.spl"); pub const PURE: &str = include_str!("../pure.spl"); +pub const TIME: &str = include_str!("../time.spl"); +pub const NOP: &str = ""; pub fn register(runtime: &mut Runtime) { multicall! { @@ -25,5 +27,7 @@ pub fn register(runtime: &mut Runtime) { insert("isbpl.spl", ISBPL); insert("repl.spl", REPL); insert("pure.spl", PURE); + insert("time.spl", TIME); + insert("nop.spl", NOP); } } diff --git a/std.spl b/std.spl index faa9d6e..4158568 100644 --- a/std.spl +++ b/std.spl @@ -12,7 +12,7 @@ func print { | } func println { | - "\n" concat print + !!- _str "\n" concat print } construct error { @@ -96,7 +96,10 @@ construct _mega-ext { mswap { .. | mswap } foreach { | with callable this ; def i 0 =i - while { i this lt } { i callable call i ++ =i } + while { !!- i this lt } { !!- i callable call i 1 + =i } + } + fforeach { | with callable this ; + 0 while { !!- dup2 this lt } { !!- callable inline-callable 1 + } } } include _mega-ext in mega @@ -368,16 +371,21 @@ construct Range { } construct RangeIter { - range + upper step idx ; construct { this | with range this ; - range this:=range - 0 this:=idx + range:lower this:=idx + range:upper this:=upper + range:step this:=step this } next { mega | with this ; - this:idx dup ++ this:=idx this:range:item + !!- + this:idx dup this:step + this:=idx + dup this:upper lt not if { + pop null + } } } diff --git a/time.spl b/time.spl new file mode 100644 index 0000000..d444fd0 --- /dev/null +++ b/time.spl @@ -0,0 +1,6 @@ + +func unixms { mega | + 0 time +} + +func sleep { mega | time }