minor fixes, add streams api
This commit is contained in:
parent
00f9db725e
commit
8c30e2fd2f
12 changed files with 702 additions and 214 deletions
15
install.spl
Normal file
15
install.spl
Normal 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
205
iter.spl
Normal 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
|
|
@ -190,7 +190,6 @@ pub fn dyn_read(stack: &mut Stack) -> OError {
|
|||
let Value::Str(s) = stack.pop().lock_ro().native.clone() else {
|
||||
return stack.err(ErrorKind::InvalidCall("dyn-read".to_owned()))
|
||||
};
|
||||
let origin = stack.peek_frame(1);
|
||||
stack.push(
|
||||
Value::Func(AFunc::new(Func {
|
||||
ret_count: 0,
|
||||
|
@ -199,7 +198,7 @@ pub fn dyn_read(stack: &mut Stack) -> OError {
|
|||
stack: stack.trace(),
|
||||
})?),
|
||||
run_at_base: false,
|
||||
origin,
|
||||
origin: stack.get_frame(),
|
||||
fname: None,
|
||||
name: "(dyn-read)".to_owned(),
|
||||
}))
|
||||
|
@ -218,7 +217,6 @@ pub fn dyn_readf(stack: &mut Stack) -> OError {
|
|||
) else {
|
||||
return stack.err(ErrorKind::InvalidCall("dyn-readf".to_owned()))
|
||||
};
|
||||
let origin = stack.peek_frame(1);
|
||||
stack.push(
|
||||
Value::Func(AFunc::new(Func {
|
||||
ret_count: 0,
|
||||
|
@ -227,15 +225,26 @@ pub fn dyn_readf(stack: &mut Stack) -> OError {
|
|||
stack: stack.trace(),
|
||||
})?),
|
||||
run_at_base: true,
|
||||
origin,
|
||||
origin: stack.get_frame(),
|
||||
fname: Some(n),
|
||||
name: "(dyn-read)".to_owned(),
|
||||
name: "root".to_owned(),
|
||||
}))
|
||||
.spl(),
|
||||
);
|
||||
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>) {
|
||||
type Fn = fn(&mut Stack) -> OError;
|
||||
let fns: [(&str, Fn, u32); 14] = [
|
||||
|
@ -259,7 +268,7 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
|||
f.0.to_owned(),
|
||||
AFunc::new(Func {
|
||||
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,
|
||||
origin: o.clone(),
|
||||
fname: None,
|
||||
|
|
71
src/lib.rs
71
src/lib.rs
|
@ -1,8 +1,79 @@
|
|||
#![allow(clippy::type_complexity)]
|
||||
#![allow(clippy::len_without_is_empty)]
|
||||
|
||||
pub mod dyn_fns;
|
||||
pub mod lexer;
|
||||
pub mod mutex;
|
||||
pub mod runtime;
|
||||
pub mod std_fns;
|
||||
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()))
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
22
src/main.rs
22
src/main.rs
|
@ -1,19 +1,9 @@
|
|||
use spl::{lexer::lex, runtime::*};
|
||||
use spl::{runtime::*, start_file};
|
||||
|
||||
use std::fs;
|
||||
use std::env::args;
|
||||
|
||||
fn main() -> OError {
|
||||
let rt = Runtime::new();
|
||||
rt.set();
|
||||
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(())
|
||||
fn main() {
|
||||
if let Err(x) = start_file(&args().nth(1).expect("no file provided to be started.")) {
|
||||
println!("{x:?}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,7 @@ use crate::{
|
|||
};
|
||||
|
||||
use core::panic;
|
||||
use std::mem;
|
||||
use std::sync::RwLockWriteGuard;
|
||||
use std::sync::{RwLockWriteGuard, RwLockReadGuard};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
|
@ -15,7 +14,8 @@ use std::{
|
|||
sync::Arc,
|
||||
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 AMType = Arc<Mut<Type>>;
|
||||
|
@ -26,7 +26,16 @@ thread_local! {
|
|||
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| {
|
||||
f(rt.borrow_mut()
|
||||
.as_mut()
|
||||
|
@ -171,7 +180,13 @@ impl Display for Frame {
|
|||
std::fmt::Display::fmt(&object.lock_ro(), f)?;
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
@ -399,9 +414,9 @@ impl Stack {
|
|||
Frame::new(func.origin.clone(), func.name.clone())
|
||||
};
|
||||
self.frames.push(Arc::new(f));
|
||||
func.to_call.call(self)?;
|
||||
let r = func.to_call.call(self);
|
||||
self.frames.pop().unwrap();
|
||||
Ok(())
|
||||
r
|
||||
}
|
||||
|
||||
pub fn get_func(&self, name: String) -> Result<AFunc, Error> {
|
||||
|
@ -558,9 +573,9 @@ pub enum Keyword {
|
|||
/// <none>
|
||||
///
|
||||
/// 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
|
||||
/// example: func main { int | dyn-__dump-check "Hello, world!" dyn-__dump 0 }
|
||||
/// example: func main { int | "Hello, world!" dyn-__dump pop 0 }
|
||||
Dump,
|
||||
/// def <name>
|
||||
///
|
||||
|
@ -816,7 +831,7 @@ impl PartialEq for Object {
|
|||
impl PartialOrd for Object {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
if self.kind != other.kind {
|
||||
panic!();
|
||||
return None;
|
||||
}
|
||||
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 {
|
||||
pub fn exec(&self, stack: &mut Stack) -> OError {
|
||||
for word in self.words.clone() {
|
||||
|
@ -927,7 +954,7 @@ impl Words {
|
|||
stack.set_var(
|
||||
name.clone(),
|
||||
Value::Str(
|
||||
runtime(move |mut rt| {
|
||||
runtime_mut(move |mut rt| {
|
||||
rt.make_type(name.clone(), move |mut t| {
|
||||
for field in fields {
|
||||
t.add_property(field, origin.clone())?;
|
||||
|
@ -1113,18 +1140,11 @@ pub struct Error {
|
|||
|
||||
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()
|
||||
}
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std::{
|
||||
collections::VecDeque,
|
||||
env::{args, var, vars},
|
||||
env::{args, vars},
|
||||
fs,
|
||||
io::{stdin, stdout, Write},
|
||||
mem, process,
|
||||
mem,
|
||||
process::{self, Stdio},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
|
@ -190,7 +191,7 @@ pub fn plus(stack: &mut Stack) -> OError {
|
|||
stack.push(
|
||||
match (a, b) {
|
||||
(Value::Mega(a), Value::Mega(b)) => Value::Mega(a + b),
|
||||
x => todo!("{x:?}"),
|
||||
x => stack.err(ErrorKind::InvalidCall("plus".to_owned()))?,
|
||||
}
|
||||
.spl(),
|
||||
);
|
||||
|
@ -430,7 +431,7 @@ pub fn callp(stack: &mut Stack) -> OError {
|
|||
stack.call(&a)?;
|
||||
for _ in 0..a.ret_count {
|
||||
stack.pop();
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -487,7 +488,9 @@ pub fn exec(stack: &mut Stack) -> OError {
|
|||
};
|
||||
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(())
|
||||
|
@ -500,7 +503,9 @@ pub fn exec2(stack: &mut Stack) -> OError {
|
|||
unsafe {
|
||||
let f = stack.pop_frame(0);
|
||||
let f1 = stack.pop_frame(0);
|
||||
let f2 = stack.pop_frame(0);
|
||||
a.to_call.call(stack)?;
|
||||
stack.push_frame(f2);
|
||||
stack.push_frame(f1);
|
||||
stack.push_frame(f);
|
||||
}
|
||||
|
@ -555,15 +560,15 @@ pub fn alit_end(stack: &mut Stack) -> OError {
|
|||
|
||||
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()))
|
||||
return stack.err(ErrorKind::InvalidCall("import".to_owned()))
|
||||
};
|
||||
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('@') {
|
||||
s = x.to_owned();
|
||||
} else {
|
||||
s = stack
|
||||
.get_frame()
|
||||
.peek_frame(1)
|
||||
.origin
|
||||
.file
|
||||
.rsplit_once('/')
|
||||
|
@ -600,9 +605,66 @@ pub fn readln(stack: &mut Stack) -> OError {
|
|||
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>) {
|
||||
type Fn = fn(&mut Stack) -> OError;
|
||||
let fns: [(&str, Fn, u32); 44] = [
|
||||
let fns: [(&str, Fn, u32); 46] = [
|
||||
("pop", pop, 0),
|
||||
("dup", dup, 2),
|
||||
("clone", clone, 1),
|
||||
|
@ -647,6 +709,8 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
|||
("alit-end", alit_end, 1),
|
||||
("import", import, 0),
|
||||
("readln", readln, 1),
|
||||
("command", command, 0),
|
||||
("command-wait", command_wait, 1),
|
||||
];
|
||||
for f in fns {
|
||||
r.define_func(
|
||||
|
|
173
src/stream.rs
173
src/stream.rs
|
@ -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 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>>>> =
|
||||
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(
|
||||
name: &str,
|
||||
|
@ -34,10 +43,11 @@ impl StreamType {
|
|||
pub struct Stream {
|
||||
reader: Box<dyn Read + 'static>,
|
||||
writer: Box<dyn Write + 'static>,
|
||||
close: fn(&mut Self),
|
||||
}
|
||||
|
||||
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);
|
||||
Self {
|
||||
// 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.
|
||||
reader: Box::new(unsafe { mem::transmute::<&mut _, &mut T>(rw.as_mut()) }),
|
||||
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 {
|
||||
reader: Box::new(reader),
|
||||
writer: Box::new(writer),
|
||||
close,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,30 +123,24 @@ where
|
|||
}
|
||||
|
||||
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| {
|
||||
require_on_stack!(s, Str, stack, "write-stream");
|
||||
let stream = get_stream_type(s.clone())
|
||||
.ok_or_else(|| {
|
||||
stack.error(ErrorKind::VariableNotFound(format!("__stream-type-{s}")))
|
||||
})?
|
||||
.make_stream(stack)?;
|
||||
let stream = runtime_mut(move |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)?,
|
||||
stream
|
||||
))
|
||||
})?;
|
||||
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()))
|
||||
};
|
||||
pub fn write_stream(stack: &mut Stack) -> OError {
|
||||
require_on_stack!(id, Mega, stack, "write-stream");
|
||||
require_array_on_stack!(a, stack, "write-stream");
|
||||
let stream = runtime(|rt| {
|
||||
rt.get_stream(id as u128)
|
||||
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
||||
|
@ -158,9 +164,132 @@ pub fn write_to_stream(stack: &mut Stack) -> OError {
|
|||
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>) {
|
||||
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;
|
||||
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 {
|
||||
r.define_func(
|
||||
f.0.to_owned(),
|
||||
|
|
172
std.spl
172
std.spl
|
@ -12,11 +12,6 @@ func println { |
|
|||
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 ;
|
||||
|
@ -24,7 +19,12 @@ construct _mega-ext {
|
|||
i -- mswap
|
||||
}
|
||||
mswap { .. | mswap }
|
||||
foreach { | with this ;
|
||||
def i 0 =i
|
||||
while { i this lt } { i callable call i ++ =i }
|
||||
}
|
||||
} include _mega-ext in mega
|
||||
|
||||
construct _array-ext {
|
||||
;
|
||||
get { any | array-get }
|
||||
|
@ -51,138 +51,24 @@ construct _array-ext {
|
|||
def i 0 =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
|
||||
|
||||
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
|
||||
"iter.spl" import
|
||||
|
||||
construct List {
|
||||
array
|
||||
|
@ -215,11 +101,17 @@ construct _ShrinkingArray {
|
|||
0 this:remove
|
||||
}
|
||||
pop { any | with this ;
|
||||
this:array:len not if {
|
||||
null 2 stop
|
||||
}
|
||||
def item
|
||||
[ this:array:to-stack =item ] this:=array
|
||||
item
|
||||
}
|
||||
remove { any | with index this ;
|
||||
this:array:len not if {
|
||||
null 2 stop
|
||||
}
|
||||
def item
|
||||
this:array:len index - =index
|
||||
[ this:array:to-stack index:mswap =item (index --):mswap ] this:=array
|
||||
|
@ -325,6 +217,7 @@ construct Range {
|
|||
itm
|
||||
2 stop
|
||||
} not if {
|
||||
null
|
||||
2 stop
|
||||
}
|
||||
}
|
||||
|
@ -447,6 +340,12 @@ func -- { mega |
|
|||
|
||||
func _ { | }
|
||||
|
||||
func call-main-on-file { | with file ;
|
||||
"@" file concat import
|
||||
update-types
|
||||
argv main exit
|
||||
}
|
||||
|
||||
func update-types { |
|
||||
{ | with type ;
|
||||
{ self | } "unwrap" type dyn-def-method
|
||||
|
@ -461,6 +360,3 @@ func update-types { |
|
|||
}
|
||||
update-types
|
||||
|
||||
1 argv:sget:unwrap import
|
||||
update-types
|
||||
argv main exit
|
||||
|
|
78
stream.spl
Normal file
78
stream.spl
Normal 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
|
||||
}
|
10
test.spl
10
test.spl
|
@ -1,4 +1,6 @@
|
|||
|
||||
"stream.spl" import
|
||||
|
||||
func main { int | with args ;
|
||||
def thing
|
||||
|
||||
|
@ -97,6 +99,14 @@ func main { int | with args ;
|
|||
argv:iter
|
||||
{ str | " " concat } swap:map
|
||||
&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
|
||||
|
||||
100
|
||||
|
|
1
test.txt
Normal file
1
test.txt
Normal file
|
@ -0,0 +1 @@
|
|||
hi
|
Loading…
Add table
Reference in a new issue