From 98f39a5aa350b708d1e37b1eb36271e2c147b641 Mon Sep 17 00:00:00 2001 From: TudbuT Date: Sun, 19 Feb 2023 19:34:25 +0100 Subject: [PATCH] improved error handling, importing, more stdlib functions --- Cargo.lock | 7 + Cargo.toml | 1 + src/dyn_fns.rs | 3 + src/lexer.rs | 13 +- src/lib.rs | 7 +- src/main.rs | 38 +--- src/mutex.rs | 16 +- src/runtime.rs | 254 ++++++++++++++++++++------- src/std_fns.rs | 190 ++++++++++++++++++-- src/stream.rs | 177 +++++++++++++++++++ std.spl | 465 +++++++++++++++++++++++++++++++++++++++++++++++++ test.spl | 87 +++++++-- 12 files changed, 1128 insertions(+), 130 deletions(-) create mode 100644 src/stream.rs create mode 100644 std.spl diff --git a/Cargo.lock b/Cargo.lock index 6181c5b..07f5430 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + [[package]] name = "readformat" version = "0.1.2" @@ -12,5 +18,6 @@ checksum = "b03f7fbd470aa8b3ad163c85cce8bccfc11cc9c44ef12da0a4eddd98bd307352" name = "spl" version = "0.1.0" dependencies = [ + "once_cell", "readformat", ] diff --git a/Cargo.toml b/Cargo.toml index b21276e..046cdc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] readformat = "0.1" +once_cell = "1.17" diff --git a/src/dyn_fns.rs b/src/dyn_fns.rs index 80028b1..a748eba 100644 --- a/src/dyn_fns.rs +++ b/src/dyn_fns.rs @@ -198,6 +198,7 @@ pub fn dyn_read(stack: &mut Stack) -> OError { kind: ErrorKind::LexError(format!("{x:?}")), stack: stack.trace(), })?), + run_at_base: false, origin, fname: None, name: "(dyn-read)".to_owned(), @@ -225,6 +226,7 @@ pub fn dyn_readf(stack: &mut Stack) -> OError { kind: ErrorKind::LexError(format!("{x:?}")), stack: stack.trace(), })?), + run_at_base: true, origin, fname: Some(n), name: "(dyn-read)".to_owned(), @@ -258,6 +260,7 @@ pub fn register(r: &mut Stack, o: Arc) { AFunc::new(Func { ret_count: f.2, to_call: FuncImpl::Native(f.1), + run_at_base: false, origin: o.clone(), fname: None, name: f.0.to_owned(), diff --git a/src/lexer.rs b/src/lexer.rs index c6facfa..2750ed2 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -56,6 +56,7 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option, Words, u words.push(Word::Const(Value::Func(AFunc::new(Func { ret_count: block.0.ok_or(LexerError::FunctionBlockExpected)?, to_call: FuncImpl::SPL(block.1), + run_at_base: false, origin: Arc::new(Frame::dummy()), fname: None, name: "dyn".to_owned(), @@ -136,14 +137,20 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option, Words, u x if x.starts_with('\"') => { words.push(Word::Const(Value::Str(x[1..].to_owned()))); } - x if x.chars().all(|c| c.is_numeric() || c == '_') && !x.starts_with('_') => { + x if x.chars().all(|c| c.is_numeric() || c == '_' || c == '-') + && !x.starts_with('_') + && x.contains(char::is_numeric) => + { words.push(Word::Const(Value::Mega( x.parse() .map_err(|_| LexerError::InvalidNumber(x.to_owned()))?, ))); } - x if x.chars().all(|c| c.is_numeric() || c == '.' || c == '_') - && !x.starts_with('_') => + x if x + .chars() + .all(|c| c.is_numeric() || c == '.' || c == '_' || c == '-') + && !x.starts_with('_') + && x.contains(char::is_numeric) => { words.push(Word::Const(Value::Double( x.parse() diff --git a/src/lib.rs b/src/lib.rs index 8e67ced..5c17a2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,8 @@ -pub(crate) mod dyn_fns; +#![allow(clippy::type_complexity)] +#![allow(clippy::len_without_is_empty)] +pub mod dyn_fns; pub mod lexer; pub mod mutex; pub mod runtime; -pub(crate) mod std_fns; +pub mod std_fns; +pub mod stream; diff --git a/src/main.rs b/src/main.rs index 12a7e6d..83dd9e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use spl::{lexer::lex, runtime::*}; -use std::{env::args, fs}; +use std::fs; fn main() -> OError { let rt = Runtime::new(); @@ -9,42 +9,6 @@ fn main() -> OError { file: "std.spl".to_owned(), function: "root".to_owned(), }); - fn argv(stack: &mut Stack) -> OError { - stack.push(Value::Array(args().into_iter().map(|x| Value::Str(x).spl()).collect()).spl()); - Ok(()) - } - fn read_file(stack: &mut Stack) -> OError { - let Value::Str(s) = stack.pop().lock_ro().native.clone() else { - return stack.err(ErrorKind::InvalidCall("read_file".to_owned())) - }; - stack.push( - Value::Str( - fs::read_to_string(s).map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?, - ) - .spl(), - ); - Ok(()) - } - stack.define_func( - "argv".to_owned(), - AFunc::new(Func { - ret_count: 1, - to_call: FuncImpl::Native(argv), - origin: stack.get_frame(), - fname: None, - name: "argv".to_owned(), - }), - ); - stack.define_func( - "read-file".to_owned(), - AFunc::new(Func { - ret_count: 1, - to_call: FuncImpl::Native(read_file), - origin: stack.get_frame(), - fname: None, - name: "read-file".to_owned(), - }), - ); let words = lex(fs::read_to_string("std.spl").unwrap()).map_err(|x| Error { kind: ErrorKind::LexError(format!("{x:?}")), stack: Vec::new(), diff --git a/src/mutex.rs b/src/mutex.rs index a2e33cc..24495ca 100644 --- a/src/mutex.rs +++ b/src/mutex.rs @@ -1,10 +1,13 @@ -use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use std::{ + fmt::Display, + sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}, +}; #[derive(Debug)] pub struct Mut(RwLock); impl Mut { - pub fn new(obj: T) -> Mut { + pub const fn new(obj: T) -> Mut { Mut(RwLock::new(obj)) } @@ -17,6 +20,15 @@ impl Mut { } } +impl Display for Mut +where + T: Display, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.read().unwrap().fmt(f) + } +} + impl Clone for Mut where T: Clone, diff --git a/src/runtime.rs b/src/runtime.rs index 800b5fc..7986ba2 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,7 +1,11 @@ -use crate::{dyn_fns, mutex::*, std_fns}; +use crate::{ + dyn_fns, + mutex::*, + std_fns, + stream::{self, *}, +}; use core::panic; -use std::collections::VecDeque; use std::mem; use std::sync::RwLockWriteGuard; use std::{ @@ -11,6 +15,7 @@ use std::{ sync::Arc, vec, }; +use std::{collections::VecDeque, thread::panicking}; pub type AMObject = Arc>; pub type AMType = Arc>; @@ -35,6 +40,19 @@ pub struct Runtime { next_type_id: u32, types_by_name: HashMap, types_by_id: HashMap, + next_stream_id: u128, + streams: HashMap>>, +} + +impl Debug for Runtime { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Runtime") + .field("next_type_id", &self.next_type_id) + .field("types_by_name", &self.types_by_name) + .field("types_by_id", &self.types_by_id) + .field("next_stream_id", &self.next_stream_id) + .finish() + } } impl Default for Runtime { @@ -49,6 +67,8 @@ impl Runtime { next_type_id: 0, types_by_name: HashMap::new(), types_by_id: HashMap::new(), + next_stream_id: 0, + streams: HashMap::new(), }; let _ = rt.make_type("null".to_owned(), Ok); // infallible let _ = rt.make_type("int".to_owned(), Ok); // infallible @@ -91,6 +111,20 @@ impl Runtime { Ok(t) } + pub fn register_stream(&mut self, stream: Stream) -> (u128, Arc>) { + let id = (self.next_stream_id, self.next_stream_id += 1).0; + self.streams.insert(id, Arc::new(Mut::new(stream))); + (id, self.streams.get(&id).unwrap().clone()) + } + + pub fn get_stream(&self, id: u128) -> Option>> { + self.streams.get(&id).cloned() + } + + pub fn destroy_stream(&mut self, id: u128) { + self.streams.remove(&id); + } + pub fn reset() { RUNTIME.with(|x| *x.borrow_mut() = None); } @@ -112,18 +146,19 @@ impl SetRuntime for Arc> { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FrameInfo { pub file: String, pub function: String, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Frame { parent: Option>, pub variables: Mut>, pub functions: Mut>, pub origin: FrameInfo, + pub redirect_to_base: bool, } impl Display for Frame { @@ -136,6 +171,7 @@ impl Display for Frame { std::fmt::Display::fmt(&object.lock_ro(), f)?; f.write_str("\n")?; } + f.write_str("\n")?; Ok(()) } } @@ -150,6 +186,7 @@ impl Frame { file: "\0".to_owned(), function: "\0".to_owned(), }, + redirect_to_base: false, } } @@ -162,6 +199,7 @@ impl Frame { file: "RUNTIME".to_owned(), function: "root".to_owned(), }, + redirect_to_base: false, } } @@ -171,6 +209,7 @@ impl Frame { variables: Mut::new(HashMap::new()), functions: Mut::new(HashMap::new()), origin: info, + redirect_to_base: false, } } @@ -183,10 +222,16 @@ impl Frame { ..parent.origin.clone() }, parent: Some(parent), + redirect_to_base: false, } } - pub fn new_in(parent: Arc, origin: String, function: String) -> Self { + pub fn new_in( + parent: Arc, + origin: String, + function: String, + redirect_to_parent: bool, + ) -> Self { Frame { parent: Some(parent), variables: Mut::new(HashMap::new()), @@ -195,6 +240,7 @@ impl Frame { file: origin, function, }, + redirect_to_base: redirect_to_parent, } } @@ -248,24 +294,43 @@ impl Frame { r } + pub fn readable_path(&self) -> String { + let mut item = String::new(); + let path = self.path(); + let mut file = "\0".to_owned(); + for element in path { + if element.file != file { + item += " | in "; + item += &element.file; + item += ":"; + file = element.file; + } + item += " "; + item += &element.function; + } + item + } + pub fn is_dummy(&self) -> bool { self.parent.is_none() && self.origin.file == "\0" && self.origin.function == "\0" } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Stack { frames: Vec>, object_stack: Vec, + pub return_accumultor: u32, } impl Display for Stack { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { for frame in &self.frames { - f.write_str("Frame: ")?; - f.write_str(&frame.origin.file)?; + f.write_str("Frame:")?; + f.write_str(&frame.readable_path())?; + f.write_str("\n")?; + std::fmt::Display::fmt(&frame.as_ref(), f)?; f.write_str("\n\n")?; - frame.as_ref().fmt(f)?; } f.write_str("Stack: \n")?; for object in &self.object_stack { @@ -289,10 +354,12 @@ impl Stack { let mut r = Stack { frames: vec![o.clone()], object_stack: Vec::new(), + return_accumultor: 0, }; dyn_fns::register(&mut r, o.clone()); - std_fns::register(&mut r, o); + std_fns::register(&mut r, o.clone()); + stream::register(&mut r, o); r } @@ -302,28 +369,35 @@ impl Stack { let mut r = Stack { frames: vec![o.clone()], object_stack: Vec::new(), + return_accumultor: 0, }; dyn_fns::register(&mut r, o.clone()); - std_fns::register(&mut r, o); + std_fns::register(&mut r, o.clone()); + stream::register(&mut r, o); r } pub fn define_func(&mut self, name: String, func: AFunc) { - self.frames - .last_mut() - .unwrap() - .functions - .lock() - .insert(name, func); + let mut frame = self.frames.last().unwrap().clone(); + if frame.redirect_to_base { + frame = self.frames.first().unwrap().clone(); + } + frame.functions.lock().insert(name, func); } pub fn call(&mut self, func: &AFunc) -> OError { - let mut f = Frame::new(func.origin.clone(), func.name.clone()); - if let Some(ref cname) = func.fname { - f.origin.file = cname.clone(); - } + let f = if let Some(ref cname) = func.fname { + Frame::new_in( + func.origin.clone(), + cname.clone(), + func.name.clone(), + func.run_at_base, + ) + } else { + Frame::new(func.origin.clone(), func.name.clone()) + }; self.frames.push(Arc::new(f)); func.to_call.call(self)?; self.frames.pop().unwrap(); @@ -349,7 +423,10 @@ impl Stack { } pub fn define_var(&mut self, name: String) { - let frame = self.frames.last_mut().unwrap().clone(); + let mut frame = self.frames.last().unwrap().clone(); + if frame.redirect_to_base { + frame = self.frames.first().unwrap().clone(); + } let tmpname = name.clone(); let tmpframe = frame.clone(); frame.functions.lock().insert( @@ -361,6 +438,7 @@ impl Stack { stack.push(tmpframe.get_var(tmpname.clone(), stack)?); Ok(()) }))), + run_at_base: false, fname: Some("RUNTIME".to_owned()), name: name.clone(), }), @@ -376,6 +454,7 @@ impl Stack { let v = stack.pop(); tmpframe.set_var(tmpname.clone(), v, stack) }))), + run_at_base: false, fname: Some("RUNTIME".to_owned()), name: "=".to_owned() + &name, }), @@ -383,6 +462,19 @@ impl Stack { frame.variables.lock().insert(name, Value::Null.spl()); } + pub fn pop_until(&mut self, obj: AMObject) -> Vec { + let Some((idx, ..)) = self.object_stack.iter().enumerate().rfind(|o| *o.1.lock_ro() == *obj.lock_ro()) else { + return Vec::new() + }; + let items = self.object_stack[idx + 1..].to_vec(); + self.object_stack = self.object_stack[0..idx].to_vec(); + items + } + + pub fn len(&self) -> usize { + self.object_stack.len() + } + pub fn set_var(&self, name: String, obj: AMObject) -> OError { self.get_frame().set_var(name, obj, self) } @@ -435,22 +527,7 @@ impl Stack { pub fn trace(&self) -> Vec { self.frames .iter() - .map(|frame| { - let mut item = String::new(); - let path = frame.path(); - let mut file = "\0".to_owned(); - for element in path { - if element.file != file { - item += " | in "; - item += &element.file; - item += ":"; - file = element.file; - } - item += " "; - item += &element.function; - } - item - }) + .map(|frame| frame.readable_path()) .collect() } @@ -461,10 +538,16 @@ impl Stack { .clone() } + /// # Safety + /// This function is not technically unsafe. It is marked this way to indicate that it + /// can cause unstable runtime states and panics. However, memory safety is still guaranteed. pub unsafe fn pop_frame(&mut self, index: usize) -> Arc { self.frames.remove(self.frames.len() - index - 1) } + /// # Safety + /// This function is not technically unsafe. It is marked this way to indicate that it + /// can cause unstable runtime states and panics. However, memory safety is still guaranteed. pub unsafe fn push_frame(&mut self, frame: Arc) { self.frames.push(frame); } @@ -576,7 +659,6 @@ pub struct Words { } #[derive(Clone)] -#[allow(clippy::type_complexity)] pub enum FuncImpl { Native(fn(&mut Stack) -> OError), NativeDyn(Arc OError>>), @@ -597,6 +679,7 @@ impl FuncImpl { pub struct Func { pub ret_count: u32, pub to_call: FuncImpl, + pub run_at_base: bool, pub origin: Arc, pub fname: Option, pub name: String, @@ -653,6 +736,18 @@ impl Type { None } + pub fn write_into(&self, object: &mut Object) { + let mut to_apply = self.properties.clone(); + let mut q = VecDeque::from(self.parents.clone()); + while let Some(t) = q.pop_front() { + to_apply.append(&mut t.lock_ro().properties.clone()); + q.append(&mut VecDeque::from(t.lock_ro().parents.clone())); + } + for property in to_apply.into_iter().rev() { + object.property_map.insert(property, Value::Null.spl()); + } + } + pub fn add_property(&mut self, name: String, origin: Arc) -> OError { let tmpname = name.clone(); self.functions.insert( @@ -675,6 +770,7 @@ impl Type { ); Ok(()) }))), + run_at_base: false, origin: origin.clone(), fname: Some("RUNTIME".to_owned()), name: name.clone(), @@ -691,6 +787,7 @@ impl Type { o.lock().property_map.insert(tmpname.clone(), v); Ok(()) }))), + run_at_base: false, origin, fname: Some("RUNTIME".to_owned()), name: "=".to_owned() + &name, @@ -732,11 +829,13 @@ impl Display for Object { f.write_str(&self.kind.lock_ro().name)?; f.write_str("(")?; self.native.fmt(f)?; - f.write_str(") { ")?; + f.write_str(") {")?; for (k, v) in &self.property_map { + f.write_str(" ")?; f.write_str(k)?; f.write_str(": ")?; std::fmt::Display::fmt(&v.lock_ro(), f)?; + f.write_str(",")?; } f.write_str(" }")?; Ok(()) @@ -745,22 +844,18 @@ impl Display for Object { impl Object { pub fn new(kind: AMType, native: Value) -> Object { - Object { - property_map: { - let mut map = HashMap::new(); - for property in &kind.lock_ro().properties { - map.insert(property.clone(), Value::Null.spl()); - } - map - }, - kind, + let mut r = Object { + property_map: HashMap::new(), + kind: kind.clone(), native, - } + }; + kind.lock_ro().write_into(&mut r); + r } pub fn is_truthy(&self) -> bool { match &self.native { - Value::Null => false, + Value::Null => self.kind.lock_ro().id != 0, Value::Int(x) => x > &0, Value::Long(x) => x > &0, Value::Mega(x) => x > &0, @@ -768,7 +863,7 @@ impl Object { Value::Double(_) => true, Value::Func(_) => true, Value::Array(_) => true, - Value::Str(x) => x.is_empty(), + Value::Str(x) => !x.is_empty(), } } } @@ -820,6 +915,7 @@ impl Words { Arc::new(Func { ret_count: rem, to_call: FuncImpl::SPL(words), + run_at_base: false, origin: stack.get_frame(), fname: None, name, @@ -842,6 +938,7 @@ impl Words { Arc::new(Func { ret_count: v.0, to_call: FuncImpl::SPL(v.1), + run_at_base: false, origin: origin.clone(), fname: None, name: name.clone() + ":" + &k, @@ -861,12 +958,12 @@ impl Words { let rstack = &stack; runtime(move |rt| { rt.get_type_by_name(tb.clone()) - .ok_or_else(|| rstack.error(ErrorKind::TypeNotFound(tb.clone())))? + .ok_or_else(|| rstack.error(ErrorKind::TypeNotFound(tb)))? .lock() .parents .push( - rt.get_type_by_name(ta) - .ok_or_else(|| rstack.error(ErrorKind::TypeNotFound(tb)))?, + rt.get_type_by_name(ta.clone()) + .ok_or_else(|| rstack.error(ErrorKind::TypeNotFound(ta)))?, ); Ok(()) })?; @@ -877,6 +974,9 @@ impl Words { break; } blk.exec(stack)?; + if stack.return_accumultor > 0 { + break; + } }, Keyword::If(blk) => { if stack.pop().lock_ro().is_truthy() { @@ -891,8 +991,16 @@ impl Words { } } }, - Word::Const(x) => stack.push(x.clone().ensure_init(stack).spl()), + Word::Const(x) => { + if option_env!("SPLDEBUG").is_some() { + println!("CNST({}) {x:?}", stack.len()); + } + stack.push(x.clone().ensure_init(stack).spl()) + } Word::Call(x, rem, ra) => { + if option_env!("SPLDEBUG").is_some() { + println!("CALL({}) {x}", stack.len()); + } let f = stack.get_func(x.clone())?; if ra != 0 { let mut f = Value::Func(f); @@ -908,11 +1016,13 @@ impl Words { stack.push(ftmp.clone().spl()); Ok(()) }))), + run_at_base: false, origin: stack.get_frame(), fname: None, name: s + &x, })); } + stack.pop(); stack.push(f.spl()); } else { stack.call(&f)?; @@ -928,13 +1038,15 @@ impl Words { let o = o.lock_ro(); let f0 = o.kind.lock_ro(); let f = f0 - .functions - .get(&x) + .get_fn(x.clone()) .ok_or_else(|| Error { kind: ErrorKind::MethodNotFound(f0.name.clone(), x.clone()), stack: stack.trace(), })? .clone(); + if option_env!("SPLDEBUG").is_some() { + println!("CALL({}) {}:{x}", stack.len(), &o.kind.lock_ro().name); + } mem::drop(f0); mem::drop(o); if ra != 0 { @@ -951,11 +1063,13 @@ impl Words { stack.push(ftmp.clone().spl()); Ok(()) }))), + run_at_base: false, origin: stack.get_frame(), fname: None, name: s + &x, })); } + stack.pop(); stack.push(f.spl()) } else { stack.call(&f)?; @@ -967,6 +1081,10 @@ impl Words { } } } + if stack.return_accumultor > 0 { + stack.return_accumultor -= 1; + return Ok(()); + } } Ok(()) } @@ -988,8 +1106,26 @@ pub enum ErrorKind { CustomObject(AMObject), } -#[derive(Debug, PartialEq, Eq)] +#[derive(PartialEq, Eq)] pub struct Error { pub kind: ErrorKind, pub stack: Vec, } + +impl Debug for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if panicking() { + f.write_str("\n\nSPL PANIC DUE TO UNCAUGHT ERROR:\n")?; + f.write_str(format!("Error: {:?}", self.kind).as_str())?; + f.write_str("\n")?; + f.write_str(self.stack.join("\n").as_str())?; + f.write_str("\n\n")?; + Ok(()) + } else { + f.debug_struct("Error") + .field("kind", &self.kind) + .field("stack", &self.stack) + .finish() + } + } +} diff --git a/src/std_fns.rs b/src/std_fns.rs index 91f42d5..d7de1ef 100644 --- a/src/std_fns.rs +++ b/src/std_fns.rs @@ -1,11 +1,15 @@ use std::{ - io::{stdout, Write}, + collections::VecDeque, + env::{args, var, vars}, + fs, + io::{stdin, stdout, Write}, mem, process, sync::Arc, }; -use crate::{mutex::Mut, runtime::*}; +use crate::{dyn_fns, mutex::Mut, runtime::*}; +#[macro_export] macro_rules! type_err { ($stack:expr, $a:expr, $b:expr) => { $stack.err(ErrorKind::InvalidType($a.to_owned(), $b.to_owned()))? @@ -46,6 +50,21 @@ pub fn swap(stack: &mut Stack) -> OError { Ok(()) } +pub fn mswap(stack: &mut Stack) -> OError { + let Value::Mega(i) = stack.pop().lock_ro().native.clone() else { + return stack.err(ErrorKind::InvalidCall("nswap".to_owned())) + }; + let mut array = VecDeque::with_capacity(i as usize); + for _ in 0..i { + array.push_back(stack.pop()); + } + for _ in 0..i { + // SAFETY: Items must exist because they are added in the previous loop + stack.push(array.pop_front().unwrap()); + } + Ok(()) +} + pub fn settype(stack: &mut Stack) -> OError { let Value::Str(s) = stack.pop().lock_ro().native.clone() else { return stack.err(ErrorKind::InvalidCall("settype".to_owned())) @@ -54,9 +73,7 @@ pub fn settype(stack: &mut Stack) -> OError { let kind = runtime(|rt| rt.get_type_by_name(s.clone())) .ok_or_else(|| stack.error(ErrorKind::TypeNotFound(s)))?; let mut obj = o.lock(); - for property in &kind.lock_ro().properties { - obj.property_map.insert(property.clone(), Value::Null.spl()); - } + kind.lock_ro().write_into(&mut obj); obj.kind = kind; mem::drop(obj); stack.push(o); @@ -139,13 +156,41 @@ pub fn not(stack: &mut Stack) -> OError { Ok(()) } +pub fn and(stack: &mut Stack) -> OError { + let a = stack.pop(); + let b = stack.pop(); + stack.push( + Value::Int(if a.lock_ro().is_truthy() && b.lock_ro().is_truthy() { + 1 + } else { + 0 + }) + .spl(), + ); + Ok(()) +} + +pub fn or(stack: &mut Stack) -> OError { + let a = stack.pop(); + let b = stack.pop(); + stack.push( + Value::Int(if a.lock_ro().is_truthy() || b.lock_ro().is_truthy() { + 1 + } else { + 0 + }) + .spl(), + ); + Ok(()) +} + pub fn plus(stack: &mut Stack) -> OError { - let a = stack.pop().lock_ro().native.clone(); let b = stack.pop().lock_ro().native.clone(); + let a = stack.pop().lock_ro().native.clone(); stack.push( match (a, b) { (Value::Mega(a), Value::Mega(b)) => Value::Mega(a + b), - _ => todo!(), + x => todo!("{x:?}"), } .spl(), ); @@ -153,8 +198,8 @@ pub fn plus(stack: &mut Stack) -> OError { } pub fn minus(stack: &mut Stack) -> OError { - let a = stack.pop().lock_ro().native.clone(); let b = stack.pop().lock_ro().native.clone(); + let a = stack.pop().lock_ro().native.clone(); stack.push( match (a, b) { (Value::Mega(a), Value::Mega(b)) => Value::Mega(a - b), @@ -166,8 +211,8 @@ pub fn minus(stack: &mut Stack) -> OError { } pub fn slash(stack: &mut Stack) -> OError { - let a = stack.pop().lock_ro().native.clone(); let b = stack.pop().lock_ro().native.clone(); + let a = stack.pop().lock_ro().native.clone(); stack.push( match (a, b) { (Value::Mega(a), Value::Mega(b)) => Value::Mega(a / b), @@ -179,8 +224,8 @@ pub fn slash(stack: &mut Stack) -> OError { } pub fn star(stack: &mut Stack) -> OError { - let a = stack.pop().lock_ro().native.clone(); let b = stack.pop().lock_ro().native.clone(); + let a = stack.pop().lock_ro().native.clone(); stack.push( match (a, b) { (Value::Mega(a), Value::Mega(b)) => Value::Mega(a * b), @@ -385,9 +430,7 @@ pub fn mr_trace(stack: &mut Stack) -> OError { .map(|x| { let item = Value::Null.spl(); let mut obj = item.lock(); - for property in &kind.lock_ro().properties { - obj.property_map.insert(property.clone(), Value::Null.spl()); - } + kind.lock_ro().write_into(&mut obj); obj.kind = kind.clone(); obj.property_map .insert("file".to_owned(), Value::Str(x.file).spl()); @@ -426,13 +469,121 @@ pub fn exec(stack: &mut Stack) -> OError { Ok(()) } +pub fn exec2(stack: &mut Stack) -> OError { + let Value::Func(a) = stack.pop().lock_ro().native.clone() else { + return stack.err(ErrorKind::InvalidCall("exec2".to_owned())) + }; + unsafe { + let f = stack.pop_frame(0); + let f1 = stack.pop_frame(0); + a.to_call.call(stack)?; + stack.push_frame(f1); + stack.push_frame(f); + } + Ok(()) +} + +pub fn stop(stack: &mut Stack) -> OError { + let Value::Int(i) = stack.pop().lock_ro().native.clone().try_mega_to_int() else { + return stack.err(ErrorKind::InvalidCall("stop".to_owned())) + }; + stack.return_accumultor += i as u32; + Ok(()) +} + +pub fn argv(stack: &mut Stack) -> OError { + stack.push(Value::Array(args().into_iter().map(|x| Value::Str(x).spl()).collect()).spl()); + Ok(()) +} + +pub fn get_env(stack: &mut Stack) -> OError { + stack.push( + Value::Array( + vars() + .into_iter() + .map(|x| Value::Array(vec![Value::Str(x.0).spl(), Value::Str(x.1).spl()]).spl()) + .collect(), + ) + .spl(), + ); + Ok(()) +} + +pub fn read_file(stack: &mut Stack) -> OError { + let Value::Str(s) = stack.pop().lock_ro().native.clone() else { + return stack.err(ErrorKind::InvalidCall("read_file".to_owned())) + }; + stack.push( + Value::Str( + fs::read_to_string(s).map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?, + ) + .spl(), + ); + Ok(()) +} + +pub fn alit_end(stack: &mut Stack) -> OError { + let s = stack.pop(); + let popped = stack.pop_until(s); + stack.push(Value::Array(popped).spl()); + Ok(()) +} + +pub fn import(stack: &mut Stack) -> OError { + let Value::Str(mut s) = stack.pop().lock_ro().native.clone() else { + return stack.err(ErrorKind::InvalidCall("include".to_owned())) + }; + if let Some(x) = s.strip_prefix('#') { + s = var("SPL_PATH").unwrap_or("/usr/lib/spl".to_owned()) + "/" + x; + } else if let Some(x) = s.strip_prefix('@') { + s = x.to_owned(); + } else { + s = stack + .get_frame() + .origin + .file + .rsplit_once('/') + .map(|x| x.0) + .unwrap_or(".") + .to_owned() + + "/" + + &s; + } + stack.push(Value::Str(s).spl()); + dup(stack)?; + read_file(stack)?; + dyn_fns::dyn_readf(stack)?; + call(stack)?; + Ok(()) +} + +pub fn readln(stack: &mut Stack) -> OError { + let mut s = String::new(); + stdin() + .read_line(&mut s) + .map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?; + let s = if let Some(s) = s.strip_suffix("\r\n") { + s.to_owned() + } else { + s + }; + let s = if let Some(s) = s.strip_suffix('\n') { + s.to_owned() + } else { + s + }; + stack.push(Value::Str(s).spl()); + Ok(()) +} + pub fn register(r: &mut Stack, o: Arc) { type Fn = fn(&mut Stack) -> OError; - let fns: [(&str, Fn, u32); 31] = [ + let fns: [(&str, Fn, u32); 42] = [ ("pop", pop, 0), ("dup", dup, 2), ("clone", clone, 1), ("swap", swap, 2), + ("mswap", mswap, 2), ("print", print, 0), ("gettype", gettype, 1), ("settype", settype, 1), @@ -444,6 +595,8 @@ pub fn register(r: &mut Stack, o: Arc) { ("lt", lt, 1), ("gt", gt, 1), ("not", not, 1), + ("and", and, 1), + ("or", or, 1), ("+", plus, 1), ("-", minus, 1), ("/", slash, 1), @@ -460,6 +613,14 @@ pub fn register(r: &mut Stack, o: Arc) { ("mr-trace", mr_trace, 1), ("exit", exit, 0), ("exec", exec, 0), + ("exec2", exec2, 0), + ("stop", stop, 0), + ("argv", argv, 1), + ("get-env", get_env, 1), + ("read-file", read_file, 1), + ("alit-end", alit_end, 1), + ("import", import, 0), + ("readln", readln, 1), ]; for f in fns { r.define_func( @@ -467,6 +628,7 @@ pub fn register(r: &mut Stack, o: Arc) { AFunc::new(Func { ret_count: f.2, to_call: FuncImpl::Native(f.1), + run_at_base: false, origin: o.clone(), fname: None, name: f.0.to_owned(), diff --git a/src/stream.rs b/src/stream.rs new file mode 100644 index 0000000..dfcbfc5 --- /dev/null +++ b/src/stream.rs @@ -0,0 +1,177 @@ +use std::{collections::HashMap, io::Read, io::Write, mem, sync::Arc}; + +use once_cell::sync::Lazy; + +use crate::{mutex::Mut, runtime::*, type_err}; + +static STREAM_TYPES: Lazy>>> = + Lazy::new(|| Arc::new(Mut::new(HashMap::new()))); + +pub fn register_stream_type( + name: &str, + supplier: impl Fn(&mut Stack) -> Result + Sync + Send + 'static, +) { + STREAM_TYPES + .lock() + .insert(name.to_owned(), StreamType::from(supplier)); +} + +pub fn get_stream_type(name: String) -> Option { + STREAM_TYPES.lock_ro().get(&name).cloned() +} + +#[derive(Clone)] +pub struct StreamType { + func: Arc Result + Sync + Send + 'static>>, +} + +impl StreamType { + pub fn make_stream(&self, stack: &mut Stack) -> Result { + (self.func)(stack) + } +} + +pub struct Stream { + reader: Box, + writer: Box, +} + +impl Stream { + pub fn new(main: T) -> Self { + let mut rw = Box::new(main); + Self { + // SAFETY: Because these are both in private fields on one object, they can not be + // written to simultaneously or read from while writing due to the guards put in place + // by the borrow checker on the Stream. + reader: Box::new(unsafe { mem::transmute::<&mut _, &mut T>(rw.as_mut()) }), + writer: rw, + } + } + pub fn new_split(reader: impl Read + 'static, writer: impl Write + 'static) -> Self { + Self { + reader: Box::new(reader), + writer: Box::new(writer), + } + } +} + +impl Read for Stream { + fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result { + self.reader.read_vectored(bufs) + } + + fn read_to_end(&mut self, buf: &mut Vec) -> std::io::Result { + self.reader.read_to_end(buf) + } + + fn read_to_string(&mut self, buf: &mut String) -> std::io::Result { + self.reader.read_to_string(buf) + } + + fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> { + self.reader.read_exact(buf) + } + + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.reader.read(buf) + } +} + +impl Write for Stream { + fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result { + self.writer.write_vectored(bufs) + } + + fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { + self.writer.write_all(buf) + } + + fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> std::io::Result<()> { + self.writer.write_fmt(fmt) + } + + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.writer.write(buf) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.writer.flush() + } +} + +impl From for StreamType +where + T: Fn(&mut Stack) -> Result + Sync + Send + 'static, +{ + fn from(value: T) -> Self { + StreamType { + func: Arc::new(Box::new(value)), + } + } +} + +pub fn new_stream(stack: &mut Stack) -> OError { + let Value::Str(s) = stack.pop().lock_ro().native.clone() else { + return stack.err(ErrorKind::InvalidCall("new-stream".to_owned())) + }; + let stream = runtime(|mut rt| { + Ok(rt.register_stream( + get_stream_type(s.clone()) + .ok_or_else(|| { + stack.error(ErrorKind::VariableNotFound(format!("__stream-type-{s}"))) + })? + .make_stream(stack)?, + )) + })?; + stack.push(Value::Mega(stream.0 as i128).spl()); + Ok(()) +} + +pub fn write_to_stream(stack: &mut Stack) -> OError { + let binding = stack.pop(); + let Value::Array(ref a) = binding.lock_ro().native else { + return stack.err(ErrorKind::InvalidCall("write-to-stream".to_owned())) + }; + let Value::Mega(id) = stack.pop().lock_ro().native.clone() else { + return stack.err(ErrorKind::InvalidCall("write-to-stream".to_owned())) + }; + let stream = runtime(|rt| { + rt.get_stream(id as u128) + .ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}")))) + })?; + let mut fixed = Vec::with_capacity(a.len()); + for item in a.iter() { + match item.lock_ro().native { + Value::Int(x) => fixed.push(x as u8), + _ => type_err!(stack, "!int", "int"), + } + } + stack.push( + Value::Mega( + stream + .lock() + .write(&fixed[..]) + .map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))? as i128, + ) + .spl(), + ); + Ok(()) +} + +pub fn register(r: &mut Stack, o: Arc) { + type Fn = fn(&mut Stack) -> OError; + let fns: [(&str, Fn, u32); 1] = [("new-stream", new_stream, 1)]; + for f in fns { + r.define_func( + f.0.to_owned(), + AFunc::new(Func { + ret_count: f.2, + to_call: FuncImpl::Native(f.1), + run_at_base: false, + origin: o.clone(), + fname: None, + name: f.0.to_owned(), + }), + ); + } +} diff --git a/std.spl b/std.spl new file mode 100644 index 0000000..2673644 --- /dev/null +++ b/std.spl @@ -0,0 +1,465 @@ + +def null + + +def program-name + +func println { | + print "\n" print +} + +{ any | with type ; + null clone type settype "construct" dyn-objcall +} "new" "str" dyn-def-method + +{ | with callable this ; + def i 0 =i + while { i this:len lt } { i this:get callable call i ++ =i } +} "foreach" "array" dyn-def-method + +construct _mega-ext { + ; + swap { .. | with this ; + i mswap + i -- mswap + } + mswap { .. | mswap } +} include _mega-ext in mega +construct _array-ext { + ; + get { any | array-get } + sget { any|null | with idx this ; + idx this:len lt idx -1 gt and dup if { + pop + idx this:get + 2 stop + } not if { + null + } + } + len { mega | array-len } + set { any | array-set } + to-stack { .. | with this ; + def len this:len =len + def i 0 =i + while { i len lt } { + i this:get + i ++ =i + } + } + foreach { | with callable this ; + def i 0 =i + while { i this:len lt } { i this:get callable call i ++ =i } + } +} include _array-ext in array + +construct _Iter { + ; + foreach { | with callable this ; + def itm + while { this:next dup =itm null eq not } { + itm callable call + } + } + collect { array | with this ; + [ { any | } this:foreach ] + } + map { MapIter | with map-function this ; + map-function this MapIter:new + } + reduce { ReduceIter | with reduce-function this ; + reduce-function this ReduceIter:new + } + fold { FoldIter | with accumulator fold-function this ; + accumulator fold-function this FoldIter:new + } + sum { mega | with this ; + { mega | with accum item ; + accum item + + } this:reduce:calculate + } + product { mega | with this ; + { mega | with accum item ; + accum item * + } this:reduce:calculate + } + filter { FilterIter | with filter this ; + filter this FilterIter:new + } +} + +construct MapIter { + origin + map-function + ; + construct { this | with map-function origin this ; + origin this:=origin + map-function this:=map-function + this + } + next { any | with this ; + this:origin:next dup null eq if { + 2 stop + } + this:map-function call + } +} + +include _Iter in MapIter + +construct ReduceIter { + origin + accumulator + reduce-function + ; + construct { this | with reduce-function origin this ; + origin this:=origin + reduce-function this:=reduce-function + this + } + next { any | with this ; + def itm + this:origin:next dup null eq if { + 2 stop + } =itm + this:accumulator null eq if { + itm dup this:=accumulator + 2 stop + } + this:accumulator itm this:reduce-function call dup this:=accumulator + } + calculate { any | with this ; + { | pop } this:foreach + this:accumulator + } +} + +include _Iter in ReduceIter + +construct FoldIter { + origin + accumulator + reduce-function + ; + construct { this | with accumulator fold-function origin this ; + accumulator this:=accumulator + origin this:=origin + fold-function this:=fold-function + this + } + next { any | with this ; + def itm + this:origin:next dup null eq if { + 2 stop + } =itm + this:accumulator itm this:fold-function call dup this:=accumulator + } +} + +include _Iter in FoldIter + +construct FilterIter { + origin + filter + ; + construct { this | with filter origin this ; + origin this:=origin + filter this:=filter + this + } + next { any | with this ; + while { 1 } { + def next this:origin:next =next + next null eq if { + null + 3 stop + } + next this:filter call if { + next 3 stop + } + } + dyn-__dump + } +} + +include _Iter in FilterIter + +construct List { + array + ; + construct { this | with array this ; + array this:=array + this + } + get { any | _:array:get } + sget { any|null | _:array:sget } + len { mega | _:array:len } + set { any | _:array:set } +} +construct _GrowingArray { + ; + push-front { | with item this ; + [ item this:array:to-stack ] this:=array + } + push { | with item this ; + [ this:array:to-stack item ] this:=array + } + insert { | with item index this ; + this:array:len index - =index + [ this:array:to-stack index:mswap item (index ++):mswap ] this:=array + } +} +construct _ShrinkingArray { + ; + pop-front { any | with this ; + 0 this:remove + } + pop { any | with this ; + def item + [ this:array:to-stack =item ] this:=array + item + } + remove { any | with index this ; + def item + this:array:len index - =index + [ this:array:to-stack index:mswap =item (index --):mswap ] this:=array + item + } +} + +include _GrowingArray in List +include _ShrinkingArray in List + +construct ArrayIter { + array + idx + ; + construct { this | with array this ; + array this:=array + 0 this:=idx + this + } + next { any | with this ; + this:idx dup ++ this:=idx this:array:sget + } +} +construct _IterableArray { + ; + iter { ArrayIter | with this ; + this gettype "array" eq dup if { + pop + this ArrayIter:new + 2 stop + } not if { + this:array ArrayIter:new + } + } +} +include _Iter in ArrayIter +include _IterableArray in List +include _IterableArray in array + +construct MicroMap { + pairs + ; + construct { this | with pairs this ; + pairs null eq if { + 0 anew List:new =pairs + } + pairs:unwrap this:=pairs + this + } + get-entry { [any,any]|null | with key this ; + this:pairs:iter + { mega | 0 swap:get key eq } swap:filter + _:next + } + get-or-create-entry { [any,any] | with key this ; + { [any,any] | + [ key null ] dup this:pairs:push + } key this:get-entry:unwrap-or + } + get { any | with key this ; + this:pairs:iter + { mega | 0 swap:get key eq } swap:filter + { any | 1 swap:get } swap:map + _:next + } + set { any | with key val this ; + val 1 (key this:get-or-create-entry):set + } + remove { any | with key this ; + this:pairs:iter + { mega | 0 swap:get key eq not } swap:filter + _:collect + List:new + =pairs + } + iter { ArrayIter | with this ; + this:pairs:iter + } +} + +construct Range { + lower + upper + step + ; + construct { this | with lower upper this ; + lower _mega this:=lower + upper _mega this:=upper + 1 this:=step + this + } + set-step { this | with step this ; + step this:=step + this + } + iter { RangeIter | with this ; + this RangeIter:new + } + item { mega|null | with index this ; + def itm index this:step * this:lower + =itm + (itm this:upper lt) (itm this:lower lt not) and dup if { + pop + itm + 2 stop + } not if { + 2 stop + } + } +} + +construct RangeIter { + range + idx + ; + construct { this | with range this ; + range this:=range + 0 this:=idx + this + } + next { mega | with this ; + this:idx dup ++ this:=idx this:range:item + } +} + +include _Iter in RangeIter + +construct shadow { } + +"Copy array"; +func acopy { array | with arr1 arr2 idx1 idx2 len ; + + def i 0 =i + while { i len lt } { + (( i idx1 + ) arr1:get) (i idx2 +) arr2:set; + i ++ =i + } + + arr2 +} + +func aadd { array | with arr1 arr2 ; + + def newarr arr1:len arr2:len + anew =newarr + + arr1 newarr 0 0 arr1:len acopy =newarr + arr2 newarr 0 arr1:len arr2:len acopy =newarr + + newarr +} + +func concat { str | with a b ; + a _array b _array aadd _str +} + +func panic { | with msg ; + program-name dup if { + program-name print " panicked at:" println + } not if { + "Program panicked at:" println + } + { | with it ; + it println + } trace:foreach + "Panic message:" println + " " print msg println + def map env =map + "SPL_PANIC_DUMP" env:get dup if { + "Dumping because SPL_PANIC_DUMP is set." println + null =map + dyn-__dump + } not if { + "SPL_PLAIN_PANIC" map:get dup if { + "Not dumping because SPL_PLAIN_PANIC is set." println + } not if { + "Type 'Yes^M' to dump. You can set SPL_PANIC_DUMP to always dump " + "on panic, or SPL_PLAIN_PANIC to never dump." concat println + readln "Yes" eq if { + dyn-__dump + } + } + } + "Exiting." println + 1 exit +} + +{ | with msg this ; + this not if { + "Assertion failed!" panic + } +} "assert" "int" dyn-def-method + +{ | with msg this ; + this if { + "Assertion failed!" panic + } +} "nassert" "int" dyn-def-method + +func assert-eq { any any | with a b ; + a b eq not if { + "Equality assertion failed!" panic + } + a b +} + +func [ { shadow | + "array" "shadow" settype +} + +func ] { array | + [ alit-end +} + +func env { MicroMap | + get-env List:new MicroMap:new +} + +func ++ { mega | + 1 + +} + +func -- { mega | + 1 - +} + +func _ { | } + +func update-types { | + { | with type ; + { self | } "unwrap" type dyn-def-method + { self | swap pop } "unwrap-or" type dyn-def-method + } dyn-all-types:foreach + { | with this ; + "null cannot be unwrapped." panic + } "unwrap" "null" dyn-def-method + { any | with fb this ; + fb call + } "unwrap-or" "null" dyn-def-method +} +update-types + +1 argv:sget:unwrap import +update-types +argv main exit diff --git a/test.spl b/test.spl index f893ca9..bd95182 100644 --- a/test.spl +++ b/test.spl @@ -6,14 +6,24 @@ func main { int | with args ; "hi" 0 thing:unwrap:set; - def thing2 thing:unwrap ChangingArray:new =thing2 + def thing2 thing:unwrap List:new =thing2 "world" thing2:unwrap:push + "hello" 0 thing2:unwrap:insert - def thing3 thing2:unwrap:array =thing3 + "printing first two words of 'hello hi world' (should be 'hello hi')" println + " " print + 0 thing2:unwrap:get print " " print + 1 thing2:unwrap:get println + "removing hello" println + thing2:pop-front; + "printing first two words again" println + " " print + 0 thing2:unwrap:get print " " print + 1 thing2:unwrap:get println - 0 thing3:unwrap:get println - 1 thing3:unwrap:get println + "" println + "testing closures and func-ptrs" println def thingy "heya1" =thingy @@ -22,16 +32,67 @@ func main { int | with args ; "heya2" =thingy { | thingy println - { | - { | with it ; - it println - } trace:foreach - } call } call + def ptr + &println =ptr + "ptr works" ptr call + &&println =ptr + "ptr-ptr works" ptr call call + thingy:&unwrap =ptr + "unwrap-ptr works" ptr call println + thingy:&&unwrap =ptr + "unwrap-ptr-ptr works" ptr call call println + + + "" println + "testing if" println + + def a "test" =a + def b "test" =b + a b eq dup if { + a " is equal to " b concat concat println + } not if { + a " is not equal to " b concat concat panic + } + + a b assert-eq; + + "" println + "testing ranges & iterators: (0..30@5) + 1" println + + def range 5 (0 30 Range:new):set-step =range + + range:iter + { | 1 + } swap:map + { | _str println } swap:foreach + + "" println + "testing Iter:sum of 5 10s" println + + 0 5 Range:new:iter + { | pop 10 } swap:map + _:sum + _str println + + "" println + "testing MicroMap" println + + def map null MicroMap:new =map + "hey" "hello" map:set; + "helloworld" "Hello, World" map:set; + "{ " print + map:iter + { | with item ; + "'" print + 0 item:get print + "': '" print + 1 item:get print + "', " print + } swap:foreach + "}" println + + "" println + 100 } - -{ | with it ; - it println -} trace:foreach