Compare commits
41 commits
Author | SHA1 | Date | |
---|---|---|---|
3445b27fa9 | |||
1b1855fe5a | |||
5c8875eb55 | |||
a8a2616a41 | |||
2bbeaebd7c | |||
35639cb2c8 | |||
c45c538952 | |||
c3fffb1e75 | |||
290630adbe | |||
e72baba154 | |||
210eaade3a | |||
51212e139a | |||
95afcf9940 | |||
a8b0a1bb53 | |||
2e126a6652 | |||
fccce8b705 | |||
eb1335ade4 | |||
ea8d3f5a6f | |||
c1475cc153 | |||
456d39399e | |||
4749c142fa | |||
c9463c6938 | |||
a9894d8a03 | |||
1c1f9a4566 | |||
e48f06a8b5 | |||
535e4816f8 | |||
b454fe9dee | |||
416a310072 | |||
706c4f023b | |||
d4dc588c35 | |||
89d14146be | |||
2446272181 | |||
6f2777b83a | |||
fd41c46cdd | |||
15ae8622e1 | |||
bb4d163b40 | |||
f86c55dd83 | |||
a1f5941c1a | |||
6d78b276d4 | |||
0fb01cfb20 | |||
2f8c50e3f0 |
33 changed files with 2099 additions and 343 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -8,12 +8,6 @@ version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "428f6ba17d0c927e57c15a86cf5d7d07a2f35b3fbf15b1eb36b7075459e150a3"
|
checksum = "428f6ba17d0c927e57c15a86cf5d7d07a2f35b3fbf15b1eb36b7075459e150a3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.17.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "readformat"
|
name = "readformat"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -22,9 +16,8 @@ checksum = "b03f7fbd470aa8b3ad163c85cce8bccfc11cc9c44ef12da0a4eddd98bd307352"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spl"
|
name = "spl"
|
||||||
version = "0.3.1"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"multicall",
|
"multicall",
|
||||||
"once_cell",
|
|
||||||
"readformat",
|
"readformat",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "spl"
|
name = "spl"
|
||||||
version = "0.3.2"
|
version = "0.4.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Stack Pogramming Language: A simple, concise scripting language."
|
description = "Stack Pogramming Language: A simple, concise scripting language."
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
@ -9,5 +9,4 @@ authors = ["TudbuT"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
readformat = "0.1"
|
readformat = "0.1"
|
||||||
once_cell = "1.17"
|
|
||||||
multicall = "0.1"
|
multicall = "0.1"
|
||||||
|
|
61
benchmark.spl
Normal file
61
benchmark.spl
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
"#time.spl" import
|
||||||
|
|
||||||
|
func main { mega | with args ;
|
||||||
|
def begin
|
||||||
|
def end
|
||||||
|
|
||||||
|
"[-] spl benchmark v0.1.0" println
|
||||||
|
"[0] benchmarking while loop with variable 0..100000" println
|
||||||
|
"==> bgin at " print time:unixms dup =begin println
|
||||||
|
def i 0 =i
|
||||||
|
while { i 100000 lt } {
|
||||||
|
i ++ =i
|
||||||
|
}
|
||||||
|
"==> done at " print time:unixms dup =end println
|
||||||
|
"==> in " print end begin - println
|
||||||
|
|
||||||
|
"[1] benchmarking foreach on 100000" println
|
||||||
|
"==> bgin at " print time:unixms dup =begin println
|
||||||
|
{ | pop } 100000 :foreach
|
||||||
|
"==> done at " print time:unixms dup =end println
|
||||||
|
"==> in " print end begin - println
|
||||||
|
|
||||||
|
"[2] benchmarking fast foreach on 100000" println
|
||||||
|
"==> bgin at " print time:unixms dup =begin println
|
||||||
|
{ | pop } 100000 :fforeach
|
||||||
|
"==> done at " print time:unixms dup =end println
|
||||||
|
"==> in " print end begin - println
|
||||||
|
|
||||||
|
"[3] benchmarking foreach on Range 100000..200000" println
|
||||||
|
"==> bgin at " print time:unixms dup =begin println
|
||||||
|
100000 200000 Range:new
|
||||||
|
:iter
|
||||||
|
:foreach <{ | with i ; }>
|
||||||
|
"==> done at " print time:unixms dup =end println
|
||||||
|
"==> in " print end begin - println
|
||||||
|
|
||||||
|
"[4] benchmarking manual multiply of 100000 x 5" println
|
||||||
|
"==> bgin at " print time:unixms dup =begin println
|
||||||
|
def i 0 =i
|
||||||
|
def n 0 =n
|
||||||
|
while { i 100000 lt } {
|
||||||
|
i ++ =i
|
||||||
|
n 5 + =n
|
||||||
|
}
|
||||||
|
" -> n = " print n println
|
||||||
|
"==> done at " print time:unixms dup =end println
|
||||||
|
"==> in " print end begin - println
|
||||||
|
|
||||||
|
"[5] benchmarking 10000 array adds" println
|
||||||
|
"==> bgin at " print time:unixms dup =begin println
|
||||||
|
def i 0 =i
|
||||||
|
def arr 0 anew =arr
|
||||||
|
while { i 10000 lt } {
|
||||||
|
i awrap arr aadd =arr
|
||||||
|
i ++ =i
|
||||||
|
}
|
||||||
|
"==> done at " print time:unixms dup =end println
|
||||||
|
"==> in " print end begin - println
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
func main { mega | with args ;
|
func main { mega | with args ;
|
||||||
[ "sudo" "mkdir" "/usr/lib/spl" ] command-wait;
|
[ "sudo" "mkdir" "/usr/lib/spl" ] command-wait;
|
||||||
[ "sh" "-c" "sudo cp *.spl /usr/lib/spl" ] command-wait;
|
[ "sh" "-c" "sudo cp -r spl/* /usr/lib/spl" ] command-wait;
|
||||||
[ "cargo" "build" "--release" ] command-wait;
|
[ "cargo" "build" "--release" ] command-wait;
|
||||||
[ "sudo" "rm" "/bin/spl" ] command-wait;
|
[ "sudo" "rm" "/bin/spl" ] command-wait;
|
||||||
[ "sudo" "cp" "target/release/spl" "/bin" ] command-wait;
|
[ "sudo" "cp" "target/release/spl" "/bin" ] command-wait;
|
||||||
|
|
29
repl.spl
29
repl.spl
|
@ -1,29 +0,0 @@
|
||||||
|
|
||||||
func main { mega | with args ;
|
|
||||||
"Welcome to the SPL REPL!" println
|
|
||||||
"Enter any code after the cursor to execute it.\n" println
|
|
||||||
|
|
||||||
"REPL" =program-name
|
|
||||||
while { 1 } {
|
|
||||||
catch {
|
|
||||||
" > " print readln dyn-read exec2 "\n" print
|
|
||||||
}
|
|
||||||
{ with err ;
|
|
||||||
err:message dup null eq if {
|
|
||||||
pop
|
|
||||||
"Uncaught error."
|
|
||||||
} err:trace
|
|
||||||
|
|
||||||
with msg trace ;
|
|
||||||
program-name dup if {
|
|
||||||
program-name print " panicked at:" println
|
|
||||||
} not if {
|
|
||||||
"Program panicked at:" println
|
|
||||||
}
|
|
||||||
&println trace:foreach
|
|
||||||
"\nPanic message:" println
|
|
||||||
" " print msg println
|
|
||||||
"\nRecovering." println
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
89
spl/fast.spl
Normal file
89
spl/fast.spl
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
|
||||||
|
"#stream.spl" import
|
||||||
|
"#http.spl" import
|
||||||
|
|
||||||
|
func ls { files | "." list-files }
|
||||||
|
func ls@ { files | list-files }
|
||||||
|
func cd { | chdir }
|
||||||
|
func cat { | read-file }
|
||||||
|
|
||||||
|
func output { | with thing ;
|
||||||
|
thing gettype "array" eq
|
||||||
|
thing gettype:ends-with<^Iter> or
|
||||||
|
dup if {
|
||||||
|
thing:foreach<&println>
|
||||||
|
}
|
||||||
|
not if {
|
||||||
|
thing _str println
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func *m { | swap :ends-with }
|
||||||
|
func m* { | swap :starts-with }
|
||||||
|
func # { | swap :filter }
|
||||||
|
func ? { | swap :map }
|
||||||
|
func times { | :iter }
|
||||||
|
|
||||||
|
func \ { | pop }
|
||||||
|
|
||||||
|
func ~@ { | env:get<"HOME"> "/" concat swap concat }
|
||||||
|
func ~ { | env:get<"HOME"> }
|
||||||
|
func $ { v | env:get }
|
||||||
|
|
||||||
|
func . { | output }
|
||||||
|
func ex { | with s ; [ ^sh ^-c s ] command-wait; }
|
||||||
|
func ex% { | command-wait; }
|
||||||
|
func ex. { | with s ; [ ^sh ^-c s ] StreamTypes:cmd:create:read-to-end<1024 16 *> }
|
||||||
|
func ex%. { | StreamTypes:cmd:create:read-to-end<1024 16 *> }
|
||||||
|
func into { | with input file ;
|
||||||
|
input gettype any<[ { | "array" eq } { | "bytearray" eq } ]> not if { input _str:to-bytes =input }
|
||||||
|
file 1 StreamTypes:file:create
|
||||||
|
dup :write-exact;<input>
|
||||||
|
:close;
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcp { stream |
|
||||||
|
StreamTypes:tcp:create
|
||||||
|
}
|
||||||
|
|
||||||
|
func curl { s | bcurl:to-str }
|
||||||
|
|
||||||
|
func bcurl { bytes | with url ;
|
||||||
|
1 if {
|
||||||
|
url:readf<"{}:{}/{}"> dup if {
|
||||||
|
=url
|
||||||
|
url:0 url:1 _int "GET" "/" url:2 concat net:http:Request:new
|
||||||
|
2 stop
|
||||||
|
} pop
|
||||||
|
url:readf<"{}:{}"> dup if {
|
||||||
|
=url
|
||||||
|
url:0 url:1 _int "GET" "/" net:http:Request:new
|
||||||
|
2 stop
|
||||||
|
} pop
|
||||||
|
url:readf<"{}/{}"> dup if {
|
||||||
|
=url
|
||||||
|
url:0 80 "GET" "/" url:1 concat net:http:Request:new
|
||||||
|
2 stop
|
||||||
|
} pop
|
||||||
|
url:readf<"{}"> dup if {
|
||||||
|
=url
|
||||||
|
url:0 80 "GET" "/" net:http:Request:new
|
||||||
|
2 stop
|
||||||
|
} pop
|
||||||
|
"invalid url" panic
|
||||||
|
} :send:body
|
||||||
|
}
|
||||||
|
|
||||||
|
construct _shell_array_ext {
|
||||||
|
idx
|
||||||
|
;
|
||||||
|
next { any | with this ;
|
||||||
|
this:idx 0 or dup ++ this:=idx this:sget
|
||||||
|
}
|
||||||
|
}
|
||||||
|
include _shell_array_ext in array
|
||||||
|
include _Iter in array
|
||||||
|
|
||||||
|
func any { bool | with o checks ;
|
||||||
|
0 checks:foreach<{ | o swap call if { pop 1 } }>
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
"#stream.spl" import
|
"stream.spl" import
|
||||||
"#net.spl" import
|
"net.spl" import
|
||||||
|
|
||||||
"http" net:register
|
"http" net:register
|
||||||
|
|
||||||
|
@ -7,6 +7,44 @@ construct net:http namespace {
|
||||||
Request
|
Request
|
||||||
Response
|
Response
|
||||||
help
|
help
|
||||||
|
;
|
||||||
|
register { | with name this ;
|
||||||
|
name "net:http" register-field
|
||||||
|
}
|
||||||
|
urlencode { str | with str this ;
|
||||||
|
[ { | with x ;
|
||||||
|
x
|
||||||
|
x "0" :_char lt not
|
||||||
|
x "9" :_char gt not and
|
||||||
|
x "a" :_char lt not
|
||||||
|
x "z" :_char gt not and or
|
||||||
|
x "A" :_char lt not
|
||||||
|
x "Z" :_char gt not and or
|
||||||
|
not
|
||||||
|
if {
|
||||||
|
pop
|
||||||
|
"%" :_char
|
||||||
|
x _mega:_str_radix<16> _array =x
|
||||||
|
x:len 1 eq if { "0" :_char }
|
||||||
|
x:to-stack
|
||||||
|
}
|
||||||
|
} str _array :foreach ] _str
|
||||||
|
}
|
||||||
|
urldecode { str | with str this ;
|
||||||
|
str _array =str
|
||||||
|
[
|
||||||
|
def i 0 =i
|
||||||
|
while { i str:len lt } {
|
||||||
|
i str:get dup "%" :_char eq if {
|
||||||
|
pop
|
||||||
|
i ++ =i
|
||||||
|
i i 2 + str:sub _str :_mega_radix<16> _int
|
||||||
|
i ++ =i
|
||||||
|
}
|
||||||
|
i ++ =i
|
||||||
|
}
|
||||||
|
] _str
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
construct net:http:Request {
|
construct net:http:Request {
|
164
spl/httpserver/base.spl
Normal file
164
spl/httpserver/base.spl
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
"../http.spl" import
|
||||||
|
"../server.spl" import
|
||||||
|
|
||||||
|
"Server" net:http:register
|
||||||
|
"server" net:http:register
|
||||||
|
|
||||||
|
construct net:http:Server {
|
||||||
|
ifaddr
|
||||||
|
port
|
||||||
|
stream
|
||||||
|
;
|
||||||
|
construct { this | with ifaddr port this ;
|
||||||
|
ifaddr this:=ifaddr;
|
||||||
|
port this:=port;
|
||||||
|
ifaddr port net:server:Types:tcp:create this:=stream;
|
||||||
|
this
|
||||||
|
}
|
||||||
|
accept { net:http:server:Request | with this ;
|
||||||
|
this this:stream:accept net:http:server:Request:new
|
||||||
|
}
|
||||||
|
close { | with this ; this:stream:close; }
|
||||||
|
}
|
||||||
|
|
||||||
|
construct net:http:server namespace {
|
||||||
|
Request
|
||||||
|
;
|
||||||
|
register { | with name this ;
|
||||||
|
name "net:http:server" register-field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
construct net:http:server:Request {
|
||||||
|
server
|
||||||
|
stream
|
||||||
|
head
|
||||||
|
body
|
||||||
|
method raw-path version
|
||||||
|
path query
|
||||||
|
headers
|
||||||
|
wrote-body
|
||||||
|
;
|
||||||
|
construct { this | with server stream this ;
|
||||||
|
server this:=server
|
||||||
|
stream this:=stream
|
||||||
|
0 banew this:=head
|
||||||
|
0 banew this:=body
|
||||||
|
this
|
||||||
|
}
|
||||||
|
read-head { this | with this ;
|
||||||
|
def read
|
||||||
|
def buf 1024 banew =buf
|
||||||
|
def found
|
||||||
|
while {
|
||||||
|
buf this:stream:read pop dup =read
|
||||||
|
"\r\n\r\n" :to-bytes buf:find dup =found null eq and
|
||||||
|
} {
|
||||||
|
this:head buf:sub<0 read>:replace<"\r" :to-bytes 0 banew> aadd this:=head
|
||||||
|
"\n\n" :to-bytes this:head:find dup null eq not if {
|
||||||
|
=found
|
||||||
|
this:head:sub<0 found>:to-str this:=head
|
||||||
|
1 ( buf:0 "\r" eq if { pop 2 } ) buf:len buf:sub =buf
|
||||||
|
buf this:=body
|
||||||
|
this
|
||||||
|
4 stop
|
||||||
|
0 } pop
|
||||||
|
}
|
||||||
|
this:head buf:sub<0 found> aadd:to-str this:=head
|
||||||
|
buf:sub<found 4 + buf:len> this:=body
|
||||||
|
this
|
||||||
|
}
|
||||||
|
parse-head { this | with this ;
|
||||||
|
this:head:split<"\n"> this:=head
|
||||||
|
def iter this:head:iter =iter
|
||||||
|
iter:next:readf<"{} {} HTTP/{}"> dup if {
|
||||||
|
dup:to-stack this:=version this:=raw-path this:=method
|
||||||
|
} pop
|
||||||
|
MicroMap:new this:=query
|
||||||
|
def p "{}?{}" this:raw-path:readf =p
|
||||||
|
this:raw-path
|
||||||
|
p if {
|
||||||
|
{ | with attrib ;
|
||||||
|
"{}={}" attrib:readf dup if {
|
||||||
|
dup:to-stack this:query:set;
|
||||||
|
} not if {
|
||||||
|
attrib "" this:query:set;
|
||||||
|
}
|
||||||
|
} "&" p:1:split:foreach
|
||||||
|
pop p:0
|
||||||
|
} this:=path
|
||||||
|
MicroMap:new this:=headers
|
||||||
|
iter:foreach<{ | with header ;
|
||||||
|
header:readf<"{}: {}"> dup if {
|
||||||
|
dup:to-stack swap:lowercase swap this:headers:set;
|
||||||
|
} pop
|
||||||
|
}>
|
||||||
|
this
|
||||||
|
}
|
||||||
|
head-str { str | with this ;
|
||||||
|
this:method
|
||||||
|
" " concat
|
||||||
|
this:raw-path concat
|
||||||
|
" HTTP/" concat
|
||||||
|
this:version concat
|
||||||
|
"\r\n" concat
|
||||||
|
this:headers:foreach<{ | :to-stack ": " swap concat concat concat "\r\n" concat }>
|
||||||
|
"\r\n" concat
|
||||||
|
}
|
||||||
|
read-body { this | with this ;
|
||||||
|
this:headers:get<"content-length"> dup if { _mega with content-length ;
|
||||||
|
def read
|
||||||
|
def buf 1024 banew =buf
|
||||||
|
while {
|
||||||
|
this:body:len content-length lt read and
|
||||||
|
} {
|
||||||
|
this:body buf:sub<0 read> aadd this:=body
|
||||||
|
buf this:stream:read pop =read
|
||||||
|
}
|
||||||
|
null
|
||||||
|
} pop
|
||||||
|
this
|
||||||
|
}
|
||||||
|
read { this | with this ;
|
||||||
|
this:read-head:parse-head:read-body
|
||||||
|
}
|
||||||
|
writeln { this | with line this ;
|
||||||
|
line "\r\n" concat :to-bytes this:stream:write-exact;
|
||||||
|
this
|
||||||
|
}
|
||||||
|
write-head { this | with status-code status-string this ;
|
||||||
|
"HTTP/1.0 " status-code _str concat " " concat status-string concat this:writeln
|
||||||
|
}
|
||||||
|
write-ok { this | with this ;
|
||||||
|
200 "OK" this:write-head
|
||||||
|
}
|
||||||
|
write-header { this | with header value this ;
|
||||||
|
header ": " concat value _str concat this:writeln
|
||||||
|
}
|
||||||
|
write-content-type { this | with ct this ;
|
||||||
|
"Content-Type" ct this:write-header
|
||||||
|
}
|
||||||
|
write-str-body { this | with body this ;
|
||||||
|
body:to-bytes this:write-body
|
||||||
|
}
|
||||||
|
write-html-body { this | with body this ;
|
||||||
|
"text/html" this:write-content-type;
|
||||||
|
body:to-bytes this:write-body
|
||||||
|
}
|
||||||
|
write-body { this | with body this ;
|
||||||
|
"Content-Length" body:len this:write-header;
|
||||||
|
"" this:writeln;
|
||||||
|
1 this:=wrote-body;
|
||||||
|
body this:stream:write-exact;
|
||||||
|
this
|
||||||
|
}
|
||||||
|
finish { | with this ;
|
||||||
|
this:wrote-body not if {
|
||||||
|
0 banew this:write-body;
|
||||||
|
}
|
||||||
|
this:stream:close;
|
||||||
|
}
|
||||||
|
is-open { bool | with this ;
|
||||||
|
this:wrote-body not
|
||||||
|
}
|
||||||
|
}
|
56
spl/httpserver/static.spl
Normal file
56
spl/httpserver/static.spl
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
"base.spl" import
|
||||||
|
"../stream.spl" import
|
||||||
|
|
||||||
|
"_static_ext_server" net:http:server:register
|
||||||
|
"_static_ext_Request" net:http:server:register
|
||||||
|
"bufsize" net:http:server:register
|
||||||
|
"client-cache" net:http:server:register
|
||||||
|
1024 net:http:server:=bufsize
|
||||||
|
|
||||||
|
construct net:http:server:_static_ext_server {
|
||||||
|
bufsize
|
||||||
|
client-cache
|
||||||
|
cached-files
|
||||||
|
;
|
||||||
|
get-cached-files { cached-files | with this ;
|
||||||
|
this:cached-files dup not if { pop MicroMap:new dup this:=cached-files }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
construct net:http:server:_static_ext_Request {
|
||||||
|
;
|
||||||
|
get-bufsize { bufsize | with this ;
|
||||||
|
this:bufsize net:http:server:bufsize or
|
||||||
|
}
|
||||||
|
get-client-cache { client-cache | with this ;
|
||||||
|
this:client-cache net:http:server:client-cache or
|
||||||
|
}
|
||||||
|
serve-file { this | with filepath path type this ;
|
||||||
|
this:path path eq if {
|
||||||
|
filepath StreamTypes:file:create<0>:read-to-end<this:get-bufsize> this:write-ok:write-content-type<type>:write-body:finish;
|
||||||
|
}
|
||||||
|
this
|
||||||
|
}
|
||||||
|
serve-file-cached { this | with filepath path type this ;
|
||||||
|
this:path path eq if {
|
||||||
|
filepath this:server:get-cached-files:get dup not if {
|
||||||
|
pop filepath StreamTypes:file:create<0>:read-to-end<this:get-bufsize> dup this:server:cached-files:set;<filepath>
|
||||||
|
}
|
||||||
|
def cache this:get-client-cache =cache
|
||||||
|
this:write-ok cache if { :write-header<"Cache-Control" "public, max-age=" cache _str concat> } :write-content-type<type>:write-body:finish;
|
||||||
|
}
|
||||||
|
this
|
||||||
|
}
|
||||||
|
serve-string { this | with string path type this ;
|
||||||
|
this:path path eq if {
|
||||||
|
string this:write-ok:write-content-type<type>:write-str-body:finish;
|
||||||
|
}
|
||||||
|
this
|
||||||
|
}
|
||||||
|
serve-html { this | with this ; "text/html" this:serve-file }
|
||||||
|
serve-html-cached { this | with this ; "text/html" this:serve-file-cached }
|
||||||
|
serve-html-string { this | with this ; "text/html" this:serve-string }
|
||||||
|
}
|
||||||
|
|
||||||
|
include net:http:server:_static_ext_server in net:http:Server
|
||||||
|
include net:http:server:_static_ext_Request in net:http:server:Request
|
|
@ -1,3 +1,4 @@
|
||||||
|
"#time.spl" import
|
||||||
native engage-compatibility-mode
|
native engage-compatibility-mode
|
||||||
func # { pop }
|
func # { pop }
|
||||||
func puts { print }
|
func puts { print }
|
||||||
|
@ -5,6 +6,8 @@ func fcall { call }
|
||||||
func strconcat { concat }
|
func strconcat { concat }
|
||||||
func aget { swap :get }
|
func aget { swap :get }
|
||||||
func stoi { _mega }
|
func stoi { _mega }
|
||||||
|
func stol { _mega }
|
||||||
|
func _int { _mega }
|
||||||
func itos { _str }
|
func itos { _str }
|
||||||
def argv'pre-isbplmod &argv =argv'pre-isbplmod
|
def argv'pre-isbplmod &argv =argv'pre-isbplmod
|
||||||
func argv {
|
func argv {
|
||||||
|
@ -13,6 +16,22 @@ func argv {
|
||||||
args nargs 2 0 nargs:len acopy
|
args nargs 2 0 nargs:len acopy
|
||||||
nargs
|
nargs
|
||||||
}
|
}
|
||||||
|
func eputs {
|
||||||
|
print
|
||||||
|
}
|
||||||
|
def import-transformers List:new =import-transformers
|
||||||
|
func isbplmod'import-transform { with s ;
|
||||||
|
"spl: [compat] transforming import " s concat println
|
||||||
|
s:_char "#" :_char eq if {
|
||||||
|
"#nop.spl" =s
|
||||||
|
}
|
||||||
|
|
||||||
|
{ with tf ;
|
||||||
|
s tf:call =s
|
||||||
|
} import-transformers:foreach
|
||||||
|
"spl: [compat] transformed to " s concat println
|
||||||
|
s
|
||||||
|
}
|
||||||
func inc {
|
func inc {
|
||||||
"isbpl.spl: ISBPL code tried to call inc, an untranslatable function" println
|
"isbpl.spl: ISBPL code tried to call inc, an untranslatable function" println
|
||||||
pop
|
pop
|
||||||
|
@ -21,3 +40,11 @@ func dec {
|
||||||
"isbpl.spl: ISBPL code tried to call inc, an untranslatable function" println
|
"isbpl.spl: ISBPL code tried to call inc, an untranslatable function" println
|
||||||
pop
|
pop
|
||||||
}
|
}
|
||||||
|
func getms {
|
||||||
|
0 time:unixms
|
||||||
|
}
|
||||||
|
func sleep {
|
||||||
|
time:sleep
|
||||||
|
}
|
||||||
|
|
||||||
|
func main { }
|
|
@ -11,13 +11,14 @@ construct _Iter {
|
||||||
arr
|
arr
|
||||||
}
|
}
|
||||||
foreach { | with callable this ;
|
foreach { | with callable this ;
|
||||||
def itm
|
!!-
|
||||||
while { this:next dup =itm null eq not } {
|
while { !!- this:next dup null eq not } {
|
||||||
itm callable call
|
!!- callable callp
|
||||||
}
|
}
|
||||||
|
pop
|
||||||
}
|
}
|
||||||
collect { array | with this ;
|
collect { array | with this ;
|
||||||
[ { any | } this:foreach ]
|
[ { | } this:foreach ]
|
||||||
}
|
}
|
||||||
map { MapIter | with map-function this ;
|
map { MapIter | with map-function this ;
|
||||||
map-function this MapIter:new
|
map-function this MapIter:new
|
||||||
|
@ -41,7 +42,7 @@ construct _Iter {
|
||||||
join { str | with separator this ;
|
join { str | with separator this ;
|
||||||
{ str | with accum item ;
|
{ str | with accum item ;
|
||||||
accum _str separator item _str concat concat
|
accum _str separator item _str concat concat
|
||||||
} this:reduce:calculate
|
} this:reduce:calculate "" or
|
||||||
}
|
}
|
||||||
filter { FilterIter | with filter this ;
|
filter { FilterIter | with filter this ;
|
||||||
filter this FilterIter:new
|
filter this FilterIter:new
|
134
spl/linkedlist.spl
Normal file
134
spl/linkedlist.spl
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
|
||||||
|
construct LinkedList {
|
||||||
|
next
|
||||||
|
value
|
||||||
|
;
|
||||||
|
construct { this | }
|
||||||
|
len { mega | with this ;
|
||||||
|
def i this:value null eq not =i
|
||||||
|
while { this:next null eq not } {
|
||||||
|
this:next =this
|
||||||
|
i ++ =i
|
||||||
|
}
|
||||||
|
i
|
||||||
|
}
|
||||||
|
push { | with item this ;
|
||||||
|
item:unwrap;
|
||||||
|
this:value null eq if {
|
||||||
|
item this:=value;
|
||||||
|
2 stop
|
||||||
|
}
|
||||||
|
LinkedList:new
|
||||||
|
dup :=value;<item>
|
||||||
|
this:last-entry:=next;
|
||||||
|
}
|
||||||
|
last-entry { list | with this ;
|
||||||
|
while { this:next null eq not } {
|
||||||
|
this:next =this
|
||||||
|
}
|
||||||
|
this
|
||||||
|
}
|
||||||
|
peek { value | with this ;
|
||||||
|
this:last-entry:value
|
||||||
|
}
|
||||||
|
pop { item | with this ;
|
||||||
|
def item this =item
|
||||||
|
null
|
||||||
|
while { item:next null eq not } {
|
||||||
|
pop item
|
||||||
|
item:next =item
|
||||||
|
}
|
||||||
|
"if theres no next item in this";
|
||||||
|
dup null eq if {
|
||||||
|
pop
|
||||||
|
this:value (null this:=value;) 2 stop
|
||||||
|
}
|
||||||
|
:=next;<null>
|
||||||
|
item:value
|
||||||
|
}
|
||||||
|
push-front { | with item this ;
|
||||||
|
item:unwrap;
|
||||||
|
this:value null eq if {
|
||||||
|
item this:=value;
|
||||||
|
2 stop
|
||||||
|
}
|
||||||
|
"append new next identical to this, then make this contain new value";
|
||||||
|
def new LinkedList:new =new
|
||||||
|
this:next new:=next;
|
||||||
|
this:value new:=value;
|
||||||
|
item this:=value;
|
||||||
|
new this:=next;
|
||||||
|
}
|
||||||
|
insert { | with item index this ;
|
||||||
|
item:unwrap;
|
||||||
|
index 0 eq if {
|
||||||
|
item this:push-front 2 stop
|
||||||
|
}
|
||||||
|
def list this =list
|
||||||
|
while { index 1 gt } {
|
||||||
|
list:next =list
|
||||||
|
index -- =index
|
||||||
|
}
|
||||||
|
item list:next:push-front
|
||||||
|
}
|
||||||
|
pop-front { item | with this ;
|
||||||
|
this:value
|
||||||
|
this:next null eq dup if {
|
||||||
|
null this:=value
|
||||||
|
}
|
||||||
|
not if {
|
||||||
|
this:next:value this:=value
|
||||||
|
this:next:next this:=next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remove { item | with index this ;
|
||||||
|
index 0 eq if {
|
||||||
|
this:pop-front 2 stop
|
||||||
|
}
|
||||||
|
def list this =list
|
||||||
|
while { index 1 gt } {
|
||||||
|
list:next =list
|
||||||
|
index -- =index
|
||||||
|
}
|
||||||
|
list:next:pop-front
|
||||||
|
}
|
||||||
|
get { item | with index this ;
|
||||||
|
while { index 0 gt } {
|
||||||
|
this:next =this
|
||||||
|
index -- =index
|
||||||
|
}
|
||||||
|
this:value
|
||||||
|
}
|
||||||
|
set { item | with value index this ;
|
||||||
|
while { index 0 gt } {
|
||||||
|
this:next =this
|
||||||
|
index -- =index
|
||||||
|
}
|
||||||
|
this:value value this:=value
|
||||||
|
}
|
||||||
|
foreach { | with callable this ;
|
||||||
|
while { this null eq not } {
|
||||||
|
this:value dup null eq not if { callable:call; }
|
||||||
|
this:next =this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iter { LinkedListIter | with this ;
|
||||||
|
this LinkedListIter:new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
construct LinkedListIter {
|
||||||
|
cur-list
|
||||||
|
;
|
||||||
|
construct { this | with list this ;
|
||||||
|
list this:=cur-list
|
||||||
|
this
|
||||||
|
}
|
||||||
|
next { item | with this ;
|
||||||
|
this:cur-list dup null eq if { 2 stop }
|
||||||
|
:value
|
||||||
|
this:cur-list:next this:=cur-list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
include _Iter in LinkedListIter
|
|
@ -1,4 +1,4 @@
|
||||||
"messaging bus, aka event bus"
|
"messaging bus, aka event bus";
|
||||||
|
|
||||||
construct messaging namespace {
|
construct messaging namespace {
|
||||||
Message
|
Message
|
|
@ -4,6 +4,6 @@
|
||||||
construct net namespace {
|
construct net namespace {
|
||||||
;
|
;
|
||||||
register { | with name this ;
|
register { | with name this ;
|
||||||
name "net" this register-field
|
name "net" register-field
|
||||||
}
|
}
|
||||||
}
|
}
|
49
spl/pure.spl
Normal file
49
spl/pure.spl
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
"stream.spl" import
|
||||||
|
|
||||||
|
"uses less native functions where possible";
|
||||||
|
|
||||||
|
func pop { | with _ ; }
|
||||||
|
|
||||||
|
func dup { x x | with x ;
|
||||||
|
x x
|
||||||
|
}
|
||||||
|
|
||||||
|
func swap { b a | with a b ;
|
||||||
|
b a
|
||||||
|
}
|
||||||
|
|
||||||
|
func not { !x | with x ;
|
||||||
|
1
|
||||||
|
x if { pop 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
func and { a&&b | with a b ;
|
||||||
|
0
|
||||||
|
a if { b if { pop 1 } }
|
||||||
|
}
|
||||||
|
|
||||||
|
func or { a||b | with a b ;
|
||||||
|
0
|
||||||
|
a if { pop 1 }
|
||||||
|
b if { pop 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
func alit-end { array | with type ;
|
||||||
|
def array 0 anew =array
|
||||||
|
while { dup type eq not } {
|
||||||
|
awrap array aadd =array
|
||||||
|
}
|
||||||
|
pop array
|
||||||
|
}
|
||||||
|
|
||||||
|
func read-file { str | with path ;
|
||||||
|
def stream path 0 StreamTypes:file:create =stream
|
||||||
|
1024 stream:read-to-end:to-str
|
||||||
|
}
|
||||||
|
|
||||||
|
func acopy { array | with from to idxsrc idxdst len ;
|
||||||
|
len:foreach<{ | with i ;
|
||||||
|
i idxsrc + from:get i idxdst + to:set;
|
||||||
|
}>
|
||||||
|
to
|
||||||
|
}
|
70
spl/repl.spl
Normal file
70
spl/repl.spl
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
|
||||||
|
"fast.spl" import
|
||||||
|
|
||||||
|
func main { mega | with args ;
|
||||||
|
"Welcome to the SPL REPL!" println
|
||||||
|
"Enter any code after the cursor to execute it." println
|
||||||
|
"fast.spl (for shell-like functions) is included.\n" println
|
||||||
|
|
||||||
|
"REPL" =program-name
|
||||||
|
while { 1 } {
|
||||||
|
catch {
|
||||||
|
def line "" =line
|
||||||
|
while { line repl-is-complete not } {
|
||||||
|
def s
|
||||||
|
line " > " print readln =s
|
||||||
|
s "\n" concat concat =line
|
||||||
|
}
|
||||||
|
line _barray =line
|
||||||
|
line:sub<0 line:len 1 -> _str =line
|
||||||
|
"!!-end" line:contains if {
|
||||||
|
2 stop
|
||||||
|
}
|
||||||
|
"\n" line:contains if {
|
||||||
|
"" println
|
||||||
|
line println
|
||||||
|
"." println
|
||||||
|
}
|
||||||
|
line dyn-read exec2 "\n" print
|
||||||
|
}
|
||||||
|
{ with err ;
|
||||||
|
err:message dup null eq if {
|
||||||
|
pop
|
||||||
|
"Uncaught error."
|
||||||
|
} err:trace
|
||||||
|
|
||||||
|
with msg trace ;
|
||||||
|
program-name dup if {
|
||||||
|
program-name print " panicked at:" println
|
||||||
|
} not if {
|
||||||
|
"Program panicked at:" println
|
||||||
|
}
|
||||||
|
&println trace:foreach
|
||||||
|
"\nPanic message:" println
|
||||||
|
" " print msg println
|
||||||
|
"\nRecovering." println
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func repl-is-complete { bool | with line ;
|
||||||
|
"!!-end" line:contains
|
||||||
|
0 line _array :foreach<{ | with char ;
|
||||||
|
char "{" :_char eq
|
||||||
|
char "(" :_char eq or
|
||||||
|
char "<" :_char eq or
|
||||||
|
char "[" :_char eq or
|
||||||
|
if {
|
||||||
|
++
|
||||||
|
}
|
||||||
|
char "}" :_char eq
|
||||||
|
char ")" :_char eq or
|
||||||
|
char ">" :_char eq or
|
||||||
|
char "]" :_char eq or
|
||||||
|
if {
|
||||||
|
--
|
||||||
|
}
|
||||||
|
}> not
|
||||||
|
or
|
||||||
|
"" line eq not and
|
||||||
|
}
|
63
spl/server.spl
Normal file
63
spl/server.spl
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
"net.spl" import
|
||||||
|
"stream.spl" import
|
||||||
|
|
||||||
|
"server" net:register
|
||||||
|
|
||||||
|
construct net:server namespace {
|
||||||
|
ServerStream
|
||||||
|
Type
|
||||||
|
_Types
|
||||||
|
types
|
||||||
|
;
|
||||||
|
Types { _Types | with this ;
|
||||||
|
this:_Types:new
|
||||||
|
}
|
||||||
|
register-type { | with id this ;
|
||||||
|
this:types id awrap aadd this:=types
|
||||||
|
id this:_Types dyn-def-field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
0 anew net:server:=types
|
||||||
|
|
||||||
|
construct net:server:Type {
|
||||||
|
id
|
||||||
|
;
|
||||||
|
construct { this | with id this ;
|
||||||
|
id this:=id
|
||||||
|
this
|
||||||
|
}
|
||||||
|
create { ServerStream | with this ;
|
||||||
|
this:id net:server:ServerStream:new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
construct net:server:_Types {
|
||||||
|
;
|
||||||
|
construct { this | with this ;
|
||||||
|
{ | with type ;
|
||||||
|
"type net:server:Type:new this:=<type>";
|
||||||
|
(type net:server:Type:new) (this ("=" type concat)) dyn-objcall
|
||||||
|
} net:server:types:foreach;
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"tcp" net:server:register-type
|
||||||
|
|
||||||
|
construct net:server:ServerStream {
|
||||||
|
id
|
||||||
|
;
|
||||||
|
construct { this | with type this ;
|
||||||
|
type new-server-stream this:=id
|
||||||
|
this
|
||||||
|
}
|
||||||
|
accept { Stream | with this ;
|
||||||
|
def stream Stream:mkinstance =stream
|
||||||
|
this:id accept-server-stream stream:=id
|
||||||
|
stream
|
||||||
|
}
|
||||||
|
close { | with this ;
|
||||||
|
this:id close-server-stream;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,11 +8,11 @@ def program-name
|
||||||
|
|
||||||
def print'pre-std &print =print'pre-std
|
def print'pre-std &print =print'pre-std
|
||||||
func print { |
|
func print { |
|
||||||
_str print'pre-std call
|
_str !!- print'pre-std inline-callable
|
||||||
}
|
}
|
||||||
|
|
||||||
func println { |
|
func println { |
|
||||||
print "\n" print
|
_str !!- "\n" concat print
|
||||||
}
|
}
|
||||||
|
|
||||||
construct error {
|
construct error {
|
||||||
|
@ -30,61 +30,60 @@ construct FrameInfo {
|
||||||
|
|
||||||
construct _str_ext {
|
construct _str_ext {
|
||||||
;
|
;
|
||||||
new { any | with this ;
|
new { any | :mkinstance:construct }
|
||||||
null clone this settype:construct
|
mkinstance { any | with this ;
|
||||||
|
null clone this settype
|
||||||
}
|
}
|
||||||
to-bytes { [int] | str-to-bytes }
|
to-bytes { [int] | str-to-bytes }
|
||||||
split { str | with splitter this ;
|
split { str | with splitter this ;
|
||||||
def bytes splitter:to-bytes =bytes
|
splitter:to-bytes this:to-bytes:split:map<{ | bytes-to-str }>
|
||||||
def iter this:to-bytes:iter =iter
|
|
||||||
def item 0 =item
|
|
||||||
[ while { item null eq not } {
|
|
||||||
def match 0 =match
|
|
||||||
[
|
|
||||||
while { match bytes:len eq not } {
|
|
||||||
iter:next =item
|
|
||||||
item null eq if {
|
|
||||||
3 stop
|
|
||||||
}
|
|
||||||
item dup (match bytes:get) eq dup if {
|
|
||||||
match ++ =match
|
|
||||||
} not if {
|
|
||||||
0 =match
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{ | pop pop } match:foreach
|
|
||||||
] _str
|
|
||||||
} ]
|
|
||||||
}
|
}
|
||||||
replace { str | with replacee replacement this ;
|
replace { str | with replacee replacement this ;
|
||||||
def bytes replacee:to-bytes =bytes
|
replacee:to-bytes replacement:to-bytes this:to-bytes:replace:to-str
|
||||||
def iter this:to-bytes:iter =iter
|
|
||||||
def item 0 =item
|
|
||||||
[ while { item null eq not } {
|
|
||||||
def match 0 =match
|
|
||||||
while { match bytes:len eq not } {
|
|
||||||
iter:next =item
|
|
||||||
item null eq if {
|
|
||||||
3 stop
|
|
||||||
}
|
}
|
||||||
item dup (match bytes:get) eq dup if {
|
find { idx | with search this ;
|
||||||
match ++ =match
|
search _barray this _barray :find
|
||||||
} not if {
|
|
||||||
0 =match
|
|
||||||
}
|
}
|
||||||
}
|
contains { bool | with search this ;
|
||||||
{ | pop pop } match:foreach
|
search this:find null eq not
|
||||||
match bytes:len eq if {
|
|
||||||
replacement _array :to-stack
|
|
||||||
}
|
|
||||||
} ] _str
|
|
||||||
}
|
}
|
||||||
starts-with { bool | with beginning this ;
|
starts-with { bool | with beginning this ;
|
||||||
beginning:to-bytes this:to-bytes:starts-with
|
beginning:to-bytes this:to-bytes:starts-with
|
||||||
}
|
}
|
||||||
|
ends-with { bool | with ending this ;
|
||||||
|
ending:to-bytes this:to-bytes:ends-with
|
||||||
|
}
|
||||||
|
escape { formatted | with this ;
|
||||||
|
"\"" this:replace<"\\" "\\\\">:replace<"\"" "\\\"">:replace<"\n" "\\n">:replace<"\r" "\\r"> concat "\"" concat
|
||||||
|
}
|
||||||
_char { int | with this ;
|
_char { int | with this ;
|
||||||
0 (this _array):get
|
0 (this _array):get
|
||||||
}
|
}
|
||||||
|
readf { [str] | with pat this ;
|
||||||
|
!!- pat this str-readf
|
||||||
|
}
|
||||||
|
readf1 { str | with pat this ;
|
||||||
|
!!- pat this:readf 0:get
|
||||||
|
}
|
||||||
|
uppercase { str | with this ;
|
||||||
|
this _array :cmap<{ | with chr ;
|
||||||
|
chr
|
||||||
|
chr "a" :_char lt not chr "z" :_char gt not and if {
|
||||||
|
pop (chr "a" :_char -) "A" :_char +
|
||||||
|
}
|
||||||
|
}> _str
|
||||||
|
}
|
||||||
|
lowercase { str | with this ;
|
||||||
|
this _array :cmap<{ | with chr ;
|
||||||
|
chr
|
||||||
|
chr "A" :_char lt not chr "Z" :_char gt not and if {
|
||||||
|
pop (chr "A" :_char -) "a" :_char +
|
||||||
|
}
|
||||||
|
}> _str
|
||||||
|
}
|
||||||
|
_mega_radix { mega | with radix this ;
|
||||||
|
this radix str-to-mega-radix
|
||||||
|
}
|
||||||
} include _str_ext in str
|
} include _str_ext in str
|
||||||
|
|
||||||
construct _mega-ext {
|
construct _mega-ext {
|
||||||
|
@ -96,12 +95,30 @@ construct _mega-ext {
|
||||||
mswap { .. | mswap }
|
mswap { .. | mswap }
|
||||||
foreach { | with callable this ;
|
foreach { | with callable this ;
|
||||||
def i 0 =i
|
def i 0 =i
|
||||||
while { i this lt } { i callable call i ++ =i }
|
while { !!- i this lt } { !!- i callable call i 1 + =i }
|
||||||
|
}
|
||||||
|
fforeach { | with callable this ;
|
||||||
|
0 while { !!- dup2 this lt } { !!- callable callp 1 + }
|
||||||
|
}
|
||||||
|
_str_radix { str | with radix this ;
|
||||||
|
this radix mega-to-str-radix
|
||||||
|
}
|
||||||
|
iter { MegaIter | with this ;
|
||||||
|
this MegaIter:new
|
||||||
}
|
}
|
||||||
} include _mega-ext in mega
|
} include _mega-ext in mega
|
||||||
|
|
||||||
construct _array-ext {
|
construct _array-ext {
|
||||||
;
|
;
|
||||||
|
another { array | with this ;
|
||||||
|
this gettype =this
|
||||||
|
this "array" eq if {
|
||||||
|
anew
|
||||||
|
}
|
||||||
|
this "bytearray" eq if {
|
||||||
|
banew
|
||||||
|
}
|
||||||
|
}
|
||||||
get { any | array-get }
|
get { any | array-get }
|
||||||
sget { any|null | with idx this ;
|
sget { any|null | with idx this ;
|
||||||
idx this:len lt idx -1 gt and dup if {
|
idx this:len lt idx -1 gt and dup if {
|
||||||
|
@ -126,9 +143,80 @@ 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 }
|
||||||
}
|
}
|
||||||
|
map { | with callable this ;
|
||||||
|
def i 0 =i
|
||||||
|
while { i this:len lt } { i this:get callable call i this:set; i ++ =i }
|
||||||
|
this
|
||||||
|
}
|
||||||
|
cmap { | with callable this ;
|
||||||
|
this clone =this
|
||||||
|
def i 0 =i
|
||||||
|
while { i this:len lt } { i this:get callable call i this:set; i ++ =i }
|
||||||
|
this
|
||||||
|
}
|
||||||
to-str { str | bytes-to-str }
|
to-str { str | bytes-to-str }
|
||||||
sub { [any] | with begin end this ;
|
sub { [any] | with begin end this ;
|
||||||
this (end begin - anew) begin 0 (end begin -) acopy
|
this (end begin - this:another) begin 0 (end begin -) acopy
|
||||||
|
}
|
||||||
|
split { arr | with splitter this ;
|
||||||
|
def i 0 =i
|
||||||
|
[ [ while { i this:len lt } {
|
||||||
|
def match 0 =match
|
||||||
|
while { match i + this:len lt match splitter:len lt and } {
|
||||||
|
i match + this:get (match splitter:get) eq dup if {
|
||||||
|
match ++ =match
|
||||||
|
} not if {
|
||||||
|
0 =match
|
||||||
|
3 stop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i this:get
|
||||||
|
match splitter:len eq if {
|
||||||
|
pop ] [
|
||||||
|
i match + 1 - =i
|
||||||
|
}
|
||||||
|
i ++ =i
|
||||||
|
} ] ]
|
||||||
|
}
|
||||||
|
replace { arr | with replacee replacement this ;
|
||||||
|
def i 0 =i
|
||||||
|
[ while { i this:len lt } {
|
||||||
|
def match 0 =match
|
||||||
|
while { match i + this:len lt match replacee:len lt and } {
|
||||||
|
i match + this:get (match replacee:get) eq dup if {
|
||||||
|
match ++ =match
|
||||||
|
} not if {
|
||||||
|
0 =match
|
||||||
|
3 stop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i this:get
|
||||||
|
match replacee:len eq if {
|
||||||
|
pop replacement:to-stack
|
||||||
|
i match + 1 - =i
|
||||||
|
}
|
||||||
|
i ++ =i
|
||||||
|
} ]
|
||||||
|
}
|
||||||
|
find { idx | with search this ;
|
||||||
|
def i 0 =i
|
||||||
|
while { i this:len lt } {
|
||||||
|
def match 0 =match
|
||||||
|
while { match i + this:len lt match search:len lt and } {
|
||||||
|
i match + this:get (match search:get) eq dup if {
|
||||||
|
match ++ =match
|
||||||
|
} not if {
|
||||||
|
0 =match
|
||||||
|
3 stop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match search:len eq if {
|
||||||
|
i
|
||||||
|
4 stop
|
||||||
|
}
|
||||||
|
i ++ =i
|
||||||
|
}
|
||||||
|
null
|
||||||
}
|
}
|
||||||
starts-with { bool | with beginning this ;
|
starts-with { bool | with beginning this ;
|
||||||
this:len beginning:len lt if {
|
this:len beginning:len lt if {
|
||||||
|
@ -137,11 +225,18 @@ construct _array-ext {
|
||||||
}
|
}
|
||||||
0 beginning:len this:sub beginning eq
|
0 beginning:len this:sub beginning eq
|
||||||
}
|
}
|
||||||
|
ends-with { bool | with ending this ;
|
||||||
|
this:len ending:len lt if {
|
||||||
|
0
|
||||||
|
2 stop
|
||||||
|
}
|
||||||
|
this:len ending:len - this:len this:sub ending eq
|
||||||
|
}
|
||||||
last { | with this ;
|
last { | with this ;
|
||||||
this:len -- this:get
|
this:len -- this:get
|
||||||
}
|
}
|
||||||
=last { | with this ;
|
=last { | with this ;
|
||||||
this:len -- this:set
|
this:len -- this:set;
|
||||||
}
|
}
|
||||||
0 { any | with this ;
|
0 { any | with this ;
|
||||||
0 this:get
|
0 this:get
|
||||||
|
@ -173,7 +268,9 @@ construct _array-ext {
|
||||||
=4 { | with this ;
|
=4 { | with this ;
|
||||||
4 this:set;
|
4 this:set;
|
||||||
}
|
}
|
||||||
} include _array-ext in array
|
}
|
||||||
|
include _array-ext in array
|
||||||
|
include _array-ext in bytearray
|
||||||
|
|
||||||
construct _func-ext {
|
construct _func-ext {
|
||||||
args
|
args
|
||||||
|
@ -221,6 +318,7 @@ construct List {
|
||||||
to-stack { .. | :array:to-stack }
|
to-stack { .. | :array:to-stack }
|
||||||
to-str { str | :array:to-str }
|
to-str { str | :array:to-str }
|
||||||
sub { [any] | :array:sub }
|
sub { [any] | :array:sub }
|
||||||
|
clear { | :=array<0 anew> }
|
||||||
}
|
}
|
||||||
construct _GrowingArray {
|
construct _GrowingArray {
|
||||||
;
|
;
|
||||||
|
@ -278,7 +376,7 @@ construct ArrayIter {
|
||||||
construct _IterableArray {
|
construct _IterableArray {
|
||||||
;
|
;
|
||||||
iter { ArrayIter | with this ;
|
iter { ArrayIter | with this ;
|
||||||
this gettype "array" eq dup if {
|
this gettype:ends-with<"array"> dup if {
|
||||||
pop
|
pop
|
||||||
this ArrayIter:new
|
this ArrayIter:new
|
||||||
2 stop
|
2 stop
|
||||||
|
@ -290,6 +388,7 @@ construct _IterableArray {
|
||||||
include _Iter in ArrayIter
|
include _Iter in ArrayIter
|
||||||
include _IterableArray in List
|
include _IterableArray in List
|
||||||
include _IterableArray in array
|
include _IterableArray in array
|
||||||
|
include _IterableArray in bytearray
|
||||||
|
|
||||||
construct MicroMap {
|
construct MicroMap {
|
||||||
pairs
|
pairs
|
||||||
|
@ -334,6 +433,20 @@ construct MicroMap {
|
||||||
foreach { | with callable this ;
|
foreach { | with callable this ;
|
||||||
callable this:pairs:foreach
|
callable this:pairs:foreach
|
||||||
}
|
}
|
||||||
|
clear { | with this ;
|
||||||
|
this:pairs:clear;
|
||||||
|
}
|
||||||
|
to-str { str | with this ;
|
||||||
|
"{ "
|
||||||
|
{ | with item ;
|
||||||
|
"'" concat
|
||||||
|
0 item:get dup null eq if { "key is null" panic } _str concat
|
||||||
|
"': '" concat
|
||||||
|
1 item:get dup null eq if { "value is null" panic } _str concat
|
||||||
|
"', " concat
|
||||||
|
} this:foreach
|
||||||
|
"}" concat
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
construct Range {
|
construct Range {
|
||||||
|
@ -368,26 +481,47 @@ construct Range {
|
||||||
}
|
}
|
||||||
|
|
||||||
construct RangeIter {
|
construct RangeIter {
|
||||||
range
|
upper step
|
||||||
idx
|
idx
|
||||||
;
|
;
|
||||||
construct { this | with range this ;
|
construct { this | with range this ;
|
||||||
range this:=range
|
range:lower this:=idx
|
||||||
0 this:=idx
|
range:upper this:=upper
|
||||||
|
range:step this:=step
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
next { mega | with this ;
|
next { mega | with this ;
|
||||||
this:idx dup ++ this:=idx this:range:item
|
!!-
|
||||||
|
this:idx dup this:step + this:=idx
|
||||||
|
dup this:upper lt not if {
|
||||||
|
pop null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
include _Iter in RangeIter
|
include _Iter in RangeIter
|
||||||
|
|
||||||
|
construct MegaIter {
|
||||||
|
i d
|
||||||
|
;
|
||||||
|
construct { this | with d this ;
|
||||||
|
d this:=d
|
||||||
|
0 this:=i
|
||||||
|
this
|
||||||
|
}
|
||||||
|
next { i | with this ;
|
||||||
|
this:i dup ++ this:=i
|
||||||
|
dup this:d lt not if { pop null }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
include _Iter in MegaIter
|
||||||
|
|
||||||
|
|
||||||
construct shadow { }
|
construct shadow { }
|
||||||
|
|
||||||
func aadd { array | with arr1 arr2 ;
|
func aadd { array | with arr1 arr2 ;
|
||||||
|
|
||||||
def newarr arr1:len arr2:len + anew =newarr
|
def newarr arr1:len arr2:len + arr1:another =newarr
|
||||||
|
|
||||||
arr1 newarr 0 0 arr1:len acopy;
|
arr1 newarr 0 0 arr1:len acopy;
|
||||||
arr2 newarr 0 arr1:len arr2:len acopy;
|
arr2 newarr 0 arr1:len arr2:len acopy;
|
||||||
|
@ -478,6 +612,10 @@ func assert-eq { any any | with a b ;
|
||||||
a b
|
a b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func awrap { array | with item ;
|
||||||
|
item 1 anew =item 0 item:set; item
|
||||||
|
}
|
||||||
|
|
||||||
func [ { shadow |
|
func [ { shadow |
|
||||||
"array" "shadow" settype
|
"array" "shadow" settype
|
||||||
}
|
}
|
||||||
|
@ -543,8 +681,21 @@ func update-types { |
|
||||||
update-types
|
update-types
|
||||||
|
|
||||||
"Adds a field to a namespace and initially sets it to the field's name.";
|
"Adds a field to a namespace and initially sets it to the field's name.";
|
||||||
func register-field { | with field-name namespace-name namespace ;
|
func register-field { | with field-name namespace-name ;
|
||||||
field-name namespace-name dyn-def-field;
|
def namespace-path
|
||||||
namespace namespace-name settype ("=" namespace-name concat) dyn-call
|
def iter
|
||||||
}
|
|
||||||
|
|
||||||
|
namespace-name:split<":"> =namespace-path;
|
||||||
|
|
||||||
|
namespace-path:iter =iter
|
||||||
|
field-name namespace-name dyn-def-field; "adds the desired field to the namespace construct";
|
||||||
|
(
|
||||||
|
iter:next dyn-call
|
||||||
|
iter:foreach<&dyn-objcall>
|
||||||
|
) namespace-name settype "updates and gets the namespace object";
|
||||||
|
|
||||||
|
("=" namespace-path:last concat) namespace-path:=last;
|
||||||
|
namespace-path:iter =iter
|
||||||
|
iter:next dyn-call
|
||||||
|
iter:foreach<&dyn-objcall>
|
||||||
|
}
|
|
@ -15,28 +15,26 @@ construct Stream {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
read-one { mega | with this ;
|
read-one { mega | with this ;
|
||||||
def buf 1 anew =buf
|
def buf 1 banew =buf
|
||||||
while { buf this:id read-stream not } { }
|
while { buf this:id read-stream pop not } { }
|
||||||
0 buf:get _mega
|
0 buf:get _mega
|
||||||
}
|
}
|
||||||
"the buffer is written to in-place.";
|
"the buffer is written to in-place.";
|
||||||
read { mega [int] | with buf this ;
|
read { mega [int] | with buf this ;
|
||||||
buf gettype "mega" eq if { buf anew =buf }
|
buf gettype "mega" eq if { buf banew =buf }
|
||||||
buf this:id read-stream buf
|
buf this:id read-stream
|
||||||
}
|
}
|
||||||
"the buffer is written to in-place.";
|
"the buffer is written to in-place.";
|
||||||
read-exact { [int] | with buf this ;
|
read-exact { [int] | with buf this ;
|
||||||
buf gettype "mega" eq if { buf anew =buf }
|
buf gettype "mega" eq if { buf banew =buf }
|
||||||
buf this:id read-all-stream buf
|
buf this:id read-all-stream buf
|
||||||
}
|
}
|
||||||
read-to-end { [int] | with buf this ;
|
read-to-end { [int] | with buf this ;
|
||||||
def full 0 anew =full
|
buf gettype "mega" eq if { buf banew =buf }
|
||||||
buf gettype "mega" eq if { buf anew =buf }
|
|
||||||
def read
|
def read
|
||||||
while { buf this:id read-stream pop _mega dup =read } {
|
0 banew while { buf this:id read-stream pop _mega dup =read } {
|
||||||
full (0 read buf:sub) aadd =full
|
(0 read buf:sub) aadd
|
||||||
}
|
}
|
||||||
full
|
|
||||||
}
|
}
|
||||||
write { mega | with buf this ;
|
write { mega | with buf this ;
|
||||||
buf this:id write-stream
|
buf this:id write-stream
|
||||||
|
@ -47,9 +45,29 @@ construct Stream {
|
||||||
flush { | with this ;
|
flush { | with this ;
|
||||||
this:id flush-stream
|
this:id flush-stream
|
||||||
}
|
}
|
||||||
|
shutdown-input { | with this ;
|
||||||
|
this:id shutdown-input-stream
|
||||||
|
}
|
||||||
close { | with this ;
|
close { | with this ;
|
||||||
this:id close-stream
|
this:id close-stream
|
||||||
}
|
}
|
||||||
|
peer { StreamPeer | with this ;
|
||||||
|
this:id get-stream-peer StreamPeer:new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
construct StreamPeer {
|
||||||
|
ip
|
||||||
|
port
|
||||||
|
;
|
||||||
|
construct { this | with ip port this ;
|
||||||
|
ip this:=ip
|
||||||
|
port this:=port
|
||||||
|
this
|
||||||
|
}
|
||||||
|
_str { str | with this ;
|
||||||
|
this:ip ":" concat this:port _str concat
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
construct StreamType {
|
construct StreamType {
|
||||||
|
@ -66,7 +84,7 @@ construct StreamType {
|
||||||
|
|
||||||
def stream-types 0 anew =stream-types
|
def stream-types 0 anew =stream-types
|
||||||
|
|
||||||
construct _StreamType {
|
construct _StreamTypes {
|
||||||
;
|
;
|
||||||
construct { this | with this ;
|
construct { this | with this ;
|
||||||
{ | with type ;
|
{ | with type ;
|
||||||
|
@ -79,7 +97,7 @@ construct _StreamType {
|
||||||
|
|
||||||
func register-stream-type { | with id ;
|
func register-stream-type { | with id ;
|
||||||
stream-types [ id ] aadd =stream-types
|
stream-types [ id ] aadd =stream-types
|
||||||
id _StreamType dyn-def-field
|
id _StreamTypes dyn-def-field
|
||||||
}
|
}
|
||||||
|
|
||||||
"tcp" register-stream-type
|
"tcp" register-stream-type
|
||||||
|
@ -87,6 +105,6 @@ func register-stream-type { | with id ;
|
||||||
"file" register-stream-type
|
"file" register-stream-type
|
||||||
"cmd" register-stream-type
|
"cmd" register-stream-type
|
||||||
|
|
||||||
func StreamTypes { _StreamType |
|
func StreamTypes { _StreamTypes |
|
||||||
_StreamType:new
|
_StreamTypes:new
|
||||||
}
|
}
|
9
spl/time.spl
Normal file
9
spl/time.spl
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
construct time namespace {
|
||||||
|
;
|
||||||
|
unixms { mega | with this ;
|
||||||
|
0 sleeptime
|
||||||
|
}
|
||||||
|
sleep { mega | with this ; _mega sleeptime }
|
||||||
|
}
|
||||||
|
|
33
src/lexer.rs
33
src/lexer.rs
|
@ -3,7 +3,7 @@ mod compat;
|
||||||
use std::{mem, sync::Arc};
|
use std::{mem, sync::Arc};
|
||||||
|
|
||||||
use crate::runtime::*;
|
use crate::runtime::*;
|
||||||
use compat::match_compat;
|
use compat::{match_compat, transform_compat};
|
||||||
use readformat::*;
|
use readformat::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
@ -57,7 +57,11 @@ fn read_block_dyn(
|
||||||
rem = Some(r as u32);
|
rem = Some(r as u32);
|
||||||
}
|
}
|
||||||
while i < str_words.len() {
|
while i < str_words.len() {
|
||||||
let word = str_words[i].to_owned();
|
let word = if !compat {
|
||||||
|
str_words[i].to_owned()
|
||||||
|
} else {
|
||||||
|
transform_compat(&str_words[i])
|
||||||
|
};
|
||||||
if compat && match_compat(word.to_owned(), str_words, &mut words, &mut i) {
|
if compat && match_compat(word.to_owned(), str_words, &mut words, &mut i) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -117,6 +121,19 @@ fn read_block_dyn(
|
||||||
run_as_base: false,
|
run_as_base: false,
|
||||||
}))))
|
}))))
|
||||||
}
|
}
|
||||||
|
// <| .. > lambda
|
||||||
|
"|" => {
|
||||||
|
let block = read_block_dyn(&str_words[i + 1..], false, ">".to_owned(), compat)?;
|
||||||
|
i += block.2;
|
||||||
|
words.push(Word::Const(Value::Func(AFunc::new(Func {
|
||||||
|
ret_count: 0,
|
||||||
|
to_call: FuncImpl::SPL(block.1),
|
||||||
|
origin: Arc::new(Frame::dummy()),
|
||||||
|
fname: None,
|
||||||
|
name: "dyn-arg".to_owned(),
|
||||||
|
run_as_base: false,
|
||||||
|
}))))
|
||||||
|
}
|
||||||
x if x.len() >= 2 && &x[0..2] == "!{" => {
|
x if x.len() >= 2 && &x[0..2] == "!{" => {
|
||||||
words.push(Word::Const(Value::Str(x[2..].to_owned())));
|
words.push(Word::Const(Value::Str(x[2..].to_owned())));
|
||||||
}
|
}
|
||||||
|
@ -236,12 +253,24 @@ fn read_block_dyn(
|
||||||
}
|
}
|
||||||
words.push(Word::Key(Keyword::With(vars)));
|
words.push(Word::Key(Keyword::With(vars)));
|
||||||
}
|
}
|
||||||
|
"inline-callable" => {
|
||||||
|
words.push(Word::Key(Keyword::InlineCallable));
|
||||||
|
}
|
||||||
|
"!!-" => {
|
||||||
|
words.push(Word::Key(Keyword::InlineStart));
|
||||||
|
}
|
||||||
|
"-!!" => {
|
||||||
|
words.push(Word::Key(Keyword::InlineEnd));
|
||||||
|
}
|
||||||
x if x == endword => {
|
x if x == endword => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
x if x.starts_with('\"') => {
|
x if x.starts_with('\"') => {
|
||||||
words.push(Word::Const(Value::Str(x[1..].to_owned())));
|
words.push(Word::Const(Value::Str(x[1..].to_owned())));
|
||||||
}
|
}
|
||||||
|
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 == '_' || c == '-')
|
x if x.chars().all(|c| c.is_numeric() || c == '_' || c == '-')
|
||||||
&& !x.starts_with('_')
|
&& !x.starts_with('_')
|
||||||
&& x.contains(char::is_numeric) =>
|
&& x.contains(char::is_numeric) =>
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
use crate::Word;
|
use crate::Word;
|
||||||
|
|
||||||
|
pub(crate) fn transform_compat(word: &str) -> String {
|
||||||
|
match word {
|
||||||
|
"try" => "catch".to_owned(),
|
||||||
|
_ => word.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn match_compat(
|
pub(crate) fn match_compat(
|
||||||
word: String,
|
word: String,
|
||||||
str_words: &[String],
|
str_words: &[String],
|
||||||
|
@ -15,6 +22,7 @@ pub(crate) fn match_compat(
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
"dec" => {
|
"dec" => {
|
||||||
|
eprintln!("spl: [compat] transforming dec call");
|
||||||
eprintln!("spl: [compat] transforming dec call");
|
eprintln!("spl: [compat] transforming dec call");
|
||||||
words.push(Word::Call("--".to_owned(), false, 0));
|
words.push(Word::Call("--".to_owned(), false, 0));
|
||||||
words.push(Word::Call("=".to_owned() + &str_words[*i - 1], false, 0));
|
words.push(Word::Call("=".to_owned() + &str_words[*i - 1], false, 0));
|
||||||
|
@ -22,8 +30,9 @@ pub(crate) fn match_compat(
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
"include" => {
|
"include" => {
|
||||||
// TODO: translate some stdlib components?
|
words.push(Word::Call("isbplmod'import-transform".to_owned(), false, 0));
|
||||||
words.push(Word::Call("import".to_owned(), false, 0));
|
words.push(Word::Call("import".to_owned(), false, 0));
|
||||||
|
*i += 1;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
|
|
43
src/lib.rs
43
src/lib.rs
|
@ -101,6 +101,24 @@ nofmt! {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! require_mut_on_stack {
|
||||||
|
($name:tt, $type:tt, $stack:expr, $fn:literal) => {
|
||||||
|
let binding = $stack.pop();
|
||||||
|
let Value::$type(ref mut $name) = binding.lock().native else {
|
||||||
|
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! require_mut {
|
||||||
|
($name:tt, $type:tt, $binding:expr, $stack:expr, $fn:literal) => {
|
||||||
|
let Value::$type(ref mut $name) = $binding.lock().native else {
|
||||||
|
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! require_int_on_stack {
|
macro_rules! require_int_on_stack {
|
||||||
($name:tt, $stack:expr, $fn:literal) => {
|
($name:tt, $stack:expr, $fn:literal) => {
|
||||||
|
@ -133,6 +151,31 @@ nofmt! {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
macro_rules! require_byte_array_on_stack {
|
||||||
|
($name:tt, $stack:expr, $fn:literal) => {
|
||||||
|
let binding = $stack.pop();
|
||||||
|
let $name = match binding.lock_ro().native {
|
||||||
|
Value::Array(ref x) => x
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|x| {
|
||||||
|
Ok(match &x.lock_ro().native {
|
||||||
|
Value::Int(x) => *x as u8,
|
||||||
|
Value::Long(x) => *x as u8,
|
||||||
|
Value::Mega(x) => *x as u8,
|
||||||
|
_ => $stack.err(ErrorKind::InvalidType(
|
||||||
|
x.lock_ro().kind.lock_ro().get_name(),
|
||||||
|
"byte".to_owned(),
|
||||||
|
))?,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
Value::ByteArray(ref x) => x.clone(),
|
||||||
|
_ => return $stack.err(ErrorKind::InvalidCall($fn.to_owned())),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[macro_export]
|
||||||
macro_rules! require_mut_array_on_stack {
|
macro_rules! require_mut_array_on_stack {
|
||||||
($name:tt, $stack:expr, $fn:literal) => {
|
($name:tt, $stack:expr, $fn:literal) => {
|
||||||
let binding = $stack.pop();
|
let binding = $stack.pop();
|
||||||
|
|
131
src/runtime.rs
131
src/runtime.rs
|
@ -46,6 +46,15 @@ pub fn runtime_mut<T>(f: impl FnOnce(RwLockWriteGuard<Runtime>) -> T) -> T {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fork_runtime() -> Arc<Mut<Runtime>> {
|
||||||
|
RUNTIME.with(|rt| {
|
||||||
|
rt.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.expect("no runtime (use .set)")
|
||||||
|
.clone()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_type(name: &str) -> Option<AMType> {
|
pub fn get_type(name: &str) -> Option<AMType> {
|
||||||
runtime(|rt| rt.get_type_by_name(name))
|
runtime(|rt| rt.get_type_by_name(name))
|
||||||
}
|
}
|
||||||
|
@ -63,6 +72,7 @@ pub struct Runtime {
|
||||||
types_by_id: HashMap<u32, AMType>,
|
types_by_id: HashMap<u32, AMType>,
|
||||||
next_stream_id: u128,
|
next_stream_id: u128,
|
||||||
streams: HashMap<u128, Arc<Mut<Stream>>>,
|
streams: HashMap<u128, Arc<Mut<Stream>>>,
|
||||||
|
server_streams: HashMap<u128, Arc<Mut<ServerStream>>>,
|
||||||
pub embedded_files: HashMap<&'static str, &'static str>,
|
pub embedded_files: HashMap<&'static str, &'static str>,
|
||||||
pub native_functions: HashMap<&'static str, (u32, FuncImpl)>,
|
pub native_functions: HashMap<&'static str, (u32, FuncImpl)>,
|
||||||
}
|
}
|
||||||
|
@ -92,6 +102,7 @@ impl Runtime {
|
||||||
types_by_id: HashMap::new(),
|
types_by_id: HashMap::new(),
|
||||||
next_stream_id: 0,
|
next_stream_id: 0,
|
||||||
streams: HashMap::new(),
|
streams: HashMap::new(),
|
||||||
|
server_streams: HashMap::new(),
|
||||||
embedded_files: HashMap::new(),
|
embedded_files: HashMap::new(),
|
||||||
native_functions: HashMap::new(),
|
native_functions: HashMap::new(),
|
||||||
};
|
};
|
||||||
|
@ -104,6 +115,7 @@ impl Runtime {
|
||||||
let _ = rt.make_type("func".to_owned(), Ok); // infallible
|
let _ = rt.make_type("func".to_owned(), Ok); // infallible
|
||||||
let _ = rt.make_type("array".to_owned(), Ok); // infallible
|
let _ = rt.make_type("array".to_owned(), Ok); // infallible
|
||||||
let _ = rt.make_type("str".to_owned(), Ok); // infallible
|
let _ = rt.make_type("str".to_owned(), Ok); // infallible
|
||||||
|
let _ = rt.make_type("bytearray".to_owned(), Ok); // infallible
|
||||||
stdlib::register(&mut rt);
|
stdlib::register(&mut rt);
|
||||||
rt
|
rt
|
||||||
}
|
}
|
||||||
|
@ -151,6 +163,23 @@ impl Runtime {
|
||||||
self.streams.remove(&id);
|
self.streams.remove(&id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn register_server_stream(
|
||||||
|
&mut self,
|
||||||
|
stream: ServerStream,
|
||||||
|
) -> (u128, Arc<Mut<ServerStream>>) {
|
||||||
|
let id = (self.next_stream_id, self.next_stream_id += 1).0;
|
||||||
|
self.server_streams.insert(id, Arc::new(Mut::new(stream)));
|
||||||
|
(id, self.server_streams.get(&id).unwrap().clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_server_stream(&self, id: u128) -> Option<Arc<Mut<ServerStream>>> {
|
||||||
|
self.server_streams.get(&id).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy_server_stream(&mut self, id: u128) {
|
||||||
|
self.server_streams.remove(&id);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_native_function(&self, name: &str) -> &(u32, FuncImpl) {
|
pub fn load_native_function(&self, name: &str) -> &(u32, FuncImpl) {
|
||||||
self.native_functions.get(name).unwrap_or_else(|| {
|
self.native_functions.get(name).unwrap_or_else(|| {
|
||||||
panic!(
|
panic!(
|
||||||
|
@ -445,6 +474,9 @@ impl Stack {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(&mut self, func: &AFunc) -> OError {
|
pub fn call(&mut self, func: &AFunc) -> OError {
|
||||||
|
if func.origin.is_dummy() {
|
||||||
|
return self.fast_call(func);
|
||||||
|
}
|
||||||
let f = if let Some(ref cname) = func.fname {
|
let f = if let Some(ref cname) = func.fname {
|
||||||
Frame::new_in(
|
Frame::new_in(
|
||||||
func.origin.clone(),
|
func.origin.clone(),
|
||||||
|
@ -461,6 +493,11 @@ impl Stack {
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fast_call(&mut self, func: &AFunc) -> OError {
|
||||||
|
let r = func.to_call.call(self);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_func(&self, name: String) -> Result<AFunc, Error> {
|
pub fn get_func(&self, name: String) -> Result<AFunc, Error> {
|
||||||
let mut frame = self.frames.last().unwrap();
|
let mut frame = self.frames.last().unwrap();
|
||||||
loop {
|
loop {
|
||||||
|
@ -482,14 +519,13 @@ impl Stack {
|
||||||
frame = self.frames.first().unwrap().clone();
|
frame = self.frames.first().unwrap().clone();
|
||||||
}
|
}
|
||||||
let tmpname = name.clone();
|
let tmpname = name.clone();
|
||||||
let tmpframe = frame.clone();
|
|
||||||
frame.functions.lock().insert(
|
frame.functions.lock().insert(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
Arc::new(Func {
|
Arc::new(Func {
|
||||||
ret_count: 1,
|
ret_count: 1,
|
||||||
origin: frame.clone(),
|
origin: Arc::new(Frame::dummy()),
|
||||||
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
|
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
|
||||||
stack.push(tmpframe.get_var(tmpname.clone(), stack)?);
|
stack.push(stack.get_frame().get_var(tmpname.clone(), stack)?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}))),
|
}))),
|
||||||
run_as_base: false,
|
run_as_base: false,
|
||||||
|
@ -498,15 +534,14 @@ impl Stack {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let tmpname = name.clone();
|
let tmpname = name.clone();
|
||||||
let tmpframe = frame.clone();
|
|
||||||
frame.functions.lock().insert(
|
frame.functions.lock().insert(
|
||||||
"=".to_owned() + &name,
|
"=".to_owned() + &name,
|
||||||
Arc::new(Func {
|
Arc::new(Func {
|
||||||
ret_count: 0,
|
ret_count: 0,
|
||||||
origin: frame.clone(),
|
origin: Arc::new(Frame::dummy()),
|
||||||
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
|
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
|
||||||
let v = stack.pop();
|
let v = stack.pop();
|
||||||
tmpframe.set_var(tmpname.clone(), v, stack)
|
stack.get_frame().set_var(tmpname.clone(), v, stack)
|
||||||
}))),
|
}))),
|
||||||
run_as_base: false,
|
run_as_base: false,
|
||||||
fname: Some("RUNTIME".to_owned()),
|
fname: Some("RUNTIME".to_owned()),
|
||||||
|
@ -640,6 +675,21 @@ pub enum Keyword {
|
||||||
/// equivalent to dyn-__dump
|
/// equivalent to dyn-__dump
|
||||||
/// example: func main { int | "Hello, world!" dyn-__dump pop 0 }
|
/// example: func main { int | "Hello, world!" dyn-__dump pop 0 }
|
||||||
Dump,
|
Dump,
|
||||||
|
/// inline-call
|
||||||
|
///
|
||||||
|
/// Inlines a callable into the current function
|
||||||
|
/// equivalent to call
|
||||||
|
InlineCallable,
|
||||||
|
/// !!-
|
||||||
|
///
|
||||||
|
/// Makes future calls inline, not adding a new stack frame
|
||||||
|
/// no equivalent
|
||||||
|
InlineStart,
|
||||||
|
/// -!!
|
||||||
|
///
|
||||||
|
/// Stops making calls inline
|
||||||
|
/// no equivalent
|
||||||
|
InlineEnd,
|
||||||
/// def <name>
|
/// def <name>
|
||||||
///
|
///
|
||||||
/// Defines a variable.
|
/// Defines a variable.
|
||||||
|
@ -723,6 +773,7 @@ pub enum Value {
|
||||||
Double(f64),
|
Double(f64),
|
||||||
Func(AFunc),
|
Func(AFunc),
|
||||||
Array(Vec<AMObject>),
|
Array(Vec<AMObject>),
|
||||||
|
ByteArray(Vec<u8>),
|
||||||
Str(String),
|
Str(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,6 +801,10 @@ impl PartialOrd for Value {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Value::Mega(a), Value::Mega(b)) => a.partial_cmp(b),
|
(Value::Mega(a), Value::Mega(b)) => a.partial_cmp(b),
|
||||||
|
(Value::Long(a), Value::Long(b)) => a.partial_cmp(b),
|
||||||
|
(Value::Int(a), Value::Int(b)) => a.partial_cmp(b),
|
||||||
|
(Value::Double(a), Value::Double(b)) => a.partial_cmp(b),
|
||||||
|
(Value::Float(a), Value::Float(b)) => a.partial_cmp(b),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -784,7 +839,7 @@ impl Words {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum FuncImpl {
|
pub enum FuncImpl {
|
||||||
Native(fn(&mut Stack) -> OError),
|
Native(fn(&mut Stack) -> OError),
|
||||||
NativeDyn(Arc<Box<dyn Fn(&mut Stack) -> OError>>),
|
NativeDyn(Arc<Box<dyn (Fn(&mut Stack) -> OError) + Send + Sync>>),
|
||||||
SPL(Words),
|
SPL(Words),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1014,6 +1069,7 @@ impl Object {
|
||||||
Value::Func(_) => true,
|
Value::Func(_) => true,
|
||||||
Value::Array(_) => true,
|
Value::Array(_) => true,
|
||||||
Value::Str(x) => !x.is_empty(),
|
Value::Str(x) => !x.is_empty(),
|
||||||
|
Value::ByteArray(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1073,6 +1129,7 @@ impl From<Value> for Object {
|
||||||
Value::Func(_) => x.get_type_by_id(6),
|
Value::Func(_) => x.get_type_by_id(6),
|
||||||
Value::Array(_) => x.get_type_by_id(7),
|
Value::Array(_) => x.get_type_by_id(7),
|
||||||
Value::Str(_) => x.get_type_by_id(8),
|
Value::Str(_) => x.get_type_by_id(8),
|
||||||
|
Value::ByteArray(_) => x.get_type_by_id(9),
|
||||||
}
|
}
|
||||||
.expect("runtime uninitialized: default types not set.")
|
.expect("runtime uninitialized: default types not set.")
|
||||||
}),
|
}),
|
||||||
|
@ -1135,20 +1192,35 @@ impl Words {
|
||||||
/// Executes the words. This does *not* create a new frame on the stack. Use [Stack::call] to
|
/// Executes the words. This does *not* create a new frame on the stack. Use [Stack::call] to
|
||||||
/// call and create a new frame.
|
/// call and create a new frame.
|
||||||
pub fn exec(&self, stack: &mut Stack) -> OError {
|
pub fn exec(&self, stack: &mut Stack) -> OError {
|
||||||
for word in self.words.clone() {
|
let mut inline_calls = false;
|
||||||
|
for word in self.words.iter() {
|
||||||
match word {
|
match word {
|
||||||
Word::Key(x) => match x {
|
Word::Key(x) => match x {
|
||||||
Keyword::Dump => println!("{stack}"),
|
Keyword::Dump => println!("{stack}"),
|
||||||
Keyword::Def(x) => stack.define_var(x),
|
Keyword::InlineCallable => {
|
||||||
|
let Value::Func(f) = stack.pop().lock_ro().native.clone() else {
|
||||||
|
return Err(
|
||||||
|
stack.error(ErrorKind::InvalidCall("inline-callable".to_owned()))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
stack.fast_call(&f)?;
|
||||||
|
}
|
||||||
|
Keyword::InlineStart => {
|
||||||
|
inline_calls = true;
|
||||||
|
}
|
||||||
|
Keyword::InlineEnd => {
|
||||||
|
inline_calls = false;
|
||||||
|
}
|
||||||
|
Keyword::Def(x) => stack.define_var(x.to_owned()),
|
||||||
Keyword::Func(name, rem, words) => stack.define_func(
|
Keyword::Func(name, rem, words) => stack.define_func(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
Arc::new(Func {
|
Arc::new(Func {
|
||||||
ret_count: rem,
|
ret_count: *rem,
|
||||||
to_call: FuncImpl::SPL(words),
|
to_call: FuncImpl::SPL(words.to_owned()),
|
||||||
origin: stack.get_frame(),
|
origin: stack.get_frame(),
|
||||||
run_as_base: false,
|
run_as_base: false,
|
||||||
fname: None,
|
fname: None,
|
||||||
name,
|
name: name.to_owned(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
Keyword::Construct(name, fields, methods, is_namespace) => {
|
Keyword::Construct(name, fields, methods, is_namespace) => {
|
||||||
|
@ -1159,14 +1231,14 @@ impl Words {
|
||||||
let t = runtime_mut(|mut rt| {
|
let t = runtime_mut(|mut rt| {
|
||||||
rt.make_type(name.clone(), |mut t| {
|
rt.make_type(name.clone(), |mut t| {
|
||||||
for field in fields {
|
for field in fields {
|
||||||
t.add_property(field, origin.clone())?;
|
t.add_property(field.to_owned(), origin.clone())?;
|
||||||
}
|
}
|
||||||
t.functions.extend(methods.into_iter().map(|(k, v)| {
|
t.functions.extend(methods.into_iter().map(|(k, v)| {
|
||||||
(
|
(
|
||||||
k.clone(),
|
k.clone(),
|
||||||
Arc::new(Func {
|
Arc::new(Func {
|
||||||
ret_count: v.0,
|
ret_count: v.0,
|
||||||
to_call: FuncImpl::SPL(v.1),
|
to_call: FuncImpl::SPL(v.1.to_owned()),
|
||||||
origin: origin.clone(),
|
origin: origin.clone(),
|
||||||
run_as_base: false,
|
run_as_base: false,
|
||||||
fname: None,
|
fname: None,
|
||||||
|
@ -1178,7 +1250,7 @@ impl Words {
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let to_set: Object = if is_namespace {
|
let to_set: Object = if *is_namespace {
|
||||||
let mut obj: Object = Value::Null.into();
|
let mut obj: Object = Value::Null.into();
|
||||||
obj.kind = t.clone();
|
obj.kind = t.clone();
|
||||||
t.lock_ro().write_into(&mut obj);
|
t.lock_ro().write_into(&mut obj);
|
||||||
|
@ -1207,13 +1279,14 @@ impl Words {
|
||||||
let rstack = &stack;
|
let rstack = &stack;
|
||||||
runtime(move |rt| {
|
runtime(move |rt| {
|
||||||
rt.get_type_by_name(&tb)
|
rt.get_type_by_name(&tb)
|
||||||
.ok_or_else(|| rstack.error(ErrorKind::TypeNotFound(tb)))?
|
.ok_or_else(|| {
|
||||||
|
rstack.error(ErrorKind::TypeNotFound(tb.to_owned()))
|
||||||
|
})?
|
||||||
.lock()
|
.lock()
|
||||||
.parents
|
.parents
|
||||||
.push(
|
.push(rt.get_type_by_name(&ta).ok_or_else(|| {
|
||||||
rt.get_type_by_name(&ta)
|
rstack.error(ErrorKind::TypeNotFound(ta.to_owned()))
|
||||||
.ok_or_else(|| rstack.error(ErrorKind::TypeNotFound(ta)))?,
|
})?);
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
@ -1262,7 +1335,7 @@ impl Words {
|
||||||
for var in vars.into_iter().rev() {
|
for var in vars.into_iter().rev() {
|
||||||
stack.define_var(var.clone());
|
stack.define_var(var.clone());
|
||||||
let obj = stack.pop();
|
let obj = stack.pop();
|
||||||
stack.set_var(var, obj)?;
|
stack.set_var(var.to_owned(), obj)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Keyword::ObjPush => {
|
Keyword::ObjPush => {
|
||||||
|
@ -1286,7 +1359,7 @@ impl Words {
|
||||||
origin: stack.get_frame(),
|
origin: stack.get_frame(),
|
||||||
run_as_base: false,
|
run_as_base: false,
|
||||||
fname: None,
|
fname: None,
|
||||||
name,
|
name: name.to_owned(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
@ -1298,6 +1371,7 @@ impl Words {
|
||||||
stack.push(x.clone().ensure_init(stack).spl())
|
stack.push(x.clone().ensure_init(stack).spl())
|
||||||
}
|
}
|
||||||
Word::Call(x, rem, ra) => {
|
Word::Call(x, rem, ra) => {
|
||||||
|
let ra = *ra;
|
||||||
if option_env!("SPLDEBUG").is_some() {
|
if option_env!("SPLDEBUG").is_some() {
|
||||||
println!("CALL({}) {x}", stack.len());
|
println!("CALL({}) {x}", stack.len());
|
||||||
}
|
}
|
||||||
|
@ -1323,9 +1397,13 @@ impl Words {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
stack.push(f.spl());
|
stack.push(f.spl());
|
||||||
|
} else {
|
||||||
|
if inline_calls {
|
||||||
|
stack.fast_call(&f)?;
|
||||||
} else {
|
} else {
|
||||||
stack.call(&f)?;
|
stack.call(&f)?;
|
||||||
if rem {
|
}
|
||||||
|
if *rem {
|
||||||
for _ in 0..f.ret_count {
|
for _ in 0..f.ret_count {
|
||||||
stack.pop();
|
stack.pop();
|
||||||
}
|
}
|
||||||
|
@ -1333,6 +1411,7 @@ impl Words {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Word::ObjCall(x, rem, ra) => {
|
Word::ObjCall(x, rem, ra) => {
|
||||||
|
let ra = *ra;
|
||||||
let o = stack.peek();
|
let o = stack.peek();
|
||||||
let o = o.lock_ro();
|
let o = o.lock_ro();
|
||||||
let f0 = o.kind.lock_ro();
|
let f0 = o.kind.lock_ro();
|
||||||
|
@ -1369,9 +1448,13 @@ impl Words {
|
||||||
}
|
}
|
||||||
stack.pop();
|
stack.pop();
|
||||||
stack.push(f.spl())
|
stack.push(f.spl())
|
||||||
|
} else {
|
||||||
|
if inline_calls {
|
||||||
|
stack.fast_call(&f)?;
|
||||||
} else {
|
} else {
|
||||||
stack.call(&f)?;
|
stack.call(&f)?;
|
||||||
if rem {
|
}
|
||||||
|
if *rem {
|
||||||
for _ in 0..f.ret_count {
|
for _ in 0..f.ret_count {
|
||||||
stack.pop();
|
stack.pop();
|
||||||
}
|
}
|
||||||
|
|
44
src/sasm.rs
44
src/sasm.rs
|
@ -24,6 +24,9 @@ fn sasm_parse<'a>(line: &str, words: &mut Vec<Word>, lines: &mut impl Iterator<I
|
||||||
let line: Vec<_> = line.split(" ").collect();
|
let line: Vec<_> = line.split(" ").collect();
|
||||||
match line[0] {
|
match line[0] {
|
||||||
"dump" => words.push(Word::Key(Keyword::Dump)),
|
"dump" => words.push(Word::Key(Keyword::Dump)),
|
||||||
|
"inline-callable" => words.push(Word::Key(Keyword::InlineCallable)),
|
||||||
|
"inline-start" => words.push(Word::Key(Keyword::InlineStart)),
|
||||||
|
"inline-end" => words.push(Word::Key(Keyword::InlineEnd)),
|
||||||
"def" => words.push(Word::Key(Keyword::Def(line[1].to_owned()))),
|
"def" => words.push(Word::Key(Keyword::Def(line[1].to_owned()))),
|
||||||
"func" => words.push(Word::Key(Keyword::Func(
|
"func" => words.push(Word::Key(Keyword::Func(
|
||||||
line[1].to_owned(),
|
line[1].to_owned(),
|
||||||
|
@ -172,6 +175,25 @@ fn sasm_parse<'a>(line: &str, words: &mut Vec<Word>, lines: &mut impl Iterator<I
|
||||||
})))),
|
})))),
|
||||||
"null" => words.push(Word::Const(Value::Null)),
|
"null" => words.push(Word::Const(Value::Null)),
|
||||||
"array" => panic!("invalid sasm const: array - not all Values can be consts!"),
|
"array" => panic!("invalid sasm const: array - not all Values can be consts!"),
|
||||||
|
"bytearray" => {
|
||||||
|
let mut array = Vec::new();
|
||||||
|
let inp = line[2].chars().collect::<Vec<_>>();
|
||||||
|
fn v(c: char) -> u8 {
|
||||||
|
if c > '0' && c <= '9' {
|
||||||
|
c as u8 - '0' as u8
|
||||||
|
} else if c > 'a' && c <= 'f' {
|
||||||
|
c as u8 - 'a' as u8
|
||||||
|
} else {
|
||||||
|
panic!("invalid sasm const: const bytearray [nonbytearray]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in (0..inp.len() / 2).map(|x| x * 2) {
|
||||||
|
let a = inp[i];
|
||||||
|
let b = inp[i + 1];
|
||||||
|
array.push(v(a) * 0x10 + v(b));
|
||||||
|
}
|
||||||
|
words.push(Word::Const(Value::ByteArray(array)));
|
||||||
|
}
|
||||||
_ => panic!("invalid sasm const: {}", line[1]),
|
_ => panic!("invalid sasm const: {}", line[1]),
|
||||||
},
|
},
|
||||||
"call" => {
|
"call" => {
|
||||||
|
@ -223,6 +245,9 @@ fn sasm_write_func(words: Words) -> String {
|
||||||
Keyword::Dump => {
|
Keyword::Dump => {
|
||||||
output += "dump\n";
|
output += "dump\n";
|
||||||
}
|
}
|
||||||
|
Keyword::InlineCallable => output += "inline-callable\n",
|
||||||
|
Keyword::InlineStart => output += "inline-start\n",
|
||||||
|
Keyword::InlineEnd => output += "inline-end\n",
|
||||||
Keyword::Def(x) => {
|
Keyword::Def(x) => {
|
||||||
output += "def ";
|
output += "def ";
|
||||||
output += &x;
|
output += &x;
|
||||||
|
@ -350,6 +375,25 @@ fn sasm_write_func(words: Words) -> String {
|
||||||
text.replace("\0", "\0\x01").replace("\n", "\0\0")
|
text.replace("\0", "\0\x01").replace("\n", "\0\0")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Value::ByteArray(b) => {
|
||||||
|
fn c(v: u8) -> char {
|
||||||
|
if v > 16 {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
(if v < 10 {
|
||||||
|
'0' as u8 + v
|
||||||
|
} else {
|
||||||
|
'a' as u8 + v - 10
|
||||||
|
}) as char
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut out = String::with_capacity(b.len() * 2);
|
||||||
|
for b in b {
|
||||||
|
out.push(c(b / 0x10));
|
||||||
|
out.push(c(b % 0x10));
|
||||||
|
}
|
||||||
|
output += &format!("const bytearray {out}\n");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Word::Call(name, rem, ra) => {
|
Word::Call(name, rem, ra) => {
|
||||||
output += "call ";
|
output += "call ";
|
||||||
|
|
434
src/std_fns.rs
434
src/std_fns.rs
|
@ -1,14 +1,17 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::{HashMap, VecDeque},
|
||||||
env::{args, vars},
|
env::{self, args, vars},
|
||||||
fs,
|
fs,
|
||||||
io::{stdin, stdout, Write},
|
io::{stdin, stdout, Write},
|
||||||
mem,
|
|
||||||
ops::{Add, Div, Mul, Rem, Sub},
|
ops::{Add, Div, Mul, Rem, Sub},
|
||||||
process::{self, Stdio},
|
process::{self, Stdio},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
thread,
|
||||||
|
time::{Duration, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use readformat::readf;
|
||||||
|
|
||||||
use crate::{dyn_fns, mutex::Mut, runtime::*, sasm::sasm_write, *};
|
use crate::{dyn_fns, mutex::Mut, runtime::*, sasm::sasm_write, *};
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
@ -50,6 +53,13 @@ pub fn dup(stack: &mut Stack) -> OError {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dup2(stack: &mut Stack) -> OError {
|
||||||
|
let o = stack.peek();
|
||||||
|
stack.push(o.clone());
|
||||||
|
stack.push(o);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pop(stack: &mut Stack) -> OError {
|
pub fn pop(stack: &mut Stack) -> OError {
|
||||||
stack.pop();
|
stack.pop();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -85,12 +95,27 @@ pub fn settype(stack: &mut Stack) -> OError {
|
||||||
let o = stack.pop();
|
let o = stack.pop();
|
||||||
let kind = runtime(|rt| rt.get_type_by_name(&s))
|
let kind = runtime(|rt| rt.get_type_by_name(&s))
|
||||||
.ok_or_else(|| stack.error(ErrorKind::TypeNotFound(s)))?;
|
.ok_or_else(|| stack.error(ErrorKind::TypeNotFound(s)))?;
|
||||||
|
set_type_internal(&o, kind);
|
||||||
|
stack.push(o);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn settypeid(stack: &mut Stack) -> OError {
|
||||||
|
let Value::Int(i) = stack.pop().lock_ro().native.clone() else {
|
||||||
|
return stack.err(ErrorKind::InvalidCall("settype".to_owned()));
|
||||||
|
};
|
||||||
|
let o = stack.pop();
|
||||||
|
let kind = runtime(|rt| rt.get_type_by_id(i as u32))
|
||||||
|
.ok_or_else(|| stack.error(ErrorKind::TypeNotFound(format!(";{i}"))))?;
|
||||||
|
set_type_internal(&o, kind);
|
||||||
|
stack.push(o);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_type_internal(o: &Arc<Mut<Object>>, kind: Arc<Mut<Type>>) {
|
||||||
let mut obj = o.lock();
|
let mut obj = o.lock();
|
||||||
kind.lock_ro().write_into(&mut obj);
|
kind.lock_ro().write_into(&mut obj);
|
||||||
obj.kind = kind;
|
obj.kind = kind;
|
||||||
mem::drop(obj);
|
|
||||||
stack.push(o);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gettype(stack: &mut Stack) -> OError {
|
pub fn gettype(stack: &mut Stack) -> OError {
|
||||||
|
@ -99,6 +124,23 @@ pub fn gettype(stack: &mut Stack) -> OError {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gettypeid(stack: &mut Stack) -> OError {
|
||||||
|
let o = stack.pop();
|
||||||
|
stack.push(Value::Int(o.lock_ro().kind.lock_ro().get_id() as i32).spl());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn barray_new(stack: &mut Stack) -> OError {
|
||||||
|
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
|
||||||
|
return stack.err(ErrorKind::InvalidCall("banew".to_owned()));
|
||||||
|
};
|
||||||
|
if i < 0 {
|
||||||
|
return stack.err(ErrorKind::InvalidCall("banew".to_owned()));
|
||||||
|
}
|
||||||
|
stack.push(Value::ByteArray(vec![0u8; i as usize]).spl());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn array_new(stack: &mut Stack) -> OError {
|
pub fn array_new(stack: &mut Stack) -> OError {
|
||||||
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
|
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
|
||||||
return stack.err(ErrorKind::InvalidCall("anew".to_owned()));
|
return stack.err(ErrorKind::InvalidCall("anew".to_owned()));
|
||||||
|
@ -112,36 +154,46 @@ pub fn array_new(stack: &mut Stack) -> OError {
|
||||||
|
|
||||||
pub fn array_len(stack: &mut Stack) -> OError {
|
pub fn array_len(stack: &mut Stack) -> OError {
|
||||||
let binding = stack.pop();
|
let binding = stack.pop();
|
||||||
let Value::Array(ref a) = binding.lock_ro().native else {
|
let len = match binding.lock_ro().native {
|
||||||
return stack.err(ErrorKind::InvalidCall("array-len".to_owned()));
|
Value::Array(ref a) => a.len(),
|
||||||
|
Value::ByteArray(ref a) => a.len(),
|
||||||
|
_ => return stack.err(ErrorKind::InvalidCall("array-len".to_owned())),
|
||||||
};
|
};
|
||||||
stack.push(Value::Mega(a.len() as i128).spl());
|
stack.push(Value::Mega(len as i128).spl());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn array_get(stack: &mut Stack) -> OError {
|
pub fn array_get(stack: &mut Stack) -> OError {
|
||||||
let binding = stack.pop();
|
let binding = stack.pop();
|
||||||
let Value::Array(ref a) = binding.lock_ro().native else {
|
let Value::Mega(i) = stack.pop().lock_ro().native else {
|
||||||
return stack.err(ErrorKind::InvalidCall("array-get".to_owned()));
|
return stack.err(ErrorKind::InvalidCall("array-get".to_owned()));
|
||||||
};
|
};
|
||||||
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
|
let o = match binding.lock_ro().native {
|
||||||
return stack.err(ErrorKind::InvalidCall("array-get".to_owned()));
|
Value::Array(ref a) => a.get(i as usize).cloned(),
|
||||||
|
Value::ByteArray(ref a) => a.get(i as usize).map(|x| Value::Int(*x as i32).spl()),
|
||||||
|
_ => return stack.err(ErrorKind::InvalidCall("array-get".to_owned())),
|
||||||
};
|
};
|
||||||
stack.push(a.get(i as usize).ok_or_else(array!(stack, i))?.clone());
|
stack.push(o.ok_or_else(array!(stack, i))?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn array_set(stack: &mut Stack) -> OError {
|
pub fn array_set(stack: &mut Stack) -> OError {
|
||||||
let binding = stack.pop();
|
let binding = &stack.pop();
|
||||||
let Value::Array(ref mut a) = binding.lock().native else {
|
let binding = &mut binding.lock().native;
|
||||||
return stack.err(ErrorKind::InvalidCall("array-set".to_owned()));
|
require_on_stack!(i, Mega, stack, "array-set");
|
||||||
};
|
|
||||||
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
|
|
||||||
return stack.err(ErrorKind::InvalidCall("array-set".to_owned()));
|
|
||||||
};
|
|
||||||
let o = stack.pop();
|
let o = stack.pop();
|
||||||
|
if let Value::Array(ref mut a) = binding {
|
||||||
stack.push(a.get(i as usize).ok_or_else(array!(stack, i))?.clone());
|
stack.push(a.get(i as usize).ok_or_else(array!(stack, i))?.clone());
|
||||||
*a.get_mut(i as usize).ok_or_else(array!(stack, i))? = o;
|
*a.get_mut(i as usize).ok_or_else(array!(stack, i))? = o;
|
||||||
|
} else if let Value::ByteArray(ref mut a) = binding {
|
||||||
|
let Value::Int(o) = o.lock_ro().native else {
|
||||||
|
return stack.err(ErrorKind::InvalidCall("array-set".to_owned()));
|
||||||
|
};
|
||||||
|
stack.push(Value::Int(*a.get(i as usize).ok_or_else(array!(stack, i))? as i32).spl());
|
||||||
|
*a.get_mut(i as usize).ok_or_else(array!(stack, i))? = o as u8;
|
||||||
|
} else {
|
||||||
|
return stack.err(ErrorKind::InvalidCall("array-set".to_owned()));
|
||||||
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,16 +239,9 @@ pub fn and(stack: &mut Stack) -> OError {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn or(stack: &mut Stack) -> OError {
|
pub fn or(stack: &mut Stack) -> OError {
|
||||||
let a = stack.pop();
|
|
||||||
let b = stack.pop();
|
let b = stack.pop();
|
||||||
stack.push(
|
let a = stack.pop();
|
||||||
Value::Int(if a.lock_ro().is_truthy() || b.lock_ro().is_truthy() {
|
stack.push(if a.lock_ro().is_truthy() { a } else { b });
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
})
|
|
||||||
.spl(),
|
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,6 +361,7 @@ pub fn to_int(stack: &mut Stack) -> OError {
|
||||||
Value::Str(x) => x
|
Value::Str(x) => x
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "int".to_owned())))?,
|
.map_err(|_| stack.error(ErrorKind::Parse(x, "int".to_owned())))?,
|
||||||
|
Value::ByteArray(x) => x.len() as i32,
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -337,6 +383,7 @@ pub fn to_long(stack: &mut Stack) -> OError {
|
||||||
Value::Str(x) => x
|
Value::Str(x) => x
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "long".to_owned())))?,
|
.map_err(|_| stack.error(ErrorKind::Parse(x, "long".to_owned())))?,
|
||||||
|
Value::ByteArray(x) => x.len() as i64,
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -358,6 +405,7 @@ pub fn to_mega(stack: &mut Stack) -> OError {
|
||||||
Value::Str(x) => x
|
Value::Str(x) => x
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "mega".to_owned())))?,
|
.map_err(|_| stack.error(ErrorKind::Parse(x, "mega".to_owned())))?,
|
||||||
|
Value::ByteArray(x) => x.len() as i128,
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -379,6 +427,7 @@ pub fn to_float(stack: &mut Stack) -> OError {
|
||||||
Value::Str(x) => x
|
Value::Str(x) => x
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "float".to_owned())))?,
|
.map_err(|_| stack.error(ErrorKind::Parse(x, "float".to_owned())))?,
|
||||||
|
Value::ByteArray(_) => type_err!(stack, "bytearray", "float"),
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -400,6 +449,7 @@ pub fn to_double(stack: &mut Stack) -> OError {
|
||||||
Value::Str(x) => x
|
Value::Str(x) => x
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "double".to_owned())))?,
|
.map_err(|_| stack.error(ErrorKind::Parse(x, "double".to_owned())))?,
|
||||||
|
Value::ByteArray(_) => type_err!(stack, "bytearray", "double"),
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -422,6 +472,11 @@ pub fn to_array(stack: &mut Stack) -> OError {
|
||||||
.chars()
|
.chars()
|
||||||
.map(|x| Value::Int(x as u32 as i32).spl())
|
.map(|x| Value::Int(x as u32 as i32).spl())
|
||||||
.collect(),
|
.collect(),
|
||||||
|
Value::ByteArray(x) => x
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|x| Value::Int(x as i32).spl())
|
||||||
|
.collect(),
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -462,6 +517,46 @@ pub fn to_str(stack: &mut Stack) -> OError {
|
||||||
fixed
|
fixed
|
||||||
}
|
}
|
||||||
Value::Str(x) => x,
|
Value::Str(x) => x,
|
||||||
|
Value::ByteArray(x) => String::from_utf8(x).map_err(|_| {
|
||||||
|
stack.error(ErrorKind::InvalidType(
|
||||||
|
"!utf8".to_owned(),
|
||||||
|
"utf8".to_owned(),
|
||||||
|
))
|
||||||
|
})?,
|
||||||
|
})
|
||||||
|
.spl(),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bytearray(stack: &mut Stack) -> OError {
|
||||||
|
let o = stack.pop().lock_ro().native.clone();
|
||||||
|
stack.push(
|
||||||
|
Value::ByteArray(match o {
|
||||||
|
Value::Null => type_err!(stack, "null", "array"),
|
||||||
|
Value::Int(_) => type_err!(stack, "int", "array"),
|
||||||
|
Value::Long(_) => type_err!(stack, "long", "array"),
|
||||||
|
Value::Mega(_) => type_err!(stack, "mega", "array"),
|
||||||
|
Value::Float(_) => type_err!(stack, "float", "array"),
|
||||||
|
Value::Double(_) => type_err!(stack, "double", "array"),
|
||||||
|
Value::Func(_) => type_err!(stack, "func", "array"),
|
||||||
|
Value::Array(x) => x
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|x| {
|
||||||
|
Ok(match &x.lock_ro().native {
|
||||||
|
Value::Int(x) => *x as u8,
|
||||||
|
Value::Long(x) => *x as u8,
|
||||||
|
Value::Mega(x) => *x as u8,
|
||||||
|
_ => stack.err(ErrorKind::InvalidType(
|
||||||
|
x.lock_ro().kind.lock_ro().get_name(),
|
||||||
|
"byte".to_owned(),
|
||||||
|
))?,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
Value::Str(x) => x.into_bytes(),
|
||||||
|
Value::ByteArray(x) => x,
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -601,23 +696,11 @@ pub fn alit_end(stack: &mut Stack) -> OError {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: rewrite
|
||||||
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("import".to_owned()));
|
return stack.err(ErrorKind::InvalidCall("import".to_owned()));
|
||||||
};
|
};
|
||||||
let fallback = s
|
|
||||||
.as_str()
|
|
||||||
.rsplit_once(|x| x == '/' || x == '#')
|
|
||||||
.map(|(.., x)| x)
|
|
||||||
.unwrap_or(s.as_str());
|
|
||||||
let fallback = runtime(|x| {
|
|
||||||
for (&p, &data) in &x.embedded_files {
|
|
||||||
if fallback == p {
|
|
||||||
return Some(data.to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
});
|
|
||||||
if let Some(x) = s.strip_prefix('#') {
|
if let Some(x) = s.strip_prefix('#') {
|
||||||
s = find_in_splpath(x).unwrap_or(x.to_owned());
|
s = find_in_splpath(x).unwrap_or(x.to_owned());
|
||||||
} else if let Some(x) = s.strip_prefix('@') {
|
} else if let Some(x) = s.strip_prefix('@') {
|
||||||
|
@ -633,7 +716,29 @@ pub fn import(stack: &mut Stack) -> OError {
|
||||||
.to_owned()
|
.to_owned()
|
||||||
+ "/"
|
+ "/"
|
||||||
+ &s;
|
+ &s;
|
||||||
|
s = s.trim_start_matches("./").to_owned();
|
||||||
}
|
}
|
||||||
|
let mut fallback = s
|
||||||
|
.as_str()
|
||||||
|
.rsplit_once(|x| x == '#')
|
||||||
|
.map(|(.., x)| x.to_owned())
|
||||||
|
.unwrap_or(s.clone());
|
||||||
|
while fallback.contains("/../") {
|
||||||
|
let mut fb = readf("{}/../{}", &fallback).unwrap();
|
||||||
|
if let Some(x) = fb[0].rsplit_once('/') {
|
||||||
|
fallback = x.0.to_owned() + &fb[1];
|
||||||
|
} else {
|
||||||
|
fallback = fb.swap_remove(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let fallback = runtime(|x| {
|
||||||
|
for (&p, &data) in &x.embedded_files {
|
||||||
|
if fallback == p {
|
||||||
|
return Some(data.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
});
|
||||||
if stack.include_file(
|
if stack.include_file(
|
||||||
&(*fs::canonicalize(s.clone())
|
&(*fs::canonicalize(s.clone())
|
||||||
.unwrap_or_else(|_| s.clone().into())
|
.unwrap_or_else(|_| s.clone().into())
|
||||||
|
@ -746,29 +851,18 @@ pub fn command_wait(stack: &mut Stack) -> OError {
|
||||||
|
|
||||||
pub fn str_to_bytes(stack: &mut Stack) -> OError {
|
pub fn str_to_bytes(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(s, Str, stack, "str-to-bytes");
|
require_on_stack!(s, Str, stack, "str-to-bytes");
|
||||||
stack.push(
|
stack.push(Value::ByteArray(s.bytes().collect()).spl());
|
||||||
Value::Array(
|
|
||||||
s.bytes()
|
|
||||||
.into_iter()
|
|
||||||
.map(|x| Value::Int(x as i32).spl())
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
.spl(),
|
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bytes_to_str(stack: &mut Stack) -> OError {
|
pub fn bytes_to_str(stack: &mut Stack) -> OError {
|
||||||
require_array_on_stack!(a, stack, "str-to-bytes");
|
if stack.peek().lock_ro().kind.lock_ro().get_name() == "bytearray" {
|
||||||
let mut chars = Vec::new();
|
require_on_stack!(a, ByteArray, stack, "bytes-to-str");
|
||||||
for item in a.iter() {
|
stack.push(Value::Str(String::from_utf8_lossy(&a[..]).into_owned()).spl());
|
||||||
if let Value::Int(x) = item.lock_ro().native.clone().try_mega_to_int() {
|
return Ok(());
|
||||||
chars.push(x as u8);
|
|
||||||
} else {
|
|
||||||
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
|
|
||||||
}
|
}
|
||||||
}
|
require_byte_array_on_stack!(a, stack, "bytes-to-str");
|
||||||
stack.push(Value::Str(String::from_utf8_lossy(&chars[..]).into_owned()).spl());
|
stack.push(Value::Str(String::from_utf8_lossy(&a).into_owned()).spl());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -777,7 +871,8 @@ pub fn acopy(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(idx_dest, Mega, stack, "acopy");
|
require_on_stack!(idx_dest, Mega, stack, "acopy");
|
||||||
require_on_stack!(idx_src, Mega, stack, "acopy");
|
require_on_stack!(idx_src, Mega, stack, "acopy");
|
||||||
let dest_array = stack.pop();
|
let dest_array = stack.pop();
|
||||||
{
|
let kind = dest_array.lock_ro().kind.lock_ro().get_name();
|
||||||
|
if kind == "array" {
|
||||||
require_mut_array!(dest, dest_array, stack, "acopy");
|
require_mut_array!(dest, dest_array, stack, "acopy");
|
||||||
require_array_on_stack!(src, stack, "acopy");
|
require_array_on_stack!(src, stack, "acopy");
|
||||||
let offset = idx_dest - idx_src;
|
let offset = idx_dest - idx_src;
|
||||||
|
@ -792,6 +887,14 @@ pub fn acopy(stack: &mut Stack) -> OError {
|
||||||
*dest.get_mut((i + offset) as usize).unwrap() = src.get(i as usize).unwrap().clone();
|
*dest.get_mut((i + offset) as usize).unwrap() = src.get(i as usize).unwrap().clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if kind == "bytearray" {
|
||||||
|
require_mut!(dest, ByteArray, dest_array, stack, "acopy");
|
||||||
|
require_byte_array_on_stack!(src, stack, "acopy");
|
||||||
|
let len = len as usize;
|
||||||
|
let idx_src = idx_src as usize;
|
||||||
|
let idx_dest = idx_dest as usize;
|
||||||
|
(&mut dest[idx_dest..idx_dest + len]).clone_from_slice(&src[idx_src..idx_src + len]);
|
||||||
|
}
|
||||||
stack.push(dest_array);
|
stack.push(dest_array);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -840,17 +943,212 @@ pub fn write_file_sasm(stack: &mut Stack) -> OError {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fork(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(callable, Func, stack, "fork");
|
||||||
|
let mut new_stack = stack.clone();
|
||||||
|
let rt = fork_runtime();
|
||||||
|
thread::spawn(move || {
|
||||||
|
rt.set();
|
||||||
|
if let Some(err) = new_stack.call(&callable).err() {
|
||||||
|
println!("{err:?}");
|
||||||
|
};
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn time(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(sleep_ms, Mega, stack, "time");
|
||||||
|
if sleep_ms != 0 {
|
||||||
|
thread::sleep(Duration::from_millis(sleep_ms as u64));
|
||||||
|
}
|
||||||
|
stack.push(
|
||||||
|
(SystemTime::now()
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_millis() as i128)
|
||||||
|
.spl(),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn str_readf(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(string, Str, stack, "str-readf");
|
||||||
|
require_on_stack!(pat, Str, stack, "str-readf");
|
||||||
|
let Some(result) = readf(&pat, &string) else {
|
||||||
|
stack.push(Value::Null.spl());
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
stack.push(Value::Array(result.into_iter().map(<String as SPL>::spl).collect()).spl());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn str_to_mega_radix(stack: &mut Stack) -> OError {
|
||||||
|
require_int_on_stack!(radix, stack, "str-to-mega-radix");
|
||||||
|
require_on_stack!(str, Str, stack, "str-to-mega-radix");
|
||||||
|
let Ok(result) = i128::from_str_radix(&str, radix as u32) else {
|
||||||
|
stack.push(Value::Null.spl());
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
stack.push(Value::Mega(result).spl());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mega_to_str_radix(stack: &mut Stack) -> OError {
|
||||||
|
require_int_on_stack!(radix, stack, "mega-to-str-radix");
|
||||||
|
require_on_stack!(mega, Mega, stack, "mega-to-str-radix");
|
||||||
|
// capacity because O(n)
|
||||||
|
let mut result = Vec::with_capacity((mega as f64).powf(1.0 / radix as f64) as usize + 2);
|
||||||
|
let neg = mega < 0;
|
||||||
|
let mut mega = mega;
|
||||||
|
if neg {
|
||||||
|
mega = -mega;
|
||||||
|
result.push('-' as u32);
|
||||||
|
}
|
||||||
|
while mega != 0 {
|
||||||
|
let i = (mega % radix as i128) as u32;
|
||||||
|
result.push(if i < 10 { '0' as u32 } else { 'a' as u32 - 10 } + i);
|
||||||
|
mega = mega / radix as i128;
|
||||||
|
}
|
||||||
|
result.reverse();
|
||||||
|
stack.push(
|
||||||
|
Value::Str(String::from_iter(
|
||||||
|
result
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| char::from_u32(x).expect("invalid radix")),
|
||||||
|
))
|
||||||
|
.spl(),
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn properties(stack: &mut Stack) -> OError {
|
||||||
|
let o = stack.pop();
|
||||||
|
let o = o.lock_ro();
|
||||||
|
let additional: Vec<AMObject> = vec![
|
||||||
|
Value::Array(vec![
|
||||||
|
":".to_owned().spl(),
|
||||||
|
o.kind.lock_ro().get_name().spl(),
|
||||||
|
])
|
||||||
|
.spl(),
|
||||||
|
Value::Array(vec![";".to_owned().spl(), o.native.clone().spl()]).spl(),
|
||||||
|
];
|
||||||
|
stack.push(
|
||||||
|
Value::Array(
|
||||||
|
o.property_map
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| Value::Array(vec![k.clone().spl(), v.clone()]).spl())
|
||||||
|
.chain(additional.into_iter())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
.spl(),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_properties(stack: &mut Stack) -> OError {
|
||||||
|
require_array_on_stack!(props, stack, "from-properties");
|
||||||
|
let mut map = HashMap::with_capacity(props.len());
|
||||||
|
for prop in props {
|
||||||
|
require_array!(prop, prop, stack, "from-properties");
|
||||||
|
if prop.len() != 2 {
|
||||||
|
stack.err(ErrorKind::InvalidCall("from-properties".to_string()))?;
|
||||||
|
}
|
||||||
|
let Value::Str(ref s) = prop[0].lock_ro().native else {
|
||||||
|
return Err(stack.error(ErrorKind::InvalidCall("from-properties".to_string())));
|
||||||
|
};
|
||||||
|
map.insert(s.to_owned(), prop[1].clone());
|
||||||
|
}
|
||||||
|
let Value::Str(kind) = map
|
||||||
|
.get(":")
|
||||||
|
.ok_or(stack.error(ErrorKind::InvalidCall("from-properties".to_string())))?
|
||||||
|
.lock_ro()
|
||||||
|
.native
|
||||||
|
.clone()
|
||||||
|
else {
|
||||||
|
return Err(stack.error(ErrorKind::InvalidCall("from-properties".to_string())));
|
||||||
|
};
|
||||||
|
let kind = runtime(|rt| rt.get_type_by_name(&kind))
|
||||||
|
.ok_or(stack.error(ErrorKind::TypeNotFound(kind.to_owned())))?;
|
||||||
|
let native = map
|
||||||
|
.get(";")
|
||||||
|
.ok_or(stack.error(ErrorKind::InvalidCall("from-properties".to_owned())))?
|
||||||
|
.lock_ro()
|
||||||
|
.native
|
||||||
|
.clone();
|
||||||
|
map.remove(";");
|
||||||
|
map.remove(":");
|
||||||
|
stack.push(Arc::new(Mut::new(Object {
|
||||||
|
kind,
|
||||||
|
native,
|
||||||
|
property_map: map,
|
||||||
|
})));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_files(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(dir, Str, stack, "list-files");
|
||||||
|
stack.push(
|
||||||
|
match fs::read_dir(&dir)
|
||||||
|
.map_err(|_| stack.error(ErrorKind::IO(format!("Not a directory: {}", &dir))))
|
||||||
|
{
|
||||||
|
Ok(it) => Value::Array(
|
||||||
|
it.filter(|x| x.is_ok())
|
||||||
|
.map(|x| {
|
||||||
|
if let Ok(x) = x {
|
||||||
|
Value::Str(x.file_name().to_string_lossy().into_owned()).spl()
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
.spl(),
|
||||||
|
Err(_) => Value::Null.spl(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_file(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(file, Str, stack, "delete-file");
|
||||||
|
stack.push(Value::Int(if fs::remove_file(file).is_ok() { 1 } else { 0 }).spl());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_dir(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(dir, Str, stack, "delete-dir");
|
||||||
|
stack.push(
|
||||||
|
Value::Int(if fs::remove_dir_all(dir).is_ok() {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
})
|
||||||
|
.spl(),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chdir(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(dir, Str, stack, "chdir");
|
||||||
|
env::set_current_dir(dir).map_err(|e| stack.error(ErrorKind::IO(e.to_string())))?;
|
||||||
|
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); 53] = [
|
let fns: [(&str, Fn, u32); 69] = [
|
||||||
("pop", pop, 0),
|
("pop", pop, 0),
|
||||||
("dup", dup, 2),
|
("dup", dup, 2),
|
||||||
|
("dup2", dup2, 3),
|
||||||
("clone", clone, 1),
|
("clone", clone, 1),
|
||||||
("swap", swap, 2),
|
("swap", swap, 2),
|
||||||
("mswap", mswap, 2),
|
("mswap", mswap, 2),
|
||||||
("print", print, 0),
|
("print", print, 0),
|
||||||
("gettype", gettype, 1),
|
("gettype", gettype, 1),
|
||||||
|
("gettypeid", gettypeid, 1),
|
||||||
("settype", settype, 1),
|
("settype", settype, 1),
|
||||||
|
("settypeid", settypeid, 1),
|
||||||
|
("banew", barray_new, 1),
|
||||||
("anew", array_new, 1),
|
("anew", array_new, 1),
|
||||||
("array-len", array_len, 1),
|
("array-len", array_len, 1),
|
||||||
("array-get", array_get, 1),
|
("array-get", array_get, 1),
|
||||||
|
@ -873,6 +1171,7 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
||||||
("_double", to_double, 1),
|
("_double", to_double, 1),
|
||||||
("_array", to_array, 1),
|
("_array", to_array, 1),
|
||||||
("_str", to_str, 1),
|
("_str", to_str, 1),
|
||||||
|
("_barray", to_bytearray, 1),
|
||||||
("call", call, 0),
|
("call", call, 0),
|
||||||
("callp", callp, 0),
|
("callp", callp, 0),
|
||||||
("trace", trace, 1),
|
("trace", trace, 1),
|
||||||
|
@ -896,6 +1195,17 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
||||||
("throw", throw, 0),
|
("throw", throw, 0),
|
||||||
("write-sasm", write_sasm, 1),
|
("write-sasm", write_sasm, 1),
|
||||||
("write-file-sasm", write_file_sasm, 1),
|
("write-file-sasm", write_file_sasm, 1),
|
||||||
|
("fork", fork, 0),
|
||||||
|
("sleeptime", time, 0),
|
||||||
|
("str-readf", str_readf, 1),
|
||||||
|
("str-to-mega-radix", str_to_mega_radix, 1),
|
||||||
|
("mega-to-str-radix", mega_to_str_radix, 1),
|
||||||
|
("properties", properties, 1),
|
||||||
|
("from-properties", from_properties, 1),
|
||||||
|
("list-files", list_files, 1),
|
||||||
|
("delete-file", delete_file, 1),
|
||||||
|
("delete-dir", delete_dir, 1),
|
||||||
|
("chdir", chdir, 0),
|
||||||
];
|
];
|
||||||
for f in fns {
|
for f in fns {
|
||||||
r.define_func(
|
r.define_func(
|
||||||
|
|
|
@ -1,15 +1,23 @@
|
||||||
use crate::Runtime;
|
use crate::Runtime;
|
||||||
use multicall::multicall;
|
use multicall::multicall;
|
||||||
|
|
||||||
pub const STD: &str = include_str!("../std.spl");
|
pub const STD: &str = include_str!("../spl/std.spl");
|
||||||
pub const NET: &str = include_str!("../net.spl");
|
pub const NET: &str = include_str!("../spl/net.spl");
|
||||||
pub const ITER: &str = include_str!("../iter.spl");
|
pub const ITER: &str = include_str!("../spl/iter.spl");
|
||||||
pub const HTTP: &str = include_str!("../http.spl");
|
pub const HTTP: &str = include_str!("../spl/http.spl");
|
||||||
pub const STREAM: &str = include_str!("../stream.spl");
|
pub const STREAM: &str = include_str!("../spl/stream.spl");
|
||||||
pub const MESSAGING: &str = include_str!("../messaging.spl");
|
pub const MESSAGING: &str = include_str!("../spl/messaging.spl");
|
||||||
pub const ASSEMBLE: &str = include_str!("../assemble.spl");
|
pub const ASSEMBLE: &str = include_str!("../spl/assemble.spl");
|
||||||
pub const ISBPL: &str = include_str!("../isbpl.spl");
|
pub const ISBPL: &str = include_str!("../spl/isbpl.spl");
|
||||||
pub const REPL: &str = include_str!("../repl.spl");
|
pub const REPL: &str = include_str!("../spl/repl.spl");
|
||||||
|
pub const FAST: &str = include_str!("../spl/fast.spl");
|
||||||
|
pub const PURE: &str = include_str!("../spl/pure.spl");
|
||||||
|
pub const TIME: &str = include_str!("../spl/time.spl");
|
||||||
|
pub const SERVER: &str = include_str!("../spl/server.spl");
|
||||||
|
pub const HTTP_SERVER: &str = include_str!("../spl/httpserver/base.spl");
|
||||||
|
pub const HTTP_SERVER_STATIC: &str = include_str!("../spl/httpserver/static.spl");
|
||||||
|
pub const LINKEDLIST: &str = include_str!("../spl/linkedlist.spl");
|
||||||
|
pub const NOP: &str = "";
|
||||||
|
|
||||||
pub fn register(runtime: &mut Runtime) {
|
pub fn register(runtime: &mut Runtime) {
|
||||||
multicall! {
|
multicall! {
|
||||||
|
@ -23,5 +31,13 @@ pub fn register(runtime: &mut Runtime) {
|
||||||
insert("assemble.spl", ASSEMBLE);
|
insert("assemble.spl", ASSEMBLE);
|
||||||
insert("isbpl.spl", ISBPL);
|
insert("isbpl.spl", ISBPL);
|
||||||
insert("repl.spl", REPL);
|
insert("repl.spl", REPL);
|
||||||
|
insert("fast.spl", FAST);
|
||||||
|
insert("pure.spl", PURE);
|
||||||
|
insert("time.spl", TIME);
|
||||||
|
insert("server.spl", SERVER);
|
||||||
|
insert("httpserver/base.spl", HTTP_SERVER);
|
||||||
|
insert("httpserver/static.spl", HTTP_SERVER_STATIC);
|
||||||
|
insert("linkedlist.spl", LINKEDLIST);
|
||||||
|
insert("nop.spl", NOP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
55
src/stream/mod.rs
Normal file
55
src/stream/mod.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
mod rw;
|
||||||
|
mod server;
|
||||||
|
pub use rw::*;
|
||||||
|
pub use server::*;
|
||||||
|
|
||||||
|
use std::sync::{Arc, LazyLock};
|
||||||
|
|
||||||
|
use crate::{mutex::Mut, runtime::*};
|
||||||
|
|
||||||
|
static IS_INITIALIZED: LazyLock<Arc<Mut<bool>>> = LazyLock::new(|| Arc::new(Mut::new(false)));
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct StreamExtraData {
|
||||||
|
peer: Option<(String, u16)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
register_stream_type("cmd", stream_cmd);
|
||||||
|
register_server_stream_type("tcp", server_stream_tcp);
|
||||||
|
*IS_INITIALIZED.lock() = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fn = fn(&mut Stack) -> OError;
|
||||||
|
let fns: [(&str, Fn, u32); 12] = [
|
||||||
|
("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),
|
||||||
|
("new-server-stream", new_server_stream, 1),
|
||||||
|
("accept-server-stream", accept_server_stream, 1),
|
||||||
|
("close-server-stream", close_server_stream, 0),
|
||||||
|
("get-stream-peer", get_stream_peer, 2),
|
||||||
|
("shutdown-input-stream", shutdown_input_stream, 0),
|
||||||
|
];
|
||||||
|
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_as_base: false,
|
||||||
|
origin: o.clone(),
|
||||||
|
fname: None,
|
||||||
|
name: f.0.to_owned(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +1,20 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fs::OpenOptions,
|
hint::black_box,
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
mem,
|
net::{TcpStream, UdpSocket},
|
||||||
net::{Shutdown, TcpStream, UdpSocket},
|
|
||||||
process::{self, Stdio},
|
process::{self, Stdio},
|
||||||
sync::Arc,
|
sync::{Arc, LazyLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use crate::*;
|
||||||
|
|
||||||
use crate::{mutex::Mut, runtime::*, *};
|
use fs::OpenOptions;
|
||||||
|
use mutex::Mut;
|
||||||
|
use stream::StreamExtraData;
|
||||||
|
|
||||||
static STREAM_TYPES: Lazy<Arc<Mut<HashMap<String, StreamType>>>> =
|
static STREAM_TYPES: LazyLock<Arc<Mut<HashMap<String, StreamType>>>> =
|
||||||
Lazy::new(|| Arc::new(Mut::new(HashMap::new())));
|
LazyLock::new(|| Arc::new(Mut::new(HashMap::new())));
|
||||||
static IS_INITIALIZED: Lazy<Arc<Mut<bool>>> = Lazy::new(|| Arc::new(Mut::new(false)));
|
|
||||||
|
|
||||||
/// Registers a custom stream type.
|
/// Registers a custom stream type.
|
||||||
pub fn register_stream_type(
|
pub fn register_stream_type(
|
||||||
|
@ -45,34 +45,57 @@ impl StreamType {
|
||||||
|
|
||||||
/// An SPL stream, holding a reader and a writer, and a function to close it.
|
/// An SPL stream, holding a reader and a writer, and a function to close it.
|
||||||
pub struct Stream {
|
pub struct Stream {
|
||||||
reader: Box<dyn Read + 'static>,
|
pub(super) reader: Box<dyn Read + Send + Sync + 'static>,
|
||||||
writer: Box<dyn Write + 'static>,
|
pub(super) _writer_storage: Option<Box<dyn Write + Send + Sync + 'static>>,
|
||||||
close: fn(&mut Self),
|
pub(super) writer: &'static mut (dyn Write + Send + Sync + 'static),
|
||||||
|
pub extra: StreamExtraData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stream {
|
impl Stream {
|
||||||
pub fn new<T: Read + Write + 'static>(main: T, close: fn(&mut Self)) -> Self {
|
pub fn new<T: Read + Write + Send + Sync + 'static>(main: T) -> 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
|
writer: unsafe {
|
||||||
// written to simultaneously or read from while writing due to the guards put in place
|
(rw.as_mut() as *mut (dyn Write + Send + Sync + 'static))
|
||||||
// by the borrow checker on the Stream.
|
.as_mut()
|
||||||
reader: Box::new(unsafe { mem::transmute::<&mut _, &mut T>(rw.as_mut()) }),
|
.unwrap()
|
||||||
writer: rw,
|
},
|
||||||
close,
|
_writer_storage: None,
|
||||||
|
reader: rw,
|
||||||
|
extra: StreamExtraData::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_split(
|
pub fn new_split(
|
||||||
reader: impl Read + 'static,
|
reader: impl Read + Send + Sync + 'static,
|
||||||
writer: impl Write + 'static,
|
writer: impl Write + Send + Sync + 'static,
|
||||||
close: fn(&mut Self),
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let mut bx = Box::new(writer);
|
||||||
Self {
|
Self {
|
||||||
reader: Box::new(reader),
|
reader: Box::new(reader),
|
||||||
writer: Box::new(writer),
|
writer: unsafe {
|
||||||
close,
|
(bx.as_mut() as *mut (dyn Write + Send + Sync + 'static))
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
},
|
||||||
|
_writer_storage: Some(bx),
|
||||||
|
extra: StreamExtraData::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn append_extra(mut self, f: impl Fn(&mut StreamExtraData)) -> Stream {
|
||||||
|
f(&mut self.extra);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shutdown_write(&mut self) {
|
||||||
|
let mut bx = Box::new(IgnoreWrite());
|
||||||
|
self.writer = unsafe {
|
||||||
|
(bx.as_mut() as *mut (dyn Write + Send + Sync + 'static))
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
self._writer_storage = Some(bx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Read for Stream {
|
impl Read for Stream {
|
||||||
|
@ -119,19 +142,30 @@ impl Write for Stream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct IgnoreWrite();
|
||||||
|
impl Write for IgnoreWrite {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> From<T> for StreamType
|
impl<T> From<T> for StreamType
|
||||||
where
|
where
|
||||||
T: Fn(&mut Stack) -> Result<Stream, Error> + Sync + Send + 'static,
|
T: Fn(&mut Stack) -> Result<Stream, Error> + Sync + Send + 'static,
|
||||||
{
|
{
|
||||||
fn from(value: T) -> Self {
|
fn from(value: T) -> Self {
|
||||||
StreamType {
|
Self {
|
||||||
func: Arc::new(Box::new(value)),
|
func: Arc::new(Box::new(value)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_stream(stack: &mut Stack) -> OError {
|
pub fn new_stream(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(s, Str, stack, "write-stream");
|
require_on_stack!(s, Str, stack, "new-stream");
|
||||||
let stream = get_stream_type(s.clone())
|
let stream = get_stream_type(s.clone())
|
||||||
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-type-{s}"))))?
|
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-type-{s}"))))?
|
||||||
.make_stream(stack)?;
|
.make_stream(stack)?;
|
||||||
|
@ -142,48 +176,36 @@ pub fn new_stream(stack: &mut Stack) -> OError {
|
||||||
|
|
||||||
pub fn write_stream(stack: &mut Stack) -> OError {
|
pub fn write_stream(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(id, Mega, stack, "write-stream");
|
require_on_stack!(id, Mega, stack, "write-stream");
|
||||||
require_array_on_stack!(a, stack, "write-stream");
|
require_byte_array_on_stack!(a, stack, "write-stream");
|
||||||
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}"))))
|
||||||
})?;
|
})?;
|
||||||
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(
|
stack.push(
|
||||||
Value::Mega(
|
Value::Mega(
|
||||||
stream
|
stream
|
||||||
.lock()
|
.lock()
|
||||||
.write(&fixed[..])
|
.write(&a)
|
||||||
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))? as i128,
|
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))? as i128,
|
||||||
)
|
)
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
black_box(&stream.lock_ro()._writer_storage);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_all_stream(stack: &mut Stack) -> OError {
|
pub fn write_all_stream(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(id, Mega, stack, "write-all-stream");
|
require_on_stack!(id, Mega, stack, "write-all-stream");
|
||||||
require_array_on_stack!(a, stack, "write-all-stream");
|
require_byte_array_on_stack!(a, stack, "write-all-stream");
|
||||||
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}"))))
|
||||||
})?;
|
})?;
|
||||||
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
|
stream
|
||||||
.lock()
|
.lock()
|
||||||
.write_all(&fixed[..])
|
.write_all(&a)
|
||||||
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?;
|
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?;
|
||||||
|
black_box(&stream.lock_ro()._writer_storage);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,18 +219,20 @@ pub fn flush_stream(stack: &mut Stack) -> OError {
|
||||||
.lock()
|
.lock()
|
||||||
.flush()
|
.flush()
|
||||||
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?;
|
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?;
|
||||||
|
black_box(&stream.lock_ro()._writer_storage);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_stream(stack: &mut Stack) -> OError {
|
pub fn read_stream(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(id, Mega, stack, "read-stream");
|
require_on_stack!(id, Mega, stack, "read-stream");
|
||||||
let array = stack.pop();
|
let array = stack.pop();
|
||||||
{
|
let kind = array.lock_ro().kind.lock_ro().get_name();
|
||||||
require_mut_array!(a, array, stack, "read-stream");
|
|
||||||
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}"))))
|
||||||
})?;
|
})?;
|
||||||
|
if kind == "array" {
|
||||||
|
require_mut_array!(a, array, stack, "read-stream");
|
||||||
let mut vec = vec![0; a.len()];
|
let mut vec = vec![0; a.len()];
|
||||||
stack.push(
|
stack.push(
|
||||||
Value::Mega(
|
Value::Mega(
|
||||||
|
@ -226,6 +250,19 @@ pub fn read_stream(stack: &mut Stack) -> OError {
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if kind == "bytearray" {
|
||||||
|
require_mut!(a, ByteArray, array, stack, "read-stream");
|
||||||
|
stack.push(
|
||||||
|
Value::Mega(
|
||||||
|
stream
|
||||||
|
.lock()
|
||||||
|
.read(a)
|
||||||
|
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?
|
||||||
|
as i128,
|
||||||
|
)
|
||||||
|
.spl(),
|
||||||
|
);
|
||||||
|
}
|
||||||
stack.push(array);
|
stack.push(array);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -233,12 +270,13 @@ pub fn read_stream(stack: &mut Stack) -> OError {
|
||||||
pub fn read_all_stream(stack: &mut Stack) -> OError {
|
pub fn read_all_stream(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(id, Mega, stack, "read-all-stream");
|
require_on_stack!(id, Mega, stack, "read-all-stream");
|
||||||
let array = stack.pop();
|
let array = stack.pop();
|
||||||
{
|
let kind = array.lock_ro().kind.lock_ro().get_name();
|
||||||
require_mut_array!(a, array, stack, "read-all-stream");
|
|
||||||
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}"))))
|
||||||
})?;
|
})?;
|
||||||
|
if kind == "array" {
|
||||||
|
require_mut_array!(a, array, stack, "read-all-stream");
|
||||||
let mut vec = vec![0; a.len()];
|
let mut vec = vec![0; a.len()];
|
||||||
stream
|
stream
|
||||||
.lock()
|
.lock()
|
||||||
|
@ -250,22 +288,34 @@ pub fn read_all_stream(stack: &mut Stack) -> OError {
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if kind == "bytearray" {
|
||||||
|
require_mut!(a, ByteArray, array, stack, "read-stream");
|
||||||
|
stream
|
||||||
|
.lock()
|
||||||
|
.read_exact(a)
|
||||||
|
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?;
|
||||||
|
}
|
||||||
stack.push(array);
|
stack.push(array);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close_stream(stack: &mut Stack) -> OError {
|
pub fn close_stream(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(id, Mega, stack, "close-stream");
|
require_on_stack!(id, Mega, stack, "close-stream");
|
||||||
if let Some(stream) = runtime(|rt| rt.get_stream(id as u128)) {
|
runtime_mut(|mut rt| rt.destroy_stream(id as u128));
|
||||||
let mut stream = stream.lock();
|
|
||||||
(stream.close)(&mut stream);
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nop(_stream: &mut Stream) {}
|
pub fn shutdown_input_stream(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(id, Mega, stack, "shutdown-input-stream");
|
||||||
|
let stream = runtime(|rt| {
|
||||||
|
rt.get_stream(id as u128)
|
||||||
|
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
||||||
|
})?;
|
||||||
|
stream.lock().shutdown_write();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn stream_file(stack: &mut Stack) -> Result<Stream, Error> {
|
pub(super) fn stream_file(stack: &mut Stack) -> Result<Stream, Error> {
|
||||||
let truncate = stack.pop().lock_ro().is_truthy();
|
let truncate = stack.pop().lock_ro().is_truthy();
|
||||||
require_on_stack!(path, Str, stack, "FILE new-stream");
|
require_on_stack!(path, Str, stack, "FILE new-stream");
|
||||||
Ok(Stream::new(
|
Ok(Stream::new(
|
||||||
|
@ -276,34 +326,23 @@ fn stream_file(stack: &mut Stack) -> Result<Stream, Error> {
|
||||||
.truncate(truncate)
|
.truncate(truncate)
|
||||||
.open(path)
|
.open(path)
|
||||||
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?,
|
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?,
|
||||||
nop,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stream_tcp(stack: &mut Stack) -> Result<Stream, Error> {
|
pub(super) fn stream_tcp(stack: &mut Stack) -> Result<Stream, Error> {
|
||||||
require_int_on_stack!(port, stack, "TCP new-stream");
|
require_int_on_stack!(port, stack, "TCP new-stream");
|
||||||
require_on_stack!(ip, Str, 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(
|
Ok(Stream::new(
|
||||||
TcpStream::connect((ip, port as u16))
|
TcpStream::connect((ip, port as u16))
|
||||||
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?,
|
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?,
|
||||||
close_tcp,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stream_udp(stack: &mut Stack) -> Result<Stream, Error> {
|
pub(super) fn stream_udp(stack: &mut Stack) -> Result<Stream, Error> {
|
||||||
require_int_on_stack!(port, stack, "UDP new-stream");
|
require_int_on_stack!(port, stack, "UDP new-stream");
|
||||||
require_on_stack!(ip, Str, stack, "UDP new-stream");
|
require_on_stack!(ip, Str, stack, "UDP new-stream");
|
||||||
require_int_on_stack!(self_port, stack, "UDP new-stream");
|
require_int_on_stack!(self_port, stack, "UDP new-stream");
|
||||||
require_on_stack!(self_ip, Str, 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))
|
let sock = UdpSocket::bind((self_ip, self_port as u16))
|
||||||
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?;
|
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?;
|
||||||
sock.connect((ip, port as u16))
|
sock.connect((ip, port as u16))
|
||||||
|
@ -323,12 +362,11 @@ fn stream_udp(stack: &mut Stack) -> Result<Stream, Error> {
|
||||||
self.0.recv(buf)
|
self.0.recv(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Stream::new(UdpRW(sock), close_udp))
|
Ok(Stream::new(UdpRW(sock)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stream_cmd(stack: &mut Stack) -> Result<Stream, Error> {
|
pub(super) fn stream_cmd(stack: &mut Stack) -> Result<Stream, Error> {
|
||||||
require_on_stack!(a, Array, stack, "CMD new-stream");
|
require_on_stack!(a, Array, stack, "CMD new-stream");
|
||||||
fn close_cmd(_stream: &mut Stream) {}
|
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
for item in a.iter() {
|
for item in a.iter() {
|
||||||
if let Value::Str(ref s) = item.lock_ro().native {
|
if let Value::Str(ref s) = item.lock_ro().native {
|
||||||
|
@ -336,7 +374,7 @@ fn stream_cmd(stack: &mut Stack) -> Result<Stream, Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
|
return stack.err(ErrorKind::InvalidCall("CMD new-stream".to_owned()));
|
||||||
}
|
}
|
||||||
let mut command = process::Command::new(&args[0])
|
let mut command = process::Command::new(&args[0])
|
||||||
.args(&args[1..])
|
.args(&args[1..])
|
||||||
|
@ -348,40 +386,26 @@ fn stream_cmd(stack: &mut Stack) -> Result<Stream, Error> {
|
||||||
Ok(Stream::new_split(
|
Ok(Stream::new_split(
|
||||||
command.stdout.take().unwrap(),
|
command.stdout.take().unwrap(),
|
||||||
command.stdin.take().unwrap(),
|
command.stdin.take().unwrap(),
|
||||||
close_cmd,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
pub(super) fn get_stream_peer(stack: &mut Stack) -> OError {
|
||||||
if !*IS_INITIALIZED.lock_ro() {
|
require_on_stack!(id, Mega, stack, "get-stream-peer");
|
||||||
register_stream_type("file", stream_file);
|
let Some((addr, port)) = runtime(|rt| -> Result<_, Error> {
|
||||||
register_stream_type("tcp", stream_tcp);
|
Ok(rt
|
||||||
register_stream_type("udp", stream_udp);
|
.get_stream(id as u128)
|
||||||
register_stream_type("cmd", stream_cmd);
|
.ok_or(stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))?
|
||||||
*IS_INITIALIZED.lock() = true;
|
.lock_ro()
|
||||||
}
|
.extra
|
||||||
|
.peer
|
||||||
type Fn = fn(&mut Stack) -> OError;
|
.clone())
|
||||||
let fns: [(&str, Fn, u32); 7] = [
|
})?
|
||||||
("new-stream", new_stream, 1),
|
else {
|
||||||
("write-stream", write_stream, 1),
|
stack.push(Value::Null.spl());
|
||||||
("write-all-stream", write_all_stream, 0),
|
stack.push(Value::Null.spl());
|
||||||
("flush-stream", flush_stream, 0),
|
return Ok(());
|
||||||
("read-stream", read_stream, 1),
|
};
|
||||||
("read-all-stream", read_all_stream, 0),
|
stack.push(addr.spl());
|
||||||
("close-stream", close_stream, 0),
|
stack.push((port as i32).spl());
|
||||||
];
|
Ok(())
|
||||||
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_as_base: false,
|
|
||||||
origin: o.clone(),
|
|
||||||
fname: None,
|
|
||||||
name: f.0.to_owned(),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
106
src/stream/server.rs
Normal file
106
src/stream/server.rs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
net::TcpListener,
|
||||||
|
sync::{Arc, LazyLock},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{mutex::Mut, *};
|
||||||
|
|
||||||
|
use super::Stream;
|
||||||
|
|
||||||
|
static SERVER_STREAM_TYPES: LazyLock<Arc<Mut<HashMap<String, ServerStreamType>>>> =
|
||||||
|
LazyLock::new(|| Arc::new(Mut::new(HashMap::new())));
|
||||||
|
|
||||||
|
/// Registers a custom stream type.
|
||||||
|
pub fn register_server_stream_type(
|
||||||
|
name: &str,
|
||||||
|
supplier: impl Fn(&mut Stack) -> Result<ServerStream, Error> + Sync + Send + 'static,
|
||||||
|
) {
|
||||||
|
SERVER_STREAM_TYPES
|
||||||
|
.lock()
|
||||||
|
.insert(name.to_owned(), ServerStreamType::from(supplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a stream type by name.
|
||||||
|
pub fn get_server_stream_type(name: String) -> Option<ServerStreamType> {
|
||||||
|
SERVER_STREAM_TYPES.lock_ro().get(&name).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An SPL stream type.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ServerStreamType {
|
||||||
|
func: Arc<Box<dyn Fn(&mut Stack) -> Result<ServerStream, Error> + Sync + Send + 'static>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerStreamType {
|
||||||
|
pub fn make_stream(&self, stack: &mut Stack) -> Result<ServerStream, Error> {
|
||||||
|
(self.func)(stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> for ServerStreamType
|
||||||
|
where
|
||||||
|
T: Fn(&mut Stack) -> Result<ServerStream, Error> + Sync + Send + 'static,
|
||||||
|
{
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
Self {
|
||||||
|
func: Arc::new(Box::new(value)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An SPL server stream, holding an acceptor and a function to close it.
|
||||||
|
pub struct ServerStream {
|
||||||
|
pub(super) acceptor: Box<dyn Fn(&mut Stack) -> Result<Stream, Error> + Sync + Send + 'static>,
|
||||||
|
}
|
||||||
|
impl ServerStream {
|
||||||
|
pub fn new(
|
||||||
|
acceptor: impl Fn(&mut Stack) -> Result<Stream, Error> + Sync + Send + 'static,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
acceptor: Box::new(acceptor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_server_stream(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(s, Str, stack, "new-stream");
|
||||||
|
let stream = get_server_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_server_stream(stream)))?;
|
||||||
|
stack.push(Value::Mega(stream.0 as i128).spl());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn accept_server_stream(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(id, Mega, stack, "accept-server-stream");
|
||||||
|
let stream = runtime(|rt| {
|
||||||
|
rt.get_server_stream(id as u128)
|
||||||
|
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
||||||
|
})?;
|
||||||
|
let stream = (stream.lock_ro().acceptor)(stack)?;
|
||||||
|
stack.push((runtime_mut(move |mut rt| rt.register_stream(stream)).0 as i128).spl());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close_server_stream(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(id, Mega, stack, "close-server-stream");
|
||||||
|
runtime_mut(|mut rt| rt.destroy_server_stream(id as u128));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn server_stream_tcp(stack: &mut Stack) -> Result<ServerStream, Error> {
|
||||||
|
require_int_on_stack!(port, stack, "TCP server-stream");
|
||||||
|
require_on_stack!(addr, Str, stack, "TCP server-stream");
|
||||||
|
let tcp = TcpListener::bind((addr, port as u16))
|
||||||
|
.map_err(|e| stack.error(ErrorKind::IO(format!("{e:?}"))))?;
|
||||||
|
let stream = ServerStream::new(move |stack| {
|
||||||
|
let socket = tcp
|
||||||
|
.accept()
|
||||||
|
.map_err(|e| stack.error(ErrorKind::IO(format!("{e:?}"))))?;
|
||||||
|
Ok(Stream::new(socket.0)
|
||||||
|
.append_extra(move |d| d.peer = Some((socket.1.ip().to_string(), socket.1.port()))))
|
||||||
|
});
|
||||||
|
Ok(stream)
|
||||||
|
}
|
159
test.spl
159
test.spl
|
@ -1,11 +1,17 @@
|
||||||
|
[
|
||||||
|
"spl/stream.spl" import
|
||||||
|
"spl/http.spl" import
|
||||||
|
"spl/messaging.spl" import
|
||||||
|
"spl/server.spl" import
|
||||||
|
"spl/time.spl" import
|
||||||
|
"spl/httpserver/base.spl" import
|
||||||
|
"spl/linkedlist.spl" import
|
||||||
|
|
||||||
"#stream.spl" import
|
|
||||||
"#http.spl" import
|
|
||||||
"#messaging.spl" import
|
|
||||||
|
|
||||||
"SPL tester" =program-name
|
"SPL tester" =program-name
|
||||||
|
|
||||||
func main { int | with args ;
|
func main { int | with args ;
|
||||||
|
|
||||||
def thing
|
def thing
|
||||||
|
|
||||||
1 anew =thing
|
1 anew =thing
|
||||||
|
@ -108,18 +114,18 @@ func main { int | with args ;
|
||||||
|
|
||||||
def file "test.txt" 1 StreamTypes:file:create =file
|
def file "test.txt" 1 StreamTypes:file:create =file
|
||||||
"hi\n" :to-bytes file:write-exact;
|
"hi\n" :to-bytes file:write-exact;
|
||||||
file:close null =file
|
file:close; null =file
|
||||||
|
|
||||||
"" println
|
"" println
|
||||||
"testing split" println
|
"testing split" println
|
||||||
{ | println } (" " "hello how are you" :split):foreach
|
{ | println } (" " "hello how are you" :split):foreach
|
||||||
"" println
|
"" println
|
||||||
|
|
||||||
catch {
|
|
||||||
use net:http:Request
|
use net:http:Request
|
||||||
"testing http" println
|
catch {
|
||||||
|
"testing http" println;
|
||||||
def req "data.tudbut.de" 80 "GET" "/spltest" Request:new =req
|
def req "data.tudbut.de" 80 "GET" "/spltest" Request:new =req
|
||||||
req:send:body _str println
|
req:send:body _str println;
|
||||||
} { with e ;
|
} { with e ;
|
||||||
e:message println
|
e:message println
|
||||||
"it seems the internet is not available" println
|
"it seems the internet is not available" println
|
||||||
|
@ -151,6 +157,145 @@ func main { int | with args ;
|
||||||
"testmsg1" bus:publish
|
"testmsg1" bus:publish
|
||||||
"testmsg3" bus:publish
|
"testmsg3" bus:publish
|
||||||
|
|
||||||
|
"" println
|
||||||
|
"testing threads" println
|
||||||
|
|
||||||
|
def other-thread-done 0 =other-thread-done
|
||||||
|
{ |
|
||||||
|
"i am in the other thread!!!" println
|
||||||
|
1 =other-thread-done
|
||||||
|
} fork
|
||||||
|
while { other-thread-done not } { "waiting for the other thread..." println }
|
||||||
|
|
||||||
|
"" println
|
||||||
|
"testing tcp server" println
|
||||||
|
|
||||||
|
" starting server thread" println
|
||||||
|
{ |
|
||||||
|
def server "0.0.0.0" 4075 net:server:Types:tcp:create =server
|
||||||
|
while { 1 } {
|
||||||
|
def stream server:accept =stream
|
||||||
|
"Hello!" :to-bytes stream:write-exact;
|
||||||
|
stream:close;
|
||||||
|
}
|
||||||
|
} fork;
|
||||||
|
50 time:sleep;
|
||||||
|
" starting client" println;
|
||||||
|
def client "localhost" 4075 StreamTypes:tcp:create =client
|
||||||
|
1024 client:read-to-end:to-str println;
|
||||||
|
" ^ this should say 'Hello!'" println;
|
||||||
|
|
||||||
|
"" println
|
||||||
|
"testing string replace" println;
|
||||||
|
|
||||||
|
"aba" "!!" "ababab" :replace println;
|
||||||
|
" ^ should be !!bab." println;
|
||||||
|
"aba" "!!" "aababab" :replace println;
|
||||||
|
" ^ should be a!!bab." println;
|
||||||
|
|
||||||
|
"" println;
|
||||||
|
"testing string split" println;
|
||||||
|
|
||||||
|
"ba" "abaabaabaa" :split:iter:join<", "> println;
|
||||||
|
" ^ should be a, a, a, a" println;
|
||||||
|
"ba" "abbaabbaababaa" :split:iter:join<", "> println;
|
||||||
|
" ^ should be ab, ab, a, , a" println;
|
||||||
|
|
||||||
|
"" println;
|
||||||
|
"testing string find" println;
|
||||||
|
|
||||||
|
"abba" "ababba" :find println;
|
||||||
|
"^ should be 2" println;
|
||||||
|
|
||||||
|
"" println;
|
||||||
|
"testing readf" println;
|
||||||
|
|
||||||
|
def array
|
||||||
|
"Hello dear {}, {}?" "Hello dear friend, how are you?" (dup println;) :readf =array
|
||||||
|
"Person was " 0 array:get concat println;
|
||||||
|
"Question was " 1 array:get concat println;
|
||||||
|
|
||||||
|
"" println;
|
||||||
|
"testing http server" println;
|
||||||
|
|
||||||
|
def server "0.0.0.0" 4076 net:http:Server:new =server
|
||||||
|
{ |
|
||||||
|
while { 1 } {
|
||||||
|
server
|
||||||
|
:accept
|
||||||
|
:read
|
||||||
|
:write-ok
|
||||||
|
:write-str-body<"Hello! This was written to HTTP!">
|
||||||
|
:finish
|
||||||
|
}
|
||||||
|
} fork
|
||||||
|
def req "localhost" 4076 "GET" "/spltest" Request:new =req
|
||||||
|
req:send:body _str println;
|
||||||
|
|
||||||
|
"" println;
|
||||||
|
"testing urlencoding";
|
||||||
|
|
||||||
|
"hello! this is a test of URL encoding!" net:http:urlencode dup println;
|
||||||
|
net:http:urldecode println;
|
||||||
|
|
||||||
|
"" println;
|
||||||
|
"testing linked lists" println;
|
||||||
|
|
||||||
|
def list LinkedList:new =list
|
||||||
|
"=> len of an empty list: " print;
|
||||||
|
list:len println;
|
||||||
|
|
||||||
|
"=> len of a list with one element: " print;
|
||||||
|
list:push;<"Hello!">
|
||||||
|
list:len println;
|
||||||
|
|
||||||
|
"=> list should not have a next yet... " print;
|
||||||
|
list:next null eq dup if { "ok" swap } not if { "BAD" } println
|
||||||
|
|
||||||
|
"=> element zero should be 'Hello!': " print list:get<0> println;
|
||||||
|
|
||||||
|
"=> iter of list should start with that too: " print list:iter dup:next println;
|
||||||
|
"=> then should be null: " print :next dup null eq if { pop "ok" } println;
|
||||||
|
|
||||||
|
"=> list should contain 'Hello!': " print list:iter:join<", "> println;
|
||||||
|
|
||||||
|
"=> with new element after that: " print
|
||||||
|
list:push;<"One!!">
|
||||||
|
list:iter:join<", "> println;
|
||||||
|
|
||||||
|
"=> pushing numbers 2..10: " print
|
||||||
|
2 10 Range:new:iter:foreach;<{ | list:push; }>
|
||||||
|
list:iter:join<", "> println;
|
||||||
|
|
||||||
|
"=> popping 9: " print
|
||||||
|
list:pop;
|
||||||
|
list:iter:join<", "> println;
|
||||||
|
"=> removing 5: " print
|
||||||
|
list:remove;<5>
|
||||||
|
list:iter:join<", "> println;
|
||||||
|
"=> popping front: " print
|
||||||
|
list:pop-front;
|
||||||
|
list:iter:join<", "> println;
|
||||||
|
|
||||||
|
"=> inserting 0 back: " print
|
||||||
|
0 list:insert;<0>
|
||||||
|
list:iter:join<", "> println;
|
||||||
|
|
||||||
|
"=> inserting 5 back: " print
|
||||||
|
5 list:insert;<5>
|
||||||
|
list:iter:join<", "> println;
|
||||||
|
|
||||||
|
|
||||||
|
5 :foreach<{ | pop "" println }>
|
||||||
|
|
||||||
|
|
||||||
|
] dup :len 0 eq not if {
|
||||||
|
"" println
|
||||||
|
"!! something went wrong somewhere. the stack is not empty." println
|
||||||
|
dyn-__dump
|
||||||
|
}
|
||||||
|
"you now have a chance to connect too: localhost :4075 :4076 - stopping in 5 seconds..." println;
|
||||||
|
5000 time:sleep;
|
||||||
100
|
100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
test.txt
1
test.txt
|
@ -1 +0,0 @@
|
||||||
hi
|
|
Loading…
Add table
Reference in a new issue