bug fixes, improve speed, add basic http support, other minor improvements

This commit is contained in:
Daniella / Tove 2023-03-06 03:45:16 +01:00
parent 25a30a5be6
commit e093c11558
Signed by: TudbuT
GPG key ID: 7D63D5634B7C417F
11 changed files with 403 additions and 112 deletions

77
http.spl Normal file
View file

@ -0,0 +1,77 @@
"stream.spl" import
construct http namespace {
Request
Response
}
construct http:Request {
host port
method path
headers
body
;
construct { this | with host port method path this ;
host this:=host
port this:=port
method this:=method
path this:=path
List:new this:=headers
"" this:=body
this
}
add-header { this | with header this ;
header this:headers:push
this
}
set-body { this | with body this ;
body this:=body
this
}
send { http:Response | with this ;
def stream this:host this:port StreamTypes:tcp:create =stream
def response http:Response:new =response
this:method:to-bytes stream:write-exact;
" " _:to-bytes stream:write-exact;
this:path:to-bytes stream:write-exact;
" HTTP/1.0\r\n" _:to-bytes stream:write-exact;
"Host: " _:to-bytes stream:write-exact;
this:host:to-bytes stream:write-exact;
"\r\nConnection: Close\r\nUser-Agent: http.spl v0.1 2023-03 (spl@mail.tudbut.de)\r\n"
_:to-bytes stream:write-exact;
{ | with header ;
header:to-bytes stream:write-exact;
"\r\n" stream:write-exact;
} this:headers:foreach
"Content-Length: " _:to-bytes stream:write-exact;
def body this:body:to-bytes =body
body:len _str:to-bytes stream:write-exact;
"\r\n\r\n" _:to-bytes stream:write-exact;
body stream:write-exact;
stream:flush;
1024 stream:read-to-end:to-str println
stream:close;
"todo" panic
}
}
construct http:Response {
state-num state-msg
headers
body
;
construct { this | with this ;
List:new this:=headers
"" this:=body
this
}
}

View file

@ -101,7 +101,7 @@ construct ReduceIter {
} =itm
this:accumulator null eq if {
itm dup this:=accumulator
null 2 stop
2 stop
}
this:accumulator itm this:reduce-function call dup this:=accumulator
}
@ -165,7 +165,7 @@ construct ChainIter {
next-iters
;
construct { this | with other origin this ;
[ other ] List:new this:=next-iters
[ other ] List:new:from this:=next-iters
origin this:=current
this
}

View file

@ -6,7 +6,7 @@ endif
syn match Comment /".*?";/
syn match Number /\<[0-9._]*\>/
syn match Function /\<func[ \n]\+[^ ]\+[ \n]\+{ .*[ ]*|\|{ .*[ ]*|\|{\|}/
syn keyword Keyword while if exit eq lt gt neg or and not + - * ++ -- % / with
syn keyword Keyword while if exit eq lt gt neg or and not + - * ++ -- % / with namespace
syn match Keyword /;/
syn keyword Type pop dup swap
syn match Type /=[a-zA-Z0-9_\-]\+\|\<_[a-zA-Z0-9_\-]\+\>/

View file

@ -39,7 +39,28 @@ pub fn dyn_construct(stack: &mut Stack) -> OError {
return stack.err(ErrorKind::InvalidCall("dyn-construct".to_owned()))
};
Words {
words: vec![Word::Key(Keyword::Construct(s, Vec::new(), Vec::new()))],
words: vec![Word::Key(Keyword::Construct(
s,
Vec::new(),
Vec::new(),
false,
))],
}
.exec(stack)?;
Ok(())
}
pub fn dyn_namespace(stack: &mut Stack) -> OError {
let Value::Str(s) = stack.pop().lock_ro().native.clone() else {
return stack.err(ErrorKind::InvalidCall("dyn-construct".to_owned()))
};
Words {
words: vec![Word::Key(Keyword::Construct(
s,
Vec::new(),
Vec::new(),
true,
))],
}
.exec(stack)?;
Ok(())
@ -245,11 +266,12 @@ pub(crate) fn wrap(f: fn(&mut Stack) -> OError) -> impl Fn(&mut Stack) -> OError
pub fn register(r: &mut Stack, o: Arc<Frame>) {
type Fn = fn(&mut Stack) -> OError;
let fns: [(&str, Fn, u32); 14] = [
let fns: [(&str, Fn, u32); 15] = [
("dyn-__dump", dyn_dump, 0),
("dyn-def", dyn_def, 0),
("dyn-func", dyn_func, 0),
("dyn-construct", dyn_construct, 0),
("dyn-namespace", dyn_namespace, 0),
("dyn-def-field", dyn_def_field, 0),
("dyn-def-method", dyn_def_method, 0),
("dyn-include", dyn_include, 0),

View file

@ -64,6 +64,12 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option<u32>, Words, u
}
"construct" => {
let name = str_words[i + 1].to_owned();
let is_namespace = if str_words[i + 2] == "namespace" {
i += 1;
true
} else {
false
};
if str_words[i + 2] != "{" {
return Err(LexerError::InvalidConstructBlock);
}
@ -91,10 +97,15 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option<u32>, Words, u
i += 1;
}
}
if !has_construct {
if !has_construct && !is_namespace {
methods.push(("construct".to_string(), (1, Words { words: vec![] })));
}
words.push(Word::Key(Keyword::Construct(name, fields, methods)));
words.push(Word::Key(Keyword::Construct(
name,
fields,
methods,
is_namespace,
)));
}
"include" => {
if let Some(x) = readf(

View file

@ -360,6 +360,7 @@ impl Frame {
pub struct Stack {
frames: Vec<Arc<Frame>>,
object_stack: Vec<AMObject>,
files: Vec<String>,
pub return_accumultor: u32,
}
@ -394,6 +395,7 @@ impl Stack {
let mut r = Stack {
frames: vec![o.clone()],
object_stack: Vec::new(),
files: Vec::new(),
return_accumultor: 0,
};
@ -409,6 +411,7 @@ impl Stack {
let mut r = Stack {
frames: vec![o.clone()],
object_stack: Vec::new(),
files: Vec::new(),
return_accumultor: 0,
};
@ -591,6 +594,15 @@ impl Stack {
pub unsafe fn push_frame(&mut self, frame: Arc<Frame>) {
self.frames.push(frame);
}
pub(crate) fn include_file(&mut self, s: &String) -> bool {
if self.files.contains(s) {
false
} else {
self.files.push(s.to_owned());
true
}
}
}
/// An SPL keyword. Used to deviate from normal linear code structure.
@ -621,7 +633,7 @@ pub enum Keyword {
/// equivalent to
/// "<name>" dyn-construct; "<field>" "<name>" dyn-def-field { <rem> | <words> } "<fn-name>"
/// "<name>" dyn-def-method
Construct(String, Vec<String>, Vec<(String, (u32, Words))>),
Construct(String, Vec<String>, Vec<(String, (u32, Words))>, bool),
/// include <typeA> in <typeB>
///
/// Adds <typeA> as a parent type of <typeB>.
@ -949,6 +961,19 @@ impl Object {
Value::Str(x) => !x.is_empty(),
}
}
pub fn field(&self, name: &str, stack: &mut Stack) -> Result<AMObject, Error> {
Ok(self
.property_map
.get(name)
.ok_or_else(|| {
stack.error(ErrorKind::PropertyNotFound(
self.kind.lock_ro().name.to_owned(),
name.to_owned(),
))
})?
.clone())
}
}
impl From<Value> for Object {
@ -1025,14 +1050,13 @@ impl Words {
name,
}),
),
Keyword::Construct(name, fields, methods) => {
Keyword::Construct(name, fields, methods, is_namespace) => {
let origin = stack.get_frame();
if !name.contains(':') {
stack.define_var(name.clone());
stack.set_var(
name.clone(),
Value::Str(
runtime_mut(move |mut rt| {
rt.make_type(name.clone(), move |mut t| {
}
let t = runtime_mut(|mut rt| {
rt.make_type(name.clone(), |mut t| {
for field in fields {
t.add_property(field, origin.clone())?;
}
@ -1051,12 +1075,30 @@ impl Words {
}));
Ok(t)
})
})?
.lock_ro()
.get_name(),
)
.spl(),
)?;
})?;
let to_set: Object = if is_namespace {
let mut obj: Object = Value::Null.into();
obj.kind = t.clone();
t.lock_ro().write_into(&mut obj);
obj
} else {
Value::Str(t.lock_ro().get_name()).into()
};
if name.contains(':') {
let Some((a, mut name)) = name.split_once(':') else { unreachable!() };
let mut f = stack.get_var(a.to_owned())?;
while let Some((a, b)) = name.split_once(':') {
name = b;
let o = f.lock_ro();
let nf = o.field(a, stack)?;
mem::drop(o);
f = nf;
}
*f.lock_ro().field(name, stack)?.lock() = to_set;
} else {
stack.set_var(name.clone(), to_set.spl())?;
}
}
Keyword::Include(ta, tb) => {
let rstack = &stack;

View file

@ -4,11 +4,17 @@ use std::{
fs,
io::{stdin, stdout, Write},
mem,
ops::{Add, Div, Mul, Rem, Sub},
process::{self, Stdio},
sync::Arc,
};
use crate::{dyn_fns, mutex::Mut, runtime::*, *};
use crate::{
dyn_fns,
mutex::Mut,
runtime::*,
*,
};
#[macro_export]
macro_rules! type_err {
@ -185,14 +191,30 @@ pub fn or(stack: &mut Stack) -> OError {
Ok(())
}
macro_rules! impl_op {
($a:expr, $b:expr, $op:tt, $err:expr, $($kind:tt,)*) => {
match ($a, $b) {
$(
(Value::$kind(a), Value::$kind(b)) => Value::$kind(a.$op(b)),
)*
_ => $err?,
}
};
}
pub fn plus(stack: &mut Stack) -> OError {
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),
_x => stack.err(ErrorKind::InvalidCall("plus".to_owned()))?,
}
impl_op!(
a,
b,
add,
stack.err(ErrorKind::InvalidCall("plus".to_owned())),
Mega,
Long,
Int,
)
.spl(),
);
Ok(())
@ -202,10 +224,15 @@ pub fn minus(stack: &mut Stack) -> OError {
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!(),
}
impl_op!(
a,
b,
sub,
stack.err(ErrorKind::InvalidCall("minus".to_owned())),
Mega,
Long,
Int,
)
.spl(),
);
Ok(())
@ -215,10 +242,15 @@ pub fn slash(stack: &mut Stack) -> OError {
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!(),
}
impl_op!(
a,
b,
div,
stack.err(ErrorKind::InvalidCall("slash".to_owned())),
Mega,
Long,
Int,
)
.spl(),
);
Ok(())
@ -228,10 +260,15 @@ pub fn star(stack: &mut Stack) -> OError {
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!(),
}
impl_op!(
a,
b,
mul,
stack.err(ErrorKind::InvalidCall("star".to_owned())),
Mega,
Long,
Int,
)
.spl(),
);
Ok(())
@ -241,10 +278,15 @@ pub fn percent(stack: &mut Stack) -> OError {
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!(),
}
impl_op!(
a,
b,
rem,
stack.err(ErrorKind::InvalidCall("star".to_owned())),
Mega,
Long,
Int,
)
.spl(),
);
Ok(())
@ -589,6 +631,13 @@ pub fn import(stack: &mut Stack) -> OError {
+ "/"
+ &s;
}
if stack.include_file(
&(*fs::canonicalize(s.clone())
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?
.as_os_str()
.to_string_lossy())
.to_owned(),
) {
stack.push(Value::Str(s).spl());
dup(stack)?;
read_file(stack).or_else(|x| {
@ -601,6 +650,7 @@ pub fn import(stack: &mut Stack) -> OError {
})?;
dyn_fns::wrap(dyn_fns::dyn_readf)(stack)?;
call(stack)?;
}
Ok(())
}
@ -710,9 +760,33 @@ pub fn bytes_to_str(stack: &mut Stack) -> OError {
Ok(())
}
pub fn acopy(stack: &mut Stack) -> OError {
require_on_stack!(len, Mega, stack, "acopy");
require_on_stack!(idx_dest, Mega, stack, "acopy");
require_on_stack!(idx_src, Mega, stack, "acopy");
let dest_array = stack.pop();
{
require_mut_array!(dest, dest_array, stack, "acopy");
require_array_on_stack!(src, stack, "acopy");
let offset = idx_dest - idx_src;
if (src.len() as i128) < idx_src + len
|| idx_src < 0
|| (dest.len() as i128) < idx_dest + len
|| idx_dest < 0
{
stack.err(ErrorKind::InvalidCall("acopy".to_owned()))?;
}
for i in idx_src..idx_src + len {
*dest.get_mut((i + offset) as usize).unwrap() = src.get(i as usize).unwrap().clone();
}
}
stack.push(dest_array);
Ok(())
}
pub fn register(r: &mut Stack, o: Arc<Frame>) {
type Fn = fn(&mut Stack) -> OError;
let fns: [(&str, Fn, u32); 48] = [
let fns: [(&str, Fn, u32); 49] = [
("pop", pop, 0),
("dup", dup, 2),
("clone", clone, 1),
@ -761,6 +835,7 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
("command-wait", command_wait, 1),
("str-to-bytes", str_to_bytes, 1),
("bytes-to-str", bytes_to_str, 1),
("acopy", acopy, 1),
];
for f in fns {
r.define_func(

View file

@ -4,7 +4,7 @@ use std::{
io::Read,
io::Write,
mem,
net::{Shutdown, TcpStream},
net::{Shutdown, TcpStream, UdpSocket},
sync::Arc,
};
@ -187,6 +187,19 @@ pub fn write_all_stream(stack: &mut Stack) -> OError {
Ok(())
}
pub fn flush_stream(stack: &mut Stack) -> OError {
require_on_stack!(id, Mega, stack, "flush-stream");
let stream = runtime(|rt| {
rt.get_stream(id as u128)
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
})?;
stream
.lock()
.flush()
.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");
let array = stack.pop();
@ -285,18 +298,48 @@ fn stream_tcp(stack: &mut Stack) -> Result<Stream, Error> {
))
}
fn stream_udp(stack: &mut Stack) -> Result<Stream, Error> {
require_int_on_stack!(port, stack, "UDP new-stream");
require_on_stack!(ip, Str, stack, "UDP new-stream");
require_int_on_stack!(self_port, stack, "UDP new-stream");
require_on_stack!(self_ip, Str, stack, "UDP new-stream");
fn close_udp(_stream: &mut Stream) {}
let sock = UdpSocket::bind((self_ip, self_port as u16))
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?;
sock.connect((ip, port as u16))
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?;
struct UdpRW(UdpSocket);
impl Write for UdpRW {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.0.send(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl Read for UdpRW {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.0.recv(buf)
}
}
Ok(Stream::new(UdpRW(sock), close_udp))
}
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);
register_stream_type("udp", stream_udp);
*IS_INITIALIZED.lock() = true;
}
type Fn = fn(&mut Stack) -> OError;
let fns: [(&str, Fn, u32); 6] = [
let fns: [(&str, Fn, u32); 7] = [
("new-stream", new_stream, 1),
("write-stream", write_stream, 1),
("write-all-stream", write_all_stream, 0),
("flush-stream", flush_stream, 0),
("read-stream", read_stream, 1),
("read-all-stream", read_all_stream, 0),
("close-stream", close_stream, 0),

49
std.spl
View file

@ -11,7 +11,7 @@ func println { |
construct _str_ext {
;
new { any | with this ;
null clone this settype "construct" dyn-objcall
null clone this settype:construct
}
to-bytes { [int] | str-to-bytes }
} include _str_ext in str
@ -56,6 +56,9 @@ construct _array-ext {
while { i this:len lt } { i this:get callable call i ++ =i }
}
to-str { str | bytes-to-str }
sub { [any] | with begin end this ;
this (end begin - anew) begin 0 (end begin -) acopy
}
0 { any | with this ;
0 this:get
}
@ -78,14 +81,22 @@ construct _array-ext {
construct List {
array
;
construct { this | with array this ;
construct { this | with this ;
0 anew this:=array
this
}
from { this | with array this ;
array this:=array
this
}
foreach { | _:array:foreach }
get { any | _:array:get }
sget { any|null | _:array:sget }
len { mega | _:array:len }
set { any | _:array:set }
to-stack { .. | _:array:to-stack }
to-str { str | _:array:to-str }
sub { [any] | _:array:sub }
}
construct _GrowingArray {
;
@ -159,11 +170,12 @@ include _IterableArray in array
construct MicroMap {
pairs
;
construct { this | with pairs this ;
pairs null eq if {
0 anew List:new =pairs
construct { this | with this ;
List:new this:=pairs
this
}
pairs:unwrap this:=pairs
from { this | with pairs this ;
pairs this:=pairs
this
}
get-entry { [any,any]|null | with key this ;
@ -189,12 +201,15 @@ construct MicroMap {
this:pairs:iter
{ mega | 0 swap:get key eq not } swap:filter
_:collect
List:new
List:new:from
=pairs
}
iter { ArrayIter | with this ;
this:pairs:iter
}
foreach { | with callable this ;
callable this:pairs:foreach
}
}
construct Range {
@ -246,24 +261,12 @@ 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
arr1 newarr 0 0 arr1:len acopy;
arr2 newarr 0 arr1:len arr2:len acopy;
newarr
}
@ -281,7 +284,7 @@ func panic { | with msg ;
{ | with it ;
it println
} trace:foreach
"Panic message:" println
"\nPanic message:" println
" " print msg println
def map env =map
"SPL_PANIC_DUMP" env:get dup if {
@ -332,7 +335,7 @@ func ] { array |
}
func env { MicroMap |
get-env List:new MicroMap:new
get-env List:new:from MicroMap:new:from
}
func ++ { mega |

View file

@ -21,20 +21,32 @@ construct Stream {
}
"the buffer is written to in-place.";
read { mega [int] | with buf this ;
buf gettype "int" eq if { buf anew =buf }
buf gettype "mega" 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 gettype "mega" eq if { buf anew =buf }
buf this:id read-all-stream buf
}
read-to-end { [int] | with buf this ;
def full 0 anew =full
buf gettype "mega" eq if { buf anew =buf }
def read
while { buf this:id read-stream pop _mega dup =read } {
full (0 read buf:sub) aadd =full
}
full
}
write { mega | with buf this ;
buf this:id write-stream
}
write-exact { | with buf this ;
buf this:id write-all-stream
}
flush { | with this ;
this:id flush-stream
}
close { | with this ;
this:id close-stream
}
@ -71,6 +83,7 @@ func register-stream-type { | with id ;
}
"tcp" register-stream-type
"udp" register-stream-type
"file" register-stream-type
func StreamTypes { _StreamType |

View file

@ -1,5 +1,6 @@
"stream.spl" import
"http.spl" import
func main { int | with args ;
def thing
@ -8,7 +9,7 @@ func main { int | with args ;
"hi" 0 thing:unwrap:set;
def thing2 thing:unwrap List:new =thing2
def thing2 thing:unwrap List:new:from =thing2
"world" thing2:unwrap:push
"hello" 0 thing2:unwrap:insert
@ -80,18 +81,17 @@ func main { int | with args ;
"" println
"testing MicroMap" println
def map null MicroMap:new =map
def map 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
} map:foreach
"}" println
"" println
@ -109,5 +109,10 @@ func main { int | with args ;
"" println
"testing http" println
def req "tudbut.de" 80 "GET" "/" http:Request:new =req
req:send
"" println
100
}