improved error handling, importing, more stdlib functions

This commit is contained in:
Daniella / Tove 2023-02-19 19:34:25 +01:00
parent 75a0de04b6
commit 98f39a5aa3
Signed by: TudbuT
GPG key ID: 7D63D5634B7C417F
12 changed files with 1128 additions and 130 deletions

7
Cargo.lock generated
View file

@ -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",
]

View file

@ -7,3 +7,4 @@ edition = "2021"
[dependencies]
readformat = "0.1"
once_cell = "1.17"

View file

@ -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<Frame>) {
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(),

View file

@ -56,6 +56,7 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option<u32>, 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<u32>, 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()

View file

@ -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;

View file

@ -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(),

View file

@ -1,10 +1,13 @@
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::{
fmt::Display,
sync::{RwLock, RwLockReadGuard, RwLockWriteGuard},
};
#[derive(Debug)]
pub struct Mut<T>(RwLock<T>);
impl<T> Mut<T> {
pub fn new(obj: T) -> Mut<T> {
pub const fn new(obj: T) -> Mut<T> {
Mut(RwLock::new(obj))
}
@ -17,6 +20,15 @@ impl<T> Mut<T> {
}
}
impl<T> Display for Mut<T>
where
T: Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.read().unwrap().fmt(f)
}
}
impl<T> Clone for Mut<T>
where
T: Clone,

View file

@ -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<Mut<Object>>;
pub type AMType = Arc<Mut<Type>>;
@ -35,6 +40,19 @@ pub struct Runtime {
next_type_id: u32,
types_by_name: HashMap<String, AMType>,
types_by_id: HashMap<u32, AMType>,
next_stream_id: u128,
streams: HashMap<u128, Arc<Mut<Stream>>>,
}
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<Mut<Stream>>) {
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<Arc<Mut<Stream>>> {
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<Mut<Runtime>> {
}
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct FrameInfo {
pub file: String,
pub function: String,
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Frame {
parent: Option<Arc<Frame>>,
pub variables: Mut<HashMap<String, AMObject>>,
pub functions: Mut<HashMap<String, AFunc>>,
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<Frame>, origin: String, function: String) -> Self {
pub fn new_in(
parent: Arc<Frame>,
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<Arc<Frame>>,
object_stack: Vec<AMObject>,
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<AMObject> {
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<String> {
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<Frame> {
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<Frame>) {
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<Box<dyn Fn(&mut Stack) -> 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<Frame>,
pub fname: Option<String>,
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<Frame>) -> 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<String>,
}
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()
}
}
}

View file

@ -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<Frame>) {
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<Frame>) {
("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<Frame>) {
("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<Frame>) {
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(),

177
src/stream.rs Normal file
View file

@ -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<Arc<Mut<HashMap<String, StreamType>>>> =
Lazy::new(|| Arc::new(Mut::new(HashMap::new())));
pub fn register_stream_type(
name: &str,
supplier: impl Fn(&mut Stack) -> Result<Stream, Error> + Sync + Send + 'static,
) {
STREAM_TYPES
.lock()
.insert(name.to_owned(), StreamType::from(supplier));
}
pub fn get_stream_type(name: String) -> Option<StreamType> {
STREAM_TYPES.lock_ro().get(&name).cloned()
}
#[derive(Clone)]
pub struct StreamType {
func: Arc<Box<dyn Fn(&mut Stack) -> Result<Stream, Error> + Sync + Send + 'static>>,
}
impl StreamType {
pub fn make_stream(&self, stack: &mut Stack) -> Result<Stream, Error> {
(self.func)(stack)
}
}
pub struct Stream {
reader: Box<dyn Read + 'static>,
writer: Box<dyn Write + 'static>,
}
impl Stream {
pub fn new<T: Read + Write + 'static>(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<usize> {
self.reader.read_vectored(bufs)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
self.reader.read_to_end(buf)
}
fn read_to_string(&mut self, buf: &mut String) -> std::io::Result<usize> {
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<usize> {
self.reader.read(buf)
}
}
impl Write for Stream {
fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
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<usize> {
self.writer.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
self.writer.flush()
}
}
impl<T> From<T> for StreamType
where
T: Fn(&mut Stack) -> Result<Stream, Error> + 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<Frame>) {
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(),
}),
);
}
}

465
std.spl Normal file
View file

@ -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

View file

@ -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