minor fixes, add streams api

This commit is contained in:
Daniella / Tove 2023-02-25 11:37:07 +01:00
parent 00f9db725e
commit 8c30e2fd2f
Signed by: TudbuT
GPG key ID: 7D63D5634B7C417F
12 changed files with 702 additions and 214 deletions

15
install.spl Normal file
View file

@ -0,0 +1,15 @@
"to be run with `cargo run install.spl`.";
func main { mega |
[ "sudo" "mkdir" "/usr/lib/spl" ] command-wait;
[ "sh" "-c" "sudo cp *.spl /usr/lib/spl" ] command-wait;
[ "cargo" "build" "--release" ] command-wait;
[ "sudo" "cp" "target/release/spl" "/bin" ] command-wait;
"make sure its executable";
[ "sudo" "chmod" "a+rx" "/bin/spl" ] command-wait;
"SPL is now installed!" println
0
}

205
iter.spl Normal file
View file

@ -0,0 +1,205 @@
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
}
join { str | with separator this ;
{ str | with accum item ;
accum _str separator item _str strconcat strconcat
} this:reduce:calculate
}
filter { FilterIter | with filter this ;
filter this FilterIter:new
}
skip { this | with amount this ;
{ |
this:next;
} amount:foreach
}
count { mega | with this ;
def n 0 =n
while { this:next null eq not } {
n ++ =n
}
n
}
last { any | with this ;
def last
def cur
while { this:next dup =cur null eq not } {
cur =last
}
last
}
chain { ChainIter | with other this ;
other this ChainIter:new
}
enumerate { EnumerationIter | with this ;
this EnumerationIter: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
null 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
}
}
}
}
include _Iter in FilterIter
construct ChainIter {
current
next-iters
;
construct { this | with other origin this ;
[ other ] List:new this:=next-iters
origin this:=current
this
}
next { any | with this ;
def item this:current:next =item
while { item null eq } {
this:next-iters:pop-front dup null eq not if {
this:=current
this:current:next =item
} 2 stop
}
item
}
chain { this | with other this ;
other this:next-iters:push
}
}
include _Iter in ChainIter
construct EnumerationIter {
origin
idx
;
construct { this | with origin this ;
origin this:=origin
this
}
next { [mega,any]|null | with this ;
this:origin:next dup null eq not if {
[ swap this:idx swap ]
this:idx ++ this:=idx
}
}
}
include _Iter in EnumerationIter

View file

