improved error handling, importing, more stdlib functions
This commit is contained in:
parent
75a0de04b6
commit
98f39a5aa3
12 changed files with 1128 additions and 130 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -7,3 +7,4 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
readformat = "0.1"
|
||||
once_cell = "1.17"
|
||||
|
|
|
@ -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(),
|
||||
|
|
13
src/lexer.rs
13
src/lexer.rs
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
38
src/main.rs
38
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(),
|
||||
|
|
16
src/mutex.rs
16
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<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,
|
||||
|
|
254
src/runtime.rs
254
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<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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
190
src/std_fns.rs
190
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<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
177
src/stream.rs
Normal 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
465
std.spl
Normal 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
|
87
test.spl
87
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
|
||||
|
|
Loading…
Add table
Reference in a new issue