@ -190,7 +190,6 @@ pub fn dyn_read(stack: &mut Stack) -> OError {
let Value::Str(s) = stack.pop().lock_ro().native.clone() else { let Value::Str(s) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("dyn-read".to_owned())) return stack.err(ErrorKind::InvalidCall("dyn-read".to_owned()))
}; };
let origin = stack.peek_frame(1);
stack.push( stack.push(
Value::Func(AFunc::new(Func { Value::Func(AFunc::new(Func {
ret_count: 0, ret_count: 0,
@ -199,7 +198,7 @@ pub fn dyn_read(stack: &mut Stack) -> OError {
stack: stack.trace(), stack: stack.trace(),
})?), })?),
run_at_base: false, run_at_base: false,
origin, origin: stack.get_frame(),
fname: None, fname: None,
name: "(dyn-read)".to_owned(), name: "(dyn-read)".to_owned(),
})) }))
@ -218,7 +217,6 @@ pub fn dyn_readf(stack: &mut Stack) -> OError {
) else { ) else {
return stack.err(ErrorKind::InvalidCall("dyn-readf".to_owned())) return stack.err(ErrorKind::InvalidCall("dyn-readf".to_owned()))
}; };
let origin = stack.peek_frame(1);
stack.push( stack.push(
Value::Func(AFunc::new(Func { Value::Func(AFunc::new(Func {
ret_count: 0, ret_count: 0,
@ -227,15 +225,26 @@ pub fn dyn_readf(stack: &mut Stack) -> OError {
stack: stack.trace(), stack: stack.trace(),
})?), })?),
run_at_base: true, run_at_base: true,
origin, origin: stack.get_frame(),
fname: Some(n), fname: Some(n),
name: "(dyn-read)".to_owned(), name: "root".to_owned(),
})) }))
.spl(), .spl(),
); );
Ok(()) Ok(())
} }
fn wrap(f: fn(&mut Stack) -> OError) -> impl Fn(&mut Stack) -> OError {
move |stack| {
unsafe {
let frame = stack.pop_frame(0);
let r = f(stack);
stack.push_frame(frame);
r
}
}
}
pub fn register(r: &mut Stack, o: Arc<Frame>) { pub fn register(r: &mut Stack, o: Arc<Frame>) {
type Fn = fn(&mut Stack) -> OError; type Fn = fn(&mut Stack) -> OError;
let fns: [(&str, Fn, u32); 14] = [ let fns: [(&str, Fn, u32); 14] = [
@ -259,7 +268,7 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
f.0.to_owned(), f.0.to_owned(),
AFunc::new(Func { AFunc::new(Func {
ret_count: f.2, ret_count: f.2,
to_call: FuncImpl::Native(f.1), to_call: FuncImpl::NativeDyn(Arc::new(Box::new(wrap(f.1)))),
run_at_base: false, run_at_base: false,
origin: o.clone(), origin: o.clone(),
fname: None, fname: None,

View file

@ -1,8 +1,79 @@
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
#![allow(clippy::len_without_is_empty)] #![allow(clippy::len_without_is_empty)]
pub mod dyn_fns; pub mod dyn_fns;
pub mod lexer; pub mod lexer;
pub mod mutex; pub mod mutex;
pub mod runtime; pub mod runtime;
pub mod std_fns; pub mod std_fns;
pub mod stream; pub mod stream;
use std::fs;
use lexer::lex;
use runtime::*;
pub fn start_file(path: &str) -> Result<Stack, Error> {
Runtime::new().set();
(start_file_in_runtime(path), Runtime::reset()).0
}
pub fn start_file_in_runtime(path: &str) -> Result<Stack, Error> {
let mut stack = Stack::new_in(FrameInfo {
file: "std.spl".to_owned(),
function: "root".to_owned(),
});
// import stdlib
let words =
lex(fs::read_to_string(find_in_splpath("std.spl")).unwrap()).map_err(|x| Error {
kind: ErrorKind::LexError(format!("{x:?}")),
stack: Vec::new(),
})?;
words.exec(&mut stack)?;
// run file
Words {
words: vec![
Word::Const(Value::Str(path.to_owned())),
Word::Call("call-main-on-file".to_owned(), false, 0),
],
}
.exec(&mut stack)?;
Ok(stack)
}
#[macro_export]
macro_rules! require_on_stack {
($name:tt, $type:tt, $stack:expr, $fn:literal) => {
let Value::$type($name) = $stack.pop().lock_ro().native.clone() else {
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
};
};
}
#[macro_export]
macro_rules! require_int_on_stack {
($name:tt, $stack:expr, $fn:literal) => {
let Value::Int($name) = $stack.pop().lock_ro().native.clone().try_mega_to_int() else {
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
};
};
}
#[macro_export]
macro_rules! require_array_on_stack {
($name:tt, $stack:expr, $fn:literal) => {
let _binding = $stack.pop();
let Value::Array(ref $name) = _binding.lock_ro().native else {
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
};
};
}
#[macro_export]
macro_rules! require_mut_array_on_stack {
($name:tt, $stack:expr, $fn:literal) => {
let _binding = $stack.pop();
let Value::Array(ref mut $name) = _binding.lock().native else {
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
};
};
}

View file

@ -1,19 +1,9 @@
use spl::{lexer::lex, runtime::*}; use spl::{runtime::*, start_file};
use std::fs; use std::env::args;
fn main() -> OError { fn main() {
let rt = Runtime::new(); if let Err(x) = start_file(&args().nth(1).expect("no file provided to be started.")) {
rt.set(); println!("{x:?}");
let mut stack = Stack::new_in(FrameInfo { }
file: "std.spl".to_owned(),
function: "root".to_owned(),
});
let words = lex(fs::read_to_string("std.spl").unwrap()).map_err(|x| Error {
kind: ErrorKind::LexError(format!("{x:?}")),
stack: Vec::new(),
})?;
words.exec(&mut stack)?;
Runtime::reset();
Ok(())
} }

View file

@ -6,8 +6,7 @@ use crate::{
}; };
use core::panic; use core::panic;
use std::mem; use std::sync::{RwLockWriteGuard, RwLockReadGuard};
use std::sync::RwLockWriteGuard;
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::HashMap, collections::HashMap,
@ -15,7 +14,8 @@ use std::{
sync::Arc, sync::Arc,
vec, vec,
}; };
use std::{collections::VecDeque, thread::panicking}; use std::collections::VecDeque;
use std::{env::var, mem, path::Path};
pub type AMObject = Arc<Mut<Object>>; pub type AMObject = Arc<Mut<Object>>;
pub type AMType = Arc<Mut<Type>>; pub type AMType = Arc<Mut<Type>>;
@ -26,7 +26,16 @@ thread_local! {
static RUNTIME: RefCell<Option<Arc<Mut<Runtime>>>> = RefCell::new(None); static RUNTIME: RefCell<Option<Arc<Mut<Runtime>>>> = RefCell::new(None);
} }
pub fn runtime<T>(f: impl FnOnce(RwLockWriteGuard<Runtime>) -> T) -> T { pub fn runtime<T>(f: impl FnOnce(RwLockReadGuard<Runtime>) -> T) -> T {
RUNTIME.with(|rt| {
f(rt.borrow_mut()
.as_mut()
.expect("no runtime (use .set())")
.lock_ro())
})
}
pub fn runtime_mut<T>(f: impl FnOnce(RwLockWriteGuard<Runtime>) -> T) -> T {
RUNTIME.with(|rt| { RUNTIME.with(|rt| {
f(rt.borrow_mut() f(rt.borrow_mut()
.as_mut() .as_mut()
@ -171,7 +180,13 @@ impl Display for Frame {
std::fmt::Display::fmt(&object.lock_ro(), f)?; std::fmt::Display::fmt(&object.lock_ro(), f)?;
f.write_str("\n")?; f.write_str("\n")?;
} }
f.write_str("\n")?; f.write_str("\nFuncs: \n")?;
for (name, ..) in self.functions.lock_ro().iter() {
f.write_str(" ")?;
f.write_str(name)?;
f.write_str("\n")?;
}
f.write_str("\n\n")?;
Ok(()) Ok(())
} }
} }
@ -399,9 +414,9 @@ impl Stack {
Frame::new(func.origin.clone(), func.name.clone()) Frame::new(func.origin.clone(), func.name.clone())
}; };
self.frames.push(Arc::new(f)); self.frames.push(Arc::new(f));
func.to_call.call(self)?; let r = func.to_call.call(self);
self.frames.pop().unwrap(); self.frames.pop().unwrap();
Ok(()) r
} }
pub fn get_func(&self, name: String) -> Result<AFunc, Error> { pub fn get_func(&self, name: String) -> Result<AFunc, Error> {
@ -558,9 +573,9 @@ pub enum Keyword {
/// <none> /// <none>
/// ///
/// Dumps stack. Not available as actual keyword, therefore only obtainable through AST /// Dumps stack. Not available as actual keyword, therefore only obtainable through AST
/// manipulation or modding. When using dyn variant, it must be enabled in the main function. /// manipulation, a dyn call, or modding.
/// equivalent to dyn-__dump /// equivalent to dyn-__dump
/// example: func main { int | dyn-__dump-check "Hello, world!" dyn-__dump 0 } /// example: func main { int | "Hello, world!" dyn-__dump pop 0 }
Dump, Dump,
/// def <name> /// def <name>
/// ///
@ -816,7 +831,7 @@ impl PartialEq for Object {
impl PartialOrd for Object { impl PartialOrd for Object {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
if self.kind != other.kind { if self.kind != other.kind {
panic!(); return None;
} }
self.native.partial_cmp(&other.native) self.native.partial_cmp(&other.native)
} }
@ -903,6 +918,18 @@ where
} }
} }
pub fn find_in_splpath(path: &str) -> String {
if Path::new(path).exists() {
return path.to_owned();
}
let s = var("SPL_PATH").unwrap_or("/usr/lib/spl".to_owned()) + "/" + path;
if Path::new(&s).exists() {
s
} else {
path.to_owned()
}
}
impl Words { impl Words {
pub fn exec(&self, stack: &mut Stack) -> OError { pub fn exec(&self, stack: &mut Stack) -> OError {
for word in self.words.clone() { for word in self.words.clone() {
@ -927,7 +954,7 @@ impl Words {
stack.set_var( stack.set_var(
name.clone(), name.clone(),
Value::Str( Value::Str(
runtime(move |mut rt| { runtime_mut(move |mut rt| {
rt.make_type(name.clone(), move |mut t| { rt.make_type(name.clone(), move |mut t| {
for field in fields { for field in fields {
t.add_property(field, origin.clone())?; t.add_property(field, origin.clone())?;
@ -1113,18 +1140,11 @@ pub struct Error {
impl Debug for Error { impl Debug for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 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("\n\nSPL PANIC DUE TO UNCAUGHT ERROR:\n")?; f.write_str(format!("Error: {:?}", self.kind).as_str())?;
f.write_str(format!("Error: {:?}", self.kind).as_str())?; f.write_str("\n")?;
f.write_str("\n")?; f.write_str(self.stack.join("\n").as_str())?;
f.write_str(self.stack.join("\n").as_str())?; f.write_str("\n\n")?;
f.write_str("\n\n")?; Ok(())
Ok(())
} else {
f.debug_struct("Error")
.field("kind", &self.kind)
.field("stack", &self.stack)
.finish()
}
} }
} }

View file

@ -1,9 +1,10 @@
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
env::{args, var, vars}, env::{args, vars},
fs, fs,
io::{stdin, stdout, Write}, io::{stdin, stdout, Write},
mem, process, mem,
process::{self, Stdio},
sync::Arc, sync::Arc,
}; };
@ -190,7 +191,7 @@ pub fn plus(stack: &mut Stack) -> OError {
stack.push( stack.push(
match (a, b) { match (a, b) {
(Value::Mega(a), Value::Mega(b)) => Value::Mega(a + b), (Value::Mega(a), Value::Mega(b)) => Value::Mega(a + b),
x => todo!("{x:?}"), x => stack.err(ErrorKind::InvalidCall("plus".to_owned()))?,
} }
.spl(), .spl(),
); );
@ -430,7 +431,7 @@ pub fn callp(stack: &mut Stack) -> OError {
stack.call(&a)?; stack.call(&a)?;
for _ in 0..a.ret_count { for _ in 0..a.ret_count {
stack.pop(); stack.pop();
}; }
Ok(()) Ok(())
} }
@ -487,7 +488,9 @@ pub fn exec(stack: &mut Stack) -> OError {
}; };
unsafe { unsafe {
let f = stack.pop_frame(0); let f = stack.pop_frame(0);
let f1 = stack.pop_frame(0);
a.to_call.call(stack)?; a.to_call.call(stack)?;
stack.push_frame(f1);
stack.push_frame(f); stack.push_frame(f);
} }
Ok(()) Ok(())
@ -500,7 +503,9 @@ pub fn exec2(stack: &mut Stack) -> OError {
unsafe { unsafe {
let f = stack.pop_frame(0); let f = stack.pop_frame(0);
let f1 = stack.pop_frame(0); let f1 = stack.pop_frame(0);
let f2 = stack.pop_frame(0);
a.to_call.call(stack)?; a.to_call.call(stack)?;
stack.push_frame(f2);
stack.push_frame(f1); stack.push_frame(f1);
stack.push_frame(f); stack.push_frame(f);
} }
@ -555,15 +560,15 @@ pub fn alit_end(stack: &mut Stack) -> OError {
pub fn import(stack: &mut Stack) -> OError { pub fn import(stack: &mut Stack) -> OError {
let Value::Str(mut s) = stack.pop().lock_ro().native.clone() else { let Value::Str(mut s) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("include".to_owned())) return stack.err(ErrorKind::InvalidCall("import".to_owned()))
}; };
if let Some(x) = s.strip_prefix('#') { if let Some(x) = s.strip_prefix('#') {
s = var("SPL_PATH").unwrap_or("/usr/lib/spl".to_owned()) + "/" + x; s = find_in_splpath(x);
} else if let Some(x) = s.strip_prefix('@') { } else if let Some(x) = s.strip_prefix('@') {
s = x.to_owned(); s = x.to_owned();
} else { } else {
s = stack s = stack
.get_frame() .peek_frame(1)
.origin .origin
.file .file
.rsplit_once('/') .rsplit_once('/')
@ -600,9 +605,66 @@ pub fn readln(stack: &mut Stack) -> OError {
Ok(()) Ok(())
} }
pub fn command(stack: &mut Stack) -> OError {
let binding = stack.pop();
let Value::Array(ref a) = binding.lock_ro().native else {
return stack.err(ErrorKind::InvalidCall("command".to_owned()))
};
let mut args = Vec::new();
for item in a.into_iter() {
if let Value::Str(ref s) = item.lock_ro().native {
args.push(s.to_owned());
}
}
if args.len() < 1 {
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
}
process::Command::new(args[0].to_owned())
.args(&args[1..])
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?;
Ok(())
}
pub fn command_wait(stack: &mut Stack) -> OError {
let binding = stack.pop();
let Value::Array(ref a) = binding.lock_ro().native else {
return stack.err(ErrorKind::InvalidCall("command".to_owned()))
};
let mut args = Vec::new();
for item in a.into_iter() {
if let Value::Str(ref s) = item.lock_ro().native {
args.push(s.to_owned());
}
}
if args.len() < 1 {
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
}
stack.push(
Value::Int(
process::Command::new(args[0].to_owned())
.args(&args[1..])
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?
.wait()
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?
.code()
.unwrap_or(-1),
)
.spl(),
);
Ok(())
}
pub fn register(r: &mut Stack, o: Arc<Frame>) { pub fn register(r: &mut Stack, o: Arc<Frame>) {
type Fn = fn(&mut Stack) -> OError; type Fn = fn(&mut Stack) -> OError;
let fns: [(&str, Fn, u32); 44] = [ let fns: [(&str, Fn, u32); 46] = [
("pop", pop, 0), ("pop", pop, 0),
("dup", dup, 2), ("dup", dup, 2),
("clone", clone, 1), ("clone", clone, 1),
@ -647,6 +709,8 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
("alit-end", alit_end, 1), ("alit-end", alit_end, 1),
("import", import, 0), ("import", import, 0),
("readln", readln, 1), ("readln", readln, 1),
("command", command, 0),
("command-wait", command_wait, 1),
]; ];
for f in fns { for f in fns {
r.define_func( r.define_func(

View file

@ -1,11 +1,20 @@
use std::{collections::HashMap, io::Read, io::Write, mem, sync::Arc}; use std::{
collections::HashMap,
fs::{OpenOptions, File},
io::Read,
io::Write,
mem,
net::{TcpStream, Shutdown},
sync::Arc,
};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use crate::{mutex::Mut, runtime::*, type_err}; use crate::{mutex::Mut, runtime::*, type_err, require_on_stack, require_int_on_stack, require_array_on_stack, require_mut_array_on_stack};
static STREAM_TYPES: Lazy<Arc<Mut<HashMap<String, StreamType>>>> = static STREAM_TYPES: Lazy<Arc<Mut<HashMap<String, StreamType>>>> =
Lazy::new(|| Arc::new(Mut::new(HashMap::new()))); Lazy::new(|| Arc::new(Mut::new(HashMap::new())));
static IS_INITIALIZED: Lazy<Arc<Mut<bool>>> = Lazy::new(|| Arc::new(Mut::new(false)));
pub fn register_stream_type( pub fn register_stream_type(
name: &str, name: &str,
@ -34,10 +43,11 @@ impl StreamType {
pub struct Stream { pub struct Stream {
reader: Box<dyn Read + 'static>, reader: Box<dyn Read + 'static>,
writer: Box<dyn Write + 'static>, writer: Box<dyn Write + 'static>,
close: fn(&mut Self),
} }
impl Stream { impl Stream {
pub fn new<T: Read + Write + 'static>(main: T) -> Self { pub fn new<T: Read + Write + 'static>(main: T, close: fn(&mut Self)) -> Self {
let mut rw = Box::new(main); let mut rw = Box::new(main);
Self { Self {
// SAFETY: Because these are both in private fields on one object, they can not be // SAFETY: Because these are both in private fields on one object, they can not be
@ -45,12 +55,14 @@ impl Stream {
// by the borrow checker on the Stream. // by the borrow checker on the Stream.
reader: Box::new(unsafe { mem::transmute::<&mut _, &mut T>(rw.as_mut()) }), reader: Box::new(unsafe { mem::transmute::<&mut _, &mut T>(rw.as_mut()) }),
writer: rw, writer: rw,
close,
} }
} }
pub fn new_split(reader: impl Read + 'static, writer: impl Write + 'static) -> Self { pub fn new_split(reader: impl Read + 'static, writer: impl Write + 'static, close: fn(&mut Self)) -> Self {
Self { Self {
reader: Box::new(reader), reader: Box::new(reader),
writer: Box::new(writer), writer: Box::new(writer),
close,
} }
} }
} }
@ -111,30 +123,24 @@ where
} }
pub fn new_stream(stack: &mut Stack) -> OError { pub fn new_stream(stack: &mut Stack) -> OError {
let Value::Str(s) = stack.pop().lock_ro().native.clone() else { require_on_stack!(s, Str, stack, "write-stream");
return stack.err(ErrorKind::InvalidCall("new-stream".to_owned())) let stream = get_stream_type(s.clone())
}; .ok_or_else(|| {
let stream = runtime(|mut rt| { stack.error(ErrorKind::VariableNotFound(format!("__stream-type-{s}")))
})?
.make_stream(stack)?;
let stream = runtime_mut(move |mut rt| {
Ok(rt.register_stream( Ok(rt.register_stream(
get_stream_type(s.clone()) stream
.ok_or_else(|| {
stack.error(ErrorKind::VariableNotFound(format!("__stream-type-{s}")))
})?
.make_stream(stack)?,
)) ))
})?; })?;
stack.push(Value::Mega(stream.0 as i128).spl()); stack.push(Value::Mega(stream.0 as i128).spl());
Ok(()) Ok(())
} }
pub fn write_to_stream(stack: &mut Stack) -> OError { pub fn write_stream(stack: &mut Stack) -> OError {
let binding = stack.pop(); require_on_stack!(id, Mega, stack, "write-stream");
let Value::Array(ref a) = binding.lock_ro().native else { require_array_on_stack!(a, stack, "write-stream");
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| { let stream = runtime(|rt| {
rt.get_stream(id as u128) rt.get_stream(id as u128)
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}")))) .ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
@ -158,9 +164,132 @@ pub fn write_to_stream(stack: &mut Stack) -> OError {
Ok(()) Ok(())
} }
pub fn write_all_stream(stack: &mut Stack) -> OError {
require_on_stack!(id, Mega, stack, "write-all-stream");
require_array_on_stack!(a, stack, "write-all-stream");
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"),
}
}
stream
.lock()
.write_all(&fixed[..])
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?;
Ok(())
}
pub fn read_stream(stack: &mut Stack) -> OError {
require_on_stack!(id, Mega, stack, "read-stream");
require_mut_array_on_stack!(a, stack, "read-stream");
let stream = runtime(|rt| {
rt.get_stream(id as u128)
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
})?;
let mut vec = vec![0; a.len()];
stack.push(
Value::Mega(
stream
.lock()
.read(&mut vec[..])
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))? as i128,
)
.spl(),
);
a.clone_from_slice(
&vec.into_iter()
.map(|x| Value::Int(x as i32).spl())
.collect::<Vec<_>>(),
);
Ok(())
}
pub fn read_all_stream(stack: &mut Stack) -> OError {
require_on_stack!(id, Mega, stack, "read-all-stream");
require_mut_array_on_stack!(a, stack, "read-all-stream");
let stream = runtime(|rt| {
rt.get_stream(id as u128)
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
})?;
let mut vec = vec![0; a.len()];
stream
.lock()
.read_exact(&mut vec[..])
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?;
a.clone_from_slice(
&vec.into_iter()
.map(|x| Value::Int(x as i32).spl())
.collect::<Vec<_>>(),
);
Ok(())
}
pub fn close_stream(stack: &mut Stack) -> OError {
require_on_stack!(id, Mega, stack, "close-stream");
if let Some(stream) = runtime(|rt| {
rt.get_stream(id as u128)
}) {
let mut stream = stream.lock();
(stream.close)(&mut stream);
}
Ok(())
}
fn nop(_stream: &mut Stream) {}
fn stream_file(stack: &mut Stack) -> Result<Stream, Error> {
let truncate = stack.pop().lock_ro().is_truthy();
require_on_stack!(path, Str, stack, "FILE new-stream");
Ok(Stream::new(
OpenOptions::new()
.read(!truncate)
.write(true)
.create(truncate)
.truncate(truncate)
.open(path)
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?,
nop
))
}
fn stream_tcp(stack: &mut Stack) -> Result<Stream, Error> {
require_int_on_stack!(port, stack, "TCP new-stream");
require_on_stack!(ip, Str, stack, "TCP new-stream");
fn close_tcp(stream: &mut Stream) {
unsafe {
let f = ((stream.reader.as_mut() as *mut dyn Read).cast() as *mut TcpStream).as_mut().unwrap();
let _ = f.shutdown(Shutdown::Both);
}
}
Ok(Stream::new(
TcpStream::connect((ip, port as u16))
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?,
close_tcp
))
}
pub fn register(r: &mut Stack, o: Arc<Frame>) { pub fn register(r: &mut Stack, o: Arc<Frame>) {
if !*IS_INITIALIZED.lock_ro() {
register_stream_type("file", stream_file);
register_stream_type("tcp", stream_tcp);
*IS_INITIALIZED.lock() = true;
}
type Fn = fn(&mut Stack) -> OError; type Fn = fn(&mut Stack) -> OError;
let fns: [(&str, Fn, u32); 1] = [("new-stream", new_stream, 1)]; let fns: [(&str, Fn, u32); 6] = [
("new-stream", new_stream, 1),
("write-stream", write_stream, 1),
("write-all-stream", write_all_stream, 0),
("read-stream", read_stream, 1),
("read-all-stream", read_all_stream, 0),
("close-stream", close_stream, 0),
];
for f in fns { for f in fns {
r.define_func( r.define_func(
f.0.to_owned(), f.0.to_owned(),

172
std.spl
View file

@ -12,11 +12,6 @@ func println { |
null clone type settype "construct" dyn-objcall null clone type settype "construct" dyn-objcall
} "new" "str" dyn-def-method } "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 { construct _mega-ext {
; ;
swap { .. | with this ; swap { .. | with this ;
@ -24,7 +19,12 @@ construct _mega-ext {
i -- mswap i -- mswap
} }
mswap { .. | mswap } mswap { .. | mswap }
foreach { | with this ;
def i 0 =i
while { i this lt } { i callable call i ++ =i }
}
} include _mega-ext in mega } include _mega-ext in mega
construct _array-ext { construct _array-ext {
; ;
get { any | array-get } get { any | array-get }
@ -51,138 +51,24 @@ construct _array-ext {
def i 0 =i def i 0 =i
while { i this:len lt } { i this:get callable call i ++ =i } while { i this:len lt } { i this:get callable call i ++ =i }
} }
0 { any | with this ;
0 this:get
}
1 { any | with this ;
1 this:get
}
2 { any | with this ;
2 this:get
}
3 { any | with this ;
3 this:get
}
4 { any | with this ;
4 this:get
}
} include _array-ext in array } include _array-ext in array
construct _Iter { "iter.spl" import
;
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 { construct List {
array array
@ -215,11 +101,17 @@ construct _ShrinkingArray {
0 this:remove 0 this:remove
} }
pop { any | with this ; pop { any | with this ;
this:array:len not if {
null 2 stop
}
def item def item
[ this:array:to-stack =item ] this:=array [ this:array:to-stack =item ] this:=array
item item
} }
remove { any | with index this ; remove { any | with index this ;
this:array:len not if {
null 2 stop
}
def item def item
this:array:len index - =index this:array:len index - =index
[ this:array:to-stack index:mswap =item (index --):mswap ] this:=array [ this:array:to-stack index:mswap =item (index --):mswap ] this:=array
@ -325,6 +217,7 @@ construct Range {
itm itm
2 stop 2 stop
} not if { } not if {
null
2 stop 2 stop
} }
} }
@ -447,6 +340,12 @@ func -- { mega |
func _ { | } func _ { | }
func call-main-on-file { | with file ;
"@" file concat import
update-types
argv main exit
}
func update-types { | func update-types { |
{ | with type ; { | with type ;
{ self | } "unwrap" type dyn-def-method { self | } "unwrap" type dyn-def-method
@ -461,6 +360,3 @@ func update-types { |
} }
update-types update-types
1 argv:sget:unwrap import
update-types
argv main exit

78
stream.spl Normal file
View file

@ -0,0 +1,78 @@
"The SPL stream is an IO construct used to read and write to ";
"some external thing, for example a file or a TCP socket.";
"All functions here are encapsulations of their native counterparts.";
"Examples:";
"def tcp 'localhost' 8080 StreamType:tcp:create =tcp";
"def file 'test.txt' 1 StreamType:file:create =file 'hi':to-bytes file:write-exact; file:close null =file";
construct Stream {
id
;
construct { this | with type this ;
type new-stream this:=id
this
}
read-one { mega | with this ;
def buf 1 anew =buf
while { buf this:id read-stream not } { }
0 buf:get _mega
}
"the buffer is written to in-place.";
read { mega [int] | with buf this ;
buf gettype "int" eq if { buf anew =buf }
buf this:id read-stream buf
}
"the buffer is written to in-place.";
read-exact { [int] | with buf this ;
buf gettype "int" eq if { buf anew =buf }
buf this:id read-all-stream buf
}
write { mega | with buf this ;
buf this:id write-stream
}
write-exact { | with buf this ;
buf this:id write-all-stream
}
close { | with this ;
this:id close-stream
}
}
construct StreamType {
id
;
construct { this | with id this ;
id this:=id
this
}
create { Stream | with this ;
this:id Stream:new
}
}
def stream-types 0 anew =stream-types
construct _StreamType {
;
construct { this | with this ;
{ | with type ;
"type StreamType:new this:=<type>";
(type StreamType:new) (this ("=" type concat)) dyn-objcall
} stream-types:foreach
this
}
}
func register-stream-type { | with id ;
[ stream-types:to-stack id ] =stream-types
id _StreamType dyn-def-field
}
"tcp" register-stream-type
"file" register-stream-type
func StreamTypes { _StreamType |
_StreamType:new
}

View file

@ -1,4 +1,6 @@
"stream.spl" import
func main { int | with args ; func main { int | with args ;
def thing def thing
@ -97,6 +99,14 @@ func main { int | with args ;
argv:iter argv:iter
{ str | " " concat } swap:map { str | " " concat } swap:map
&print swap:foreach &print swap:foreach
"" println
"testing stream" println
def file "test.txt" 1 StreamTypes:file:create =file
"hi" _array file:write-exact;
file:close null =file
"" println "" println
100 100

1
test.txt Normal file
View file

@ -0,0 +1 @@
hi