Compare commits
No commits in common. "main" and "v0.3.1" have entirely different histories.
36 changed files with 363 additions and 2160 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -8,6 +8,12 @@ version = "0.1.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "428f6ba17d0c927e57c15a86cf5d7d07a2f35b3fbf15b1eb36b7075459e150a3"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "readformat"
|
||||
version = "0.1.2"
|
||||
|
@ -16,8 +22,9 @@ checksum = "b03f7fbd470aa8b3ad163c85cce8bccfc11cc9c44ef12da0a4eddd98bd307352"
|
|||
|
||||
[[package]]
|
||||
name = "spl"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"multicall",
|
||||
"once_cell",
|
||||
"readformat",
|
||||
]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "spl"
|
||||
version = "0.4.0"
|
||||
version = "0.3.1"
|
||||
edition = "2021"
|
||||
description = "Stack Pogramming Language: A simple, concise scripting language."
|
||||
license = "MIT"
|
||||
|
@ -9,4 +9,5 @@ authors = ["TudbuT"]
|
|||
|
||||
[dependencies]
|
||||
readformat = "0.1"
|
||||
once_cell = "1.17"
|
||||
multicall = "0.1"
|
||||
|
|
7
LICENSE
7
LICENSE
|
@ -1,7 +0,0 @@
|
|||
Copyright 2024 TudbuT <legal@mail.tudbut.de>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
14
README.md
14
README.md
|
@ -10,7 +10,7 @@ func main { mega | with args ;
|
|||
{ str | " " concat } swap:map
|
||||
&print swap:foreach
|
||||
"" println
|
||||
println<"and with that, we're done">
|
||||
println <{ "and with that, we're done" }
|
||||
0
|
||||
}
|
||||
```
|
||||
|
@ -177,19 +177,19 @@ func main { mega | with args ;
|
|||
```
|
||||
- SPL actually isn't fully concatenative. It supports postfix arguments as well:
|
||||
```js
|
||||
println<"and with that, we're done">
|
||||
println <{ "and with that, we're done" }
|
||||
```
|
||||
This is actually not a special interpreter feature, more so is it a special
|
||||
lexer feature. This is 100% equivalent with the non-postfix version, where the
|
||||
string is right before the `println`.
|
||||
|
||||
The same can be done for object calls. Let's rewrite the previous code with
|
||||
prefix notation:
|
||||
postfix:
|
||||
```js
|
||||
Range:new<0 5>
|
||||
Range:new <{ 0 5 }
|
||||
:iter
|
||||
:map<{ | 5 * }>
|
||||
:foreach<{ | _str println }>
|
||||
:map <{ { | 5 * } }
|
||||
:foreach <{ { | _str println } }
|
||||
```
|
||||
|
||||
I lied. This is now no longer 100% equivalent. Let's look at what happens
|
||||
|
@ -261,4 +261,4 @@ As you can see, it's relatively straight-forward to do; but there are some major
|
|||
|
||||
The second one is easy to fix, but I intend to fix the first one first. Sadly, fixing it requires
|
||||
compiling the code as a dynamic library and also getting it to work with the program its running in.
|
||||
If anyone knows how to do this properly, I'd REALLY appreciate a PR or issue explaining it.
|
||||
If anyone knows how to do this properly, I'd REALLY appreciate a PR or issue explaining it.
|
|
@ -1,61 +0,0 @@
|
|||
"#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
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
"stream.spl" import
|
||||
"net.spl" import
|
||||
"#stream.spl" import
|
||||
"#net.spl" import
|
||||
|
||||
"http" net:register
|
||||
|
||||
|
@ -7,44 +7,6 @@ construct net:http namespace {
|
|||
Request
|
||||
Response
|
||||
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 {
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
func main { mega | with args ;
|
||||
[ "sudo" "mkdir" "/usr/lib/spl" ] command-wait;
|
||||
[ "sh" "-c" "sudo cp -r spl/* /usr/lib/spl" ] command-wait;
|
||||
[ "sh" "-c" "sudo cp *.spl /usr/lib/spl" ] command-wait;
|
||||
[ "cargo" "build" "--release" ] command-wait;
|
||||
[ "sudo" "rm" "/bin/spl" ] command-wait;
|
||||
[ "sudo" "cp" "target/release/spl" "/bin" ] command-wait;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
"#time.spl" import
|
||||
native engage-compatibility-mode
|
||||
func # { pop }
|
||||
func puts { print }
|
||||
|
@ -6,8 +5,6 @@ func fcall { call }
|
|||
func strconcat { concat }
|
||||
func aget { swap :get }
|
||||
func stoi { _mega }
|
||||
func stol { _mega }
|
||||
func _int { _mega }
|
||||
func itos { _str }
|
||||
def argv'pre-isbplmod &argv =argv'pre-isbplmod
|
||||
func argv {
|
||||
|
@ -16,22 +13,6 @@ func argv {
|
|||
args nargs 2 0 nargs:len acopy
|
||||
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 {
|
||||
"isbpl.spl: ISBPL code tried to call inc, an untranslatable function" println
|
||||
pop
|
||||
|
@ -40,11 +21,3 @@ func dec {
|
|||
"isbpl.spl: ISBPL code tried to call inc, an untranslatable function" println
|
||||
pop
|
||||
}
|
||||
func getms {
|
||||
0 time:unixms
|
||||
}
|
||||
func sleep {
|
||||
time:sleep
|
||||
}
|
||||
|
||||
func main { }
|
|
@ -11,14 +11,13 @@ construct _Iter {
|
|||
arr
|
||||
}
|
||||
foreach { | with callable this ;
|
||||
!!-
|
||||
while { !!- this:next dup null eq not } {
|
||||
!!- callable callp
|
||||
def itm
|
||||
while { this:next dup =itm null eq not } {
|
||||
itm callable call
|
||||
}
|
||||
pop
|
||||
}
|
||||
collect { array | with this ;
|
||||
[ { | } this:foreach ]
|
||||
[ { any | } this:foreach ]
|
||||
}
|
||||
map { MapIter | with map-function this ;
|
||||
map-function this MapIter:new
|
||||
|
@ -42,7 +41,7 @@ construct _Iter {
|
|||
join { str | with separator this ;
|
||||
{ str | with accum item ;
|
||||
accum _str separator item _str concat concat
|
||||
} this:reduce:calculate "" or
|
||||
} this:reduce:calculate
|
||||
}
|
||||
filter { FilterIter | with filter this ;
|
||||
filter this FilterIter:new
|
|
@ -1,4 +1,4 @@
|
|||
"messaging bus, aka event bus";
|
||||
"messaging bus, aka event bus"
|
||||
|
||||
construct messaging namespace {
|
||||
Message
|
|
@ -4,6 +4,6 @@
|
|||
construct net namespace {
|
||||
;
|
||||
register { | with name this ;
|
||||
name "net" register-field
|
||||
name "net" this register-field
|
||||
}
|
||||
}
|
29
repl.spl
Normal file
29
repl.spl
Normal file
|
@ -0,0 +1,29 @@
|
|||
|
||||
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
89
spl/fast.spl
|
@ -1,89 +0,0 @@
|
|||
|
||||
"#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,164 +0,0 @@
|
|||
"../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
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
"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,134 +0,0 @@
|
|||
|
||||
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
|
49
spl/pure.spl
49
spl/pure.spl
|
@ -1,49 +0,0 @@
|
|||
"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
70
spl/repl.spl
|
@ -1,70 +0,0 @@
|
|||
|
||||
"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
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
"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;
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
construct time namespace {
|
||||
;
|
||||
unixms { mega | with this ;
|
||||
0 sleeptime
|
||||
}
|
||||
sleep { mega | with this ; _mega sleeptime }
|
||||
}
|
||||
|
65
src/lexer.rs
65
src/lexer.rs
|
@ -3,7 +3,7 @@ mod compat;
|
|||
use std::{mem, sync::Arc};
|
||||
|
||||
use crate::runtime::*;
|
||||
use compat::{match_compat, transform_compat};
|
||||
use compat::match_compat;
|
||||
use readformat::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
@ -24,15 +24,6 @@ pub fn lex(compat: bool, input: String) -> Result<Words, LexerError> {
|
|||
fn read_block(
|
||||
str_words: &[String],
|
||||
isfn: bool,
|
||||
compat: bool,
|
||||
) -> Result<(Option<u32>, Words, usize), LexerError> {
|
||||
read_block_dyn(str_words, isfn, "}".to_owned(), compat)
|
||||
}
|
||||
|
||||
fn read_block_dyn(
|
||||
str_words: &[String],
|
||||
isfn: bool,
|
||||
endword: String,
|
||||
mut compat: bool,
|
||||
) -> Result<(Option<u32>, Words, usize), LexerError> {
|
||||
if str_words.is_empty() {
|
||||
|
@ -57,11 +48,7 @@ fn read_block_dyn(
|
|||
rem = Some(r as u32);
|
||||
}
|
||||
while i < str_words.len() {
|
||||
let word = if !compat {
|
||||
str_words[i].to_owned()
|
||||
} else {
|
||||
transform_compat(&str_words[i])
|
||||
};
|
||||
let word = str_words[i].to_owned();
|
||||
if compat && match_compat(word.to_owned(), str_words, &mut words, &mut i) {
|
||||
continue;
|
||||
}
|
||||
|
@ -121,24 +108,11 @@ fn read_block_dyn(
|
|||
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] == "!{" => {
|
||||
words.push(Word::Const(Value::Str(x[2..].to_owned())));
|
||||
}
|
||||
"<" => {
|
||||
let block = read_block_dyn(&str_words[i + 1..], false, ">".to_owned(), compat)?;
|
||||
"<{" => {
|
||||
let block = read_block(&str_words[i + 1..], false, compat)?;
|
||||
i += block.2 + 1;
|
||||
let mut block = block.1.words;
|
||||
match words.remove(words.len() - 1) {
|
||||
|
@ -253,24 +227,12 @@ fn read_block_dyn(
|
|||
}
|
||||
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 => {
|
||||
"}" => {
|
||||
break;
|
||||
}
|
||||
x if x.starts_with('\"') => {
|
||||
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.starts_with('_')
|
||||
&& x.contains(char::is_numeric) =>
|
||||
|
@ -324,11 +286,7 @@ fn read_block_dyn(
|
|||
Ok((rem, Words { words }, i))
|
||||
}
|
||||
|
||||
pub fn parse(mut input: String) -> Vec<String> {
|
||||
if input.starts_with("#!") {
|
||||
input = input.split_off(input.find('\n').expect("cannot have #! without newline"));
|
||||
}
|
||||
|
||||
fn parse(input: String) -> Vec<String> {
|
||||
let mut words = Vec::new();
|
||||
let mut s = String::new();
|
||||
|
||||
|
@ -388,17 +346,6 @@ pub fn parse(mut input: String) -> Vec<String> {
|
|||
if c == '(' || c == ')' {
|
||||
continue;
|
||||
}
|
||||
if c == '<' || c == '>' {
|
||||
if s.is_empty() {
|
||||
words.push(c.to_string());
|
||||
continue;
|
||||
}
|
||||
words.push(s);
|
||||
s = String::new();
|
||||
was_in_string = false;
|
||||
words.push(c.to_string());
|
||||
continue;
|
||||
}
|
||||
if c == ' ' || c == '\t' {
|
||||
if s.is_empty() {
|
||||
continue;
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
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(
|
||||
word: String,
|
||||
str_words: &[String],
|
||||
|
@ -22,7 +15,6 @@ pub(crate) fn match_compat(
|
|||
true
|
||||
}
|
||||
"dec" => {
|
||||
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() + &str_words[*i - 1], false, 0));
|
||||
|
@ -30,9 +22,8 @@ pub(crate) fn match_compat(
|
|||
true
|
||||
}
|
||||
"include" => {
|
||||
words.push(Word::Call("isbplmod'import-transform".to_owned(), false, 0));
|
||||
// TODO: translate some stdlib components?
|
||||
words.push(Word::Call("import".to_owned(), false, 0));
|
||||
*i += 1;
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
|
|
43
src/lib.rs
43
src/lib.rs
|
@ -101,24 +101,6 @@ 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_rules! require_int_on_stack {
|
||||
($name:tt, $stack:expr, $fn:literal) => {
|
||||
|
@ -151,31 +133,6 @@ nofmt! {
|
|||
};
|
||||
}
|
||||
#[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 {
|
||||
($name:tt, $stack:expr, $fn:literal) => {
|
||||
let binding = $stack.pop();
|
||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -1,6 +1,4 @@
|
|||
use spl::{
|
||||
lex, oxidizer::RustAppBuilder, sasm::sasm_write, start_file_in_runtime, Runtime, SetRuntime,
|
||||
};
|
||||
use spl::{lex, oxidizer::RustAppBuilder, start_file_in_runtime, Runtime, SetRuntime};
|
||||
|
||||
use std::{env::args, fs};
|
||||
|
||||
|
@ -8,14 +6,9 @@ fn main() {
|
|||
Runtime::new().set();
|
||||
let mut args = args().skip(1);
|
||||
let arg = &args.next().unwrap_or("#repl.spl".to_owned());
|
||||
if arg == "--build" || arg == "--run" || arg == "--buildrun" || arg == "--debug" {
|
||||
if arg == "--build" || arg == "--run" || arg == "--buildrun" {
|
||||
let file = args.next().unwrap();
|
||||
let data = fs::read_to_string(file.clone()).expect("unable to read specified file");
|
||||
if arg == "--debug" {
|
||||
println!("words: {}", spl::parse(data.clone()).join(" "));
|
||||
println!("{}", sasm_write(lex(false, data).unwrap()));
|
||||
return;
|
||||
}
|
||||
let build_only = arg == "--build";
|
||||
if build_only {
|
||||
println!("Building SPL with specified natives file...");
|
||||
|
|
135
src/runtime.rs
135
src/runtime.rs
|
@ -46,15 +46,6 @@ 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> {
|
||||
runtime(|rt| rt.get_type_by_name(name))
|
||||
}
|
||||
|
@ -72,7 +63,6 @@ pub struct Runtime {
|
|||
types_by_id: HashMap<u32, AMType>,
|
||||
next_stream_id: u128,
|
||||
streams: HashMap<u128, Arc<Mut<Stream>>>,
|
||||
server_streams: HashMap<u128, Arc<Mut<ServerStream>>>,
|
||||
pub embedded_files: HashMap<&'static str, &'static str>,
|
||||
pub native_functions: HashMap<&'static str, (u32, FuncImpl)>,
|
||||
}
|
||||
|
@ -102,7 +92,6 @@ impl Runtime {
|
|||
types_by_id: HashMap::new(),
|
||||
next_stream_id: 0,
|
||||
streams: HashMap::new(),
|
||||
server_streams: HashMap::new(),
|
||||
embedded_files: HashMap::new(),
|
||||
native_functions: HashMap::new(),
|
||||
};
|
||||
|
@ -115,7 +104,6 @@ impl Runtime {
|
|||
let _ = rt.make_type("func".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("bytearray".to_owned(), Ok); // infallible
|
||||
stdlib::register(&mut rt);
|
||||
rt
|
||||
}
|
||||
|
@ -163,23 +151,6 @@ impl Runtime {
|
|||
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) {
|
||||
self.native_functions.get(name).unwrap_or_else(|| {
|
||||
panic!(
|
||||
|
@ -474,9 +445,6 @@ impl Stack {
|
|||
}
|
||||
|
||||
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 {
|
||||
Frame::new_in(
|
||||
func.origin.clone(),
|
||||
|
@ -493,11 +461,6 @@ impl Stack {
|
|||
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> {
|
||||
let mut frame = self.frames.last().unwrap();
|
||||
loop {
|
||||
|
@ -519,13 +482,14 @@ impl Stack {
|
|||
frame = self.frames.first().unwrap().clone();
|
||||
}
|
||||
let tmpname = name.clone();
|
||||
let tmpframe = frame.clone();
|
||||
frame.functions.lock().insert(
|
||||
name.clone(),
|
||||
Arc::new(Func {
|
||||
ret_count: 1,
|
||||
origin: Arc::new(Frame::dummy()),
|
||||
origin: frame.clone(),
|
||||
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
|
||||
stack.push(stack.get_frame().get_var(tmpname.clone(), stack)?);
|
||||
stack.push(tmpframe.get_var(tmpname.clone(), stack)?);
|
||||
Ok(())
|
||||
}))),
|
||||
run_as_base: false,
|
||||
|
@ -534,14 +498,15 @@ impl Stack {
|
|||
}),
|
||||
);
|
||||
let tmpname = name.clone();
|
||||
let tmpframe = frame.clone();
|
||||
frame.functions.lock().insert(
|
||||
"=".to_owned() + &name,
|
||||
Arc::new(Func {
|
||||
ret_count: 0,
|
||||
origin: Arc::new(Frame::dummy()),
|
||||
origin: frame.clone(),
|
||||
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
|
||||
let v = stack.pop();
|
||||
stack.get_frame().set_var(tmpname.clone(), v, stack)
|
||||
tmpframe.set_var(tmpname.clone(), v, stack)
|
||||
}))),
|
||||
run_as_base: false,
|
||||
fname: Some("RUNTIME".to_owned()),
|
||||
|
@ -675,21 +640,6 @@ pub enum Keyword {
|
|||
/// equivalent to dyn-__dump
|
||||
/// example: func main { int | "Hello, world!" dyn-__dump pop 0 }
|
||||
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>
|
||||
///
|
||||
/// Defines a variable.
|
||||
|
@ -773,7 +723,6 @@ pub enum Value {
|
|||
Double(f64),
|
||||
Func(AFunc),
|
||||
Array(Vec<AMObject>),
|
||||
ByteArray(Vec<u8>),
|
||||
Str(String),
|
||||
}
|
||||
|
||||
|
@ -801,10 +750,6 @@ impl PartialOrd for Value {
|
|||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
match (self, other) {
|
||||
(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!(),
|
||||
}
|
||||
}
|
||||
|
@ -839,7 +784,7 @@ impl Words {
|
|||
#[derive(Clone)]
|
||||
pub enum FuncImpl {
|
||||
Native(fn(&mut Stack) -> OError),
|
||||
NativeDyn(Arc<Box<dyn (Fn(&mut Stack) -> OError) + Send + Sync>>),
|
||||
NativeDyn(Arc<Box<dyn Fn(&mut Stack) -> OError>>),
|
||||
SPL(Words),
|
||||
}
|
||||
|
||||
|
@ -1069,7 +1014,6 @@ impl Object {
|
|||
Value::Func(_) => true,
|
||||
Value::Array(_) => true,
|
||||
Value::Str(x) => !x.is_empty(),
|
||||
Value::ByteArray(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1129,7 +1073,6 @@ impl From<Value> for Object {
|
|||
Value::Func(_) => x.get_type_by_id(6),
|
||||
Value::Array(_) => x.get_type_by_id(7),
|
||||
Value::Str(_) => x.get_type_by_id(8),
|
||||
Value::ByteArray(_) => x.get_type_by_id(9),
|
||||
}
|
||||
.expect("runtime uninitialized: default types not set.")
|
||||
}),
|
||||
|
@ -1192,35 +1135,20 @@ impl Words {
|
|||
/// Executes the words. This does *not* create a new frame on the stack. Use [Stack::call] to
|
||||
/// call and create a new frame.
|
||||
pub fn exec(&self, stack: &mut Stack) -> OError {
|
||||
let mut inline_calls = false;
|
||||
for word in self.words.iter() {
|
||||
for word in self.words.clone() {
|
||||
match word {
|
||||
Word::Key(x) => match x {
|
||||
Keyword::Dump => println!("{stack}"),
|
||||
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::Def(x) => stack.define_var(x),
|
||||
Keyword::Func(name, rem, words) => stack.define_func(
|
||||
name.clone(),
|
||||
Arc::new(Func {
|
||||
ret_count: *rem,
|
||||
to_call: FuncImpl::SPL(words.to_owned()),
|
||||
ret_count: rem,
|
||||
to_call: FuncImpl::SPL(words),
|
||||
origin: stack.get_frame(),
|
||||
run_as_base: false,
|
||||
fname: None,
|
||||
name: name.to_owned(),
|
||||
name,
|
||||
}),
|
||||
),
|
||||
Keyword::Construct(name, fields, methods, is_namespace) => {
|
||||
|
@ -1231,14 +1159,14 @@ impl Words {
|
|||
let t = runtime_mut(|mut rt| {
|
||||
rt.make_type(name.clone(), |mut t| {
|
||||
for field in fields {
|
||||
t.add_property(field.to_owned(), origin.clone())?;
|
||||
t.add_property(field, origin.clone())?;
|
||||
}
|
||||
t.functions.extend(methods.into_iter().map(|(k, v)| {
|
||||
(
|
||||
k.clone(),
|
||||
Arc::new(Func {
|
||||
ret_count: v.0,
|
||||
to_call: FuncImpl::SPL(v.1.to_owned()),
|
||||
to_call: FuncImpl::SPL(v.1),
|
||||
origin: origin.clone(),
|
||||
run_as_base: false,
|
||||
fname: None,
|
||||
|
@ -1250,7 +1178,7 @@ impl Words {
|
|||
})
|
||||
})?;
|
||||
|
||||
let to_set: Object = if *is_namespace {
|
||||
let to_set: Object = if is_namespace {
|
||||
let mut obj: Object = Value::Null.into();
|
||||
obj.kind = t.clone();
|
||||
t.lock_ro().write_into(&mut obj);
|
||||
|
@ -1279,14 +1207,13 @@ impl Words {
|
|||
let rstack = &stack;
|
||||
runtime(move |rt| {
|
||||
rt.get_type_by_name(&tb)
|
||||
.ok_or_else(|| {
|
||||
rstack.error(ErrorKind::TypeNotFound(tb.to_owned()))
|
||||
})?
|
||||
.ok_or_else(|| rstack.error(ErrorKind::TypeNotFound(tb)))?
|
||||
.lock()
|
||||
.parents
|
||||
.push(rt.get_type_by_name(&ta).ok_or_else(|| {
|
||||
rstack.error(ErrorKind::TypeNotFound(ta.to_owned()))
|
||||
})?);
|
||||
.push(
|
||||
rt.get_type_by_name(&ta)
|
||||
.ok_or_else(|| rstack.error(ErrorKind::TypeNotFound(ta)))?,
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
@ -1335,7 +1262,7 @@ impl Words {
|
|||
for var in vars.into_iter().rev() {
|
||||
stack.define_var(var.clone());
|
||||
let obj = stack.pop();
|
||||
stack.set_var(var.to_owned(), obj)?;
|
||||
stack.set_var(var, obj)?;
|
||||
}
|
||||
}
|
||||
Keyword::ObjPush => {
|
||||
|
@ -1359,7 +1286,7 @@ impl Words {
|
|||
origin: stack.get_frame(),
|
||||
run_as_base: false,
|
||||
fname: None,
|
||||
name: name.to_owned(),
|
||||
name,
|
||||
}),
|
||||
)
|
||||
}),
|
||||
|
@ -1371,7 +1298,6 @@ impl Words {
|
|||
stack.push(x.clone().ensure_init(stack).spl())
|
||||
}
|
||||
Word::Call(x, rem, ra) => {
|
||||
let ra = *ra;
|
||||
if option_env!("SPLDEBUG").is_some() {
|
||||
println!("CALL({}) {x}", stack.len());
|
||||
}
|
||||
|
@ -1398,12 +1324,8 @@ impl Words {
|
|||
}
|
||||
stack.push(f.spl());
|
||||
} else {
|
||||
if inline_calls {
|
||||
stack.fast_call(&f)?;
|
||||
} else {
|
||||
stack.call(&f)?;
|
||||
}
|
||||
if *rem {
|
||||
stack.call(&f)?;
|
||||
if rem {
|
||||
for _ in 0..f.ret_count {
|
||||
stack.pop();
|
||||
}
|
||||
|
@ -1411,7 +1333,6 @@ impl Words {
|
|||
}
|
||||
}
|
||||
Word::ObjCall(x, rem, ra) => {
|
||||
let ra = *ra;
|
||||
let o = stack.peek();
|
||||
let o = o.lock_ro();
|
||||
let f0 = o.kind.lock_ro();
|
||||
|
@ -1449,12 +1370,8 @@ impl Words {
|
|||
stack.pop();
|
||||
stack.push(f.spl())
|
||||
} else {
|
||||
if inline_calls {
|
||||
stack.fast_call(&f)?;
|
||||
} else {
|
||||
stack.call(&f)?;
|
||||
}
|
||||
if *rem {
|
||||
stack.call(&f)?;
|
||||
if rem {
|
||||
for _ in 0..f.ret_count {
|
||||
stack.pop();
|
||||
}
|
||||
|
|
44
src/sasm.rs
44
src/sasm.rs
|
@ -24,9 +24,6 @@ fn sasm_parse<'a>(line: &str, words: &mut Vec<Word>, lines: &mut impl Iterator<I
|
|||
let line: Vec<_> = line.split(" ").collect();
|
||||
match line[0] {
|
||||
"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()))),
|
||||
"func" => words.push(Word::Key(Keyword::Func(
|
||||
line[1].to_owned(),
|
||||
|
@ -175,25 +172,6 @@ fn sasm_parse<'a>(line: &str, words: &mut Vec<Word>, lines: &mut impl Iterator<I
|
|||
})))),
|
||||
"null" => words.push(Word::Const(Value::Null)),
|
||||
"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]),
|
||||
},
|
||||
"call" => {
|
||||
|
@ -245,9 +223,6 @@ fn sasm_write_func(words: Words) -> String {
|
|||
Keyword::Dump => {
|
||||
output += "dump\n";
|
||||
}
|
||||
Keyword::InlineCallable => output += "inline-callable\n",
|
||||
Keyword::InlineStart => output += "inline-start\n",
|
||||
Keyword::InlineEnd => output += "inline-end\n",
|
||||
Keyword::Def(x) => {
|
||||
output += "def ";
|
||||
output += &x;
|
||||
|
@ -375,25 +350,6 @@ fn sasm_write_func(words: Words) -> String {
|
|||
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) => {
|
||||
output += "call ";
|
||||
|
|
436
src/std_fns.rs
436
src/std_fns.rs
|
@ -1,17 +1,14 @@
|
|||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
env::{self, args, vars},
|
||||
collections::VecDeque,
|
||||
env::{args, vars},
|
||||
fs,
|
||||
io::{stdin, stdout, Write},
|
||||
mem,
|
||||
ops::{Add, Div, Mul, Rem, Sub},
|
||||
process::{self, Stdio},
|
||||
sync::Arc,
|
||||
thread,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use readformat::readf;
|
||||
|
||||
use crate::{dyn_fns, mutex::Mut, runtime::*, sasm::sasm_write, *};
|
||||
|
||||
#[macro_export]
|
||||
|
@ -53,13 +50,6 @@ pub fn dup(stack: &mut Stack) -> OError {
|
|||
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 {
|
||||
stack.pop();
|
||||
Ok(())
|
||||
|
@ -95,27 +85,12 @@ pub fn settype(stack: &mut Stack) -> OError {
|
|||
let o = stack.pop();
|
||||
let kind = runtime(|rt| rt.get_type_by_name(&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();
|
||||
kind.lock_ro().write_into(&mut obj);
|
||||
obj.kind = kind;
|
||||
mem::drop(obj);
|
||||
stack.push(o);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn gettype(stack: &mut Stack) -> OError {
|
||||
|
@ -124,23 +99,6 @@ pub fn gettype(stack: &mut Stack) -> OError {
|
|||
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 {
|
||||
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
|
||||
return stack.err(ErrorKind::InvalidCall("anew".to_owned()));
|
||||
|
@ -154,46 +112,36 @@ pub fn array_new(stack: &mut Stack) -> OError {
|
|||
|
||||
pub fn array_len(stack: &mut Stack) -> OError {
|
||||
let binding = stack.pop();
|
||||
let len = match binding.lock_ro().native {
|
||||
Value::Array(ref a) => a.len(),
|
||||
Value::ByteArray(ref a) => a.len(),
|
||||
_ => return stack.err(ErrorKind::InvalidCall("array-len".to_owned())),
|
||||
let Value::Array(ref a) = binding.lock_ro().native else {
|
||||
return stack.err(ErrorKind::InvalidCall("array-len".to_owned()));
|
||||
};
|
||||
stack.push(Value::Mega(len as i128).spl());
|
||||
stack.push(Value::Mega(a.len() as i128).spl());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn array_get(stack: &mut Stack) -> OError {
|
||||
let binding = stack.pop();
|
||||
let Value::Mega(i) = stack.pop().lock_ro().native else {
|
||||
let Value::Array(ref a) = binding.lock_ro().native else {
|
||||
return stack.err(ErrorKind::InvalidCall("array-get".to_owned()));
|
||||
};
|
||||
let o = match binding.lock_ro().native {
|
||||
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())),
|
||||
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
|
||||
return stack.err(ErrorKind::InvalidCall("array-get".to_owned()));
|
||||
};
|
||||
stack.push(o.ok_or_else(array!(stack, i))?);
|
||||
stack.push(a.get(i as usize).ok_or_else(array!(stack, i))?.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn array_set(stack: &mut Stack) -> OError {
|
||||
let binding = &stack.pop();
|
||||
let binding = &mut binding.lock().native;
|
||||
require_on_stack!(i, Mega, stack, "array-set");
|
||||
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());
|
||||
*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 {
|
||||
let binding = stack.pop();
|
||||
let Value::Array(ref mut a) = binding.lock().native else {
|
||||
return stack.err(ErrorKind::InvalidCall("array-set".to_owned()));
|
||||
};
|
||||
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
|
||||
return stack.err(ErrorKind::InvalidCall("array-set".to_owned()));
|
||||
};
|
||||
let o = stack.pop();
|
||||
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;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -239,9 +187,16 @@ pub fn and(stack: &mut Stack) -> OError {
|
|||
}
|
||||
|
||||
pub fn or(stack: &mut Stack) -> OError {
|
||||
let b = stack.pop();
|
||||
let a = stack.pop();
|
||||
stack.push(if a.lock_ro().is_truthy() { a } else { b });
|
||||
let b = stack.pop();
|
||||
stack.push(
|
||||
Value::Int(if a.lock_ro().is_truthy() || b.lock_ro().is_truthy() {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
})
|
||||
.spl(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -361,7 +316,6 @@ pub fn to_int(stack: &mut Stack) -> OError {
|
|||
Value::Str(x) => x
|
||||
.parse()
|
||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "int".to_owned())))?,
|
||||
Value::ByteArray(x) => x.len() as i32,
|
||||
})
|
||||
.spl(),
|
||||
);
|
||||
|
@ -383,7 +337,6 @@ pub fn to_long(stack: &mut Stack) -> OError {
|
|||
Value::Str(x) => x
|
||||
.parse()
|
||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "long".to_owned())))?,
|
||||
Value::ByteArray(x) => x.len() as i64,
|
||||
})
|
||||
.spl(),
|
||||
);
|
||||
|
@ -405,7 +358,6 @@ pub fn to_mega(stack: &mut Stack) -> OError {
|
|||
Value::Str(x) => x
|
||||
.parse()
|
||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "mega".to_owned())))?,
|
||||
Value::ByteArray(x) => x.len() as i128,
|
||||
})
|
||||
.spl(),
|
||||
);
|
||||
|
@ -427,7 +379,6 @@ pub fn to_float(stack: &mut Stack) -> OError {
|
|||
Value::Str(x) => x
|
||||
.parse()
|
||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "float".to_owned())))?,
|
||||
Value::ByteArray(_) => type_err!(stack, "bytearray", "float"),
|
||||
})
|
||||
.spl(),
|
||||
);
|
||||
|
@ -449,7 +400,6 @@ pub fn to_double(stack: &mut Stack) -> OError {
|
|||
Value::Str(x) => x
|
||||
.parse()
|
||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "double".to_owned())))?,
|
||||
Value::ByteArray(_) => type_err!(stack, "bytearray", "double"),
|
||||
})
|
||||
.spl(),
|
||||
);
|
||||
|
@ -472,11 +422,6 @@ pub fn to_array(stack: &mut Stack) -> OError {
|
|||
.chars()
|
||||
.map(|x| Value::Int(x as u32 as i32).spl())
|
||||
.collect(),
|
||||
Value::ByteArray(x) => x
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|x| Value::Int(x as i32).spl())
|
||||
.collect(),
|
||||
})
|
||||
.spl(),
|
||||
);
|
||||
|
@ -517,46 +462,6 @@ pub fn to_str(stack: &mut Stack) -> OError {
|
|||
fixed
|
||||
}
|
||||
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(),
|
||||
);
|
||||
|
@ -696,11 +601,23 @@ pub fn alit_end(stack: &mut Stack) -> OError {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: rewrite
|
||||
pub fn import(stack: &mut Stack) -> OError {
|
||||
let Value::Str(mut s) = stack.pop().lock_ro().native.clone() else {
|
||||
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('#') {
|
||||
s = find_in_splpath(x).unwrap_or(x.to_owned());
|
||||
} else if let Some(x) = s.strip_prefix('@') {
|
||||
|
@ -716,29 +633,7 @@ pub fn import(stack: &mut Stack) -> OError {
|
|||
.to_owned()
|
||||
+ "/"
|
||||
+ &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(
|
||||
&(*fs::canonicalize(s.clone())
|
||||
.unwrap_or_else(|_| s.clone().into())
|
||||
|
@ -851,18 +746,29 @@ pub fn command_wait(stack: &mut Stack) -> OError {
|
|||
|
||||
pub fn str_to_bytes(stack: &mut Stack) -> OError {
|
||||
require_on_stack!(s, Str, stack, "str-to-bytes");
|
||||
stack.push(Value::ByteArray(s.bytes().collect()).spl());
|
||||
stack.push(
|
||||
Value::Array(
|
||||
s.bytes()
|
||||
.into_iter()
|
||||
.map(|x| Value::Int(x as i32).spl())
|
||||
.collect(),
|
||||
)
|
||||
.spl(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn bytes_to_str(stack: &mut Stack) -> OError {
|
||||
if stack.peek().lock_ro().kind.lock_ro().get_name() == "bytearray" {
|
||||
require_on_stack!(a, ByteArray, stack, "bytes-to-str");
|
||||
stack.push(Value::Str(String::from_utf8_lossy(&a[..]).into_owned()).spl());
|
||||
return Ok(());
|
||||
require_array_on_stack!(a, stack, "str-to-bytes");
|
||||
let mut chars = Vec::new();
|
||||
for item in a.iter() {
|
||||
if let Value::Int(x) = item.lock_ro().native.clone().try_mega_to_int() {
|
||||
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(&a).into_owned()).spl());
|
||||
stack.push(Value::Str(String::from_utf8_lossy(&chars[..]).into_owned()).spl());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -871,8 +777,7 @@ pub fn acopy(stack: &mut Stack) -> OError {
|
|||
require_on_stack!(idx_dest, Mega, stack, "acopy");
|
||||
require_on_stack!(idx_src, Mega, stack, "acopy");
|
||||
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_array_on_stack!(src, stack, "acopy");
|
||||
let offset = idx_dest - idx_src;
|
||||
|
@ -887,14 +792,6 @@ pub fn acopy(stack: &mut Stack) -> OError {
|
|||
*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);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -943,212 +840,17 @@ pub fn write_file_sasm(stack: &mut Stack) -> OError {
|
|||
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>) {
|
||||
type Fn = fn(&mut Stack) -> OError;
|
||||
let fns: [(&str, Fn, u32); 69] = [
|
||||
let fns: [(&str, Fn, u32); 53] = [
|
||||
("pop", pop, 0),
|
||||
("dup", dup, 2),
|
||||
("dup2", dup2, 3),
|
||||
("clone", clone, 1),
|
||||
("swap", swap, 2),
|
||||
("mswap", mswap, 2),
|
||||
("print", print, 0),
|
||||
("gettype", gettype, 1),
|
||||
("gettypeid", gettypeid, 1),
|
||||
("settype", settype, 1),
|
||||
("settypeid", settypeid, 1),
|
||||
("banew", barray_new, 1),
|
||||
("anew", array_new, 1),
|
||||
("array-len", array_len, 1),
|
||||
("array-get", array_get, 1),
|
||||
|
@ -1171,7 +873,6 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
|||
("_double", to_double, 1),
|
||||
("_array", to_array, 1),
|
||||
("_str", to_str, 1),
|
||||
("_barray", to_bytearray, 1),
|
||||
("call", call, 0),
|
||||
("callp", callp, 0),
|
||||
("trace", trace, 1),
|
||||
|
@ -1195,17 +896,6 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
|||
("throw", throw, 0),
|
||||
("write-sasm", write_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 {
|
||||
r.define_func(
|
||||
|
|
|
@ -1,23 +1,15 @@
|
|||
use crate::Runtime;
|
||||
use multicall::multicall;
|
||||
|
||||
pub const STD: &str = include_str!("../spl/std.spl");
|
||||
pub const NET: &str = include_str!("../spl/net.spl");
|
||||
pub const ITER: &str = include_str!("../spl/iter.spl");
|
||||
pub const HTTP: &str = include_str!("../spl/http.spl");
|
||||
pub const STREAM: &str = include_str!("../spl/stream.spl");
|
||||
pub const MESSAGING: &str = include_str!("../spl/messaging.spl");
|
||||
pub const ASSEMBLE: &str = include_str!("../spl/assemble.spl");
|
||||
pub const ISBPL: &str = include_str!("../spl/isbpl.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 const STD: &str = include_str!("../std.spl");
|
||||
pub const NET: &str = include_str!("../net.spl");
|
||||
pub const ITER: &str = include_str!("../iter.spl");
|
||||
pub const HTTP: &str = include_str!("../http.spl");
|
||||
pub const STREAM: &str = include_str!("../stream.spl");
|
||||
pub const MESSAGING: &str = include_str!("../messaging.spl");
|
||||
pub const ASSEMBLE: &str = include_str!("../assemble.spl");
|
||||
pub const ISBPL: &str = include_str!("../isbpl.spl");
|
||||
pub const REPL: &str = include_str!("../repl.spl");
|
||||
|
||||
pub fn register(runtime: &mut Runtime) {
|
||||
multicall! {
|
||||
|
@ -31,13 +23,5 @@ pub fn register(runtime: &mut Runtime) {
|
|||
insert("assemble.spl", ASSEMBLE);
|
||||
insert("isbpl.spl", ISBPL);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
hint::black_box,
|
||||
fs::OpenOptions,
|
||||
io::{Read, Write},
|
||||
net::{TcpStream, UdpSocket},
|
||||
mem,
|
||||
net::{Shutdown, TcpStream, UdpSocket},
|
||||
process::{self, Stdio},
|
||||
sync::{Arc, LazyLock},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::*;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use fs::OpenOptions;
|
||||
use mutex::Mut;
|
||||
use stream::StreamExtraData;
|
||||
use crate::{mutex::Mut, runtime::*, *};
|
||||
|
||||
static STREAM_TYPES: LazyLock<Arc<Mut<HashMap<String, StreamType>>>> =
|
||||
LazyLock::new(|| Arc::new(Mut::new(HashMap::new())));
|
||||
static STREAM_TYPES: Lazy<Arc<Mut<HashMap<String, StreamType>>>> =
|
||||
Lazy::new(|| Arc::new(Mut::new(HashMap::new())));
|
||||
static IS_INITIALIZED: Lazy<Arc<Mut<bool>>> = Lazy::new(|| Arc::new(Mut::new(false)));
|
||||
|
||||
/// Registers a custom stream type.
|
||||
pub fn register_stream_type(
|
||||
|
@ -45,57 +45,34 @@ impl StreamType {
|
|||
|
||||
/// An SPL stream, holding a reader and a writer, and a function to close it.
|
||||
pub struct Stream {
|
||||
pub(super) reader: Box<dyn Read + Send + Sync + 'static>,
|
||||
pub(super) _writer_storage: Option<Box<dyn Write + Send + Sync + 'static>>,
|
||||
pub(super) writer: &'static mut (dyn Write + Send + Sync + 'static),
|
||||
pub extra: StreamExtraData,
|
||||
reader: Box<dyn Read + 'static>,
|
||||
writer: Box<dyn Write + 'static>,
|
||||
close: fn(&mut Self),
|
||||
}
|
||||
|
||||
impl Stream {
|
||||
pub fn new<T: Read + Write + Send + Sync + 'static>(main: T) -> Self {
|
||||
pub fn new<T: Read + Write + 'static>(main: T, close: fn(&mut Self)) -> Self {
|
||||
let mut rw = Box::new(main);
|
||||
Self {
|
||||
writer: unsafe {
|
||||
(rw.as_mut() as *mut (dyn Write + Send + Sync + 'static))
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
},
|
||||
_writer_storage: None,
|
||||
reader: rw,
|
||||
extra: StreamExtraData::default(),
|
||||
// SAFETY: Because these are both in private fields on one object, they can not be
|
||||
// written to simultaneously or read from while writing due to the guards put in place
|
||||
// by the borrow checker on the Stream.
|
||||
reader: Box::new(unsafe { mem::transmute::<&mut _, &mut T>(rw.as_mut()) }),
|
||||
writer: rw,
|
||||
close,
|
||||
}
|
||||
}
|
||||
pub fn new_split(
|
||||
reader: impl Read + Send + Sync + 'static,
|
||||
writer: impl Write + Send + Sync + 'static,
|
||||
reader: impl Read + 'static,
|
||||
writer: impl Write + 'static,
|
||||
close: fn(&mut Self),
|
||||
) -> Self {
|
||||
let mut bx = Box::new(writer);
|
||||
Self {
|
||||
reader: Box::new(reader),
|
||||
writer: unsafe {
|
||||
(bx.as_mut() as *mut (dyn Write + Send + Sync + 'static))
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
},
|
||||
_writer_storage: Some(bx),
|
||||
extra: StreamExtraData::default(),
|
||||
writer: Box::new(writer),
|
||||
close,
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -142,30 +119,19 @@ 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
|
||||
where
|
||||
T: Fn(&mut Stack) -> Result<Stream, Error> + Sync + Send + 'static,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
Self {
|
||||
StreamType {
|
||||
func: Arc::new(Box::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_stream(stack: &mut Stack) -> OError {
|
||||
require_on_stack!(s, Str, stack, "new-stream");
|
||||
require_on_stack!(s, Str, stack, "write-stream");
|
||||
let stream = get_stream_type(s.clone())
|
||||
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-type-{s}"))))?
|
||||
.make_stream(stack)?;
|
||||
|
@ -176,36 +142,48 @@ pub fn new_stream(stack: &mut Stack) -> OError {
|
|||
|
||||
pub fn write_stream(stack: &mut Stack) -> OError {
|
||||
require_on_stack!(id, Mega, stack, "write-stream");
|
||||
require_byte_array_on_stack!(a, stack, "write-stream");
|
||||
require_array_on_stack!(a, stack, "write-stream");
|
||||
let stream = runtime(|rt| {
|
||||
rt.get_stream(id as u128)
|
||||
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
||||
})?;
|
||||
let mut fixed = Vec::with_capacity(a.len());
|
||||
for item in a.iter() {
|
||||
match item.lock_ro().native {
|
||||
Value::Int(x) => fixed.push(x as u8),
|
||||
_ => type_err!(stack, "!int", "int"),
|
||||
}
|
||||
}
|
||||
stack.push(
|
||||
Value::Mega(
|
||||
stream
|
||||
.lock()
|
||||
.write(&a)
|
||||
.write(&fixed[..])
|
||||
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))? as i128,
|
||||
)
|
||||
.spl(),
|
||||
);
|
||||
black_box(&stream.lock_ro()._writer_storage);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_all_stream(stack: &mut Stack) -> OError {
|
||||
require_on_stack!(id, Mega, stack, "write-all-stream");
|
||||
require_byte_array_on_stack!(a, stack, "write-all-stream");
|
||||
require_array_on_stack!(a, stack, "write-all-stream");
|
||||
let stream = runtime(|rt| {
|
||||
rt.get_stream(id as u128)
|
||||
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
||||
})?;
|
||||
let mut fixed = Vec::with_capacity(a.len());
|
||||
for item in a.iter() {
|
||||
match item.lock_ro().native {
|
||||
Value::Int(x) => fixed.push(x as u8),
|
||||
_ => type_err!(stack, "!int", "int"),
|
||||
}
|
||||
}
|
||||
stream
|
||||
.lock()
|
||||
.write_all(&a)
|
||||
.write_all(&fixed[..])
|
||||
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?;
|
||||
black_box(&stream.lock_ro()._writer_storage);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -219,20 +197,18 @@ pub fn flush_stream(stack: &mut Stack) -> OError {
|
|||
.lock()
|
||||
.flush()
|
||||
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?;
|
||||
black_box(&stream.lock_ro()._writer_storage);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_stream(stack: &mut Stack) -> OError {
|
||||
require_on_stack!(id, Mega, stack, "read-stream");
|
||||
let array = stack.pop();
|
||||
let kind = array.lock_ro().kind.lock_ro().get_name();
|
||||
let stream = runtime(|rt| {
|
||||
rt.get_stream(id as u128)
|
||||
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
||||
})?;
|
||||
if kind == "array" {
|
||||
{
|
||||
require_mut_array!(a, array, stack, "read-stream");
|
||||
let stream = runtime(|rt| {
|
||||
rt.get_stream(id as u128)
|
||||
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
||||
})?;
|
||||
let mut vec = vec![0; a.len()];
|
||||
stack.push(
|
||||
Value::Mega(
|
||||
|
@ -250,19 +226,6 @@ pub fn read_stream(stack: &mut Stack) -> OError {
|
|||
.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);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -270,13 +233,12 @@ pub fn read_stream(stack: &mut Stack) -> OError {
|
|||
pub fn read_all_stream(stack: &mut Stack) -> OError {
|
||||
require_on_stack!(id, Mega, stack, "read-all-stream");
|
||||
let array = stack.pop();
|
||||
let kind = array.lock_ro().kind.lock_ro().get_name();
|
||||
let stream = runtime(|rt| {
|
||||
rt.get_stream(id as u128)
|
||||
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
||||
})?;
|
||||
if kind == "array" {
|
||||
{
|
||||
require_mut_array!(a, array, stack, "read-all-stream");
|
||||
let stream = runtime(|rt| {
|
||||
rt.get_stream(id as u128)
|
||||
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
||||
})?;
|
||||
let mut vec = vec![0; a.len()];
|
||||
stream
|
||||
.lock()
|
||||
|
@ -288,34 +250,22 @@ pub fn read_all_stream(stack: &mut Stack) -> OError {
|
|||
.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);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn close_stream(stack: &mut Stack) -> OError {
|
||||
require_on_stack!(id, Mega, stack, "close-stream");
|
||||
runtime_mut(|mut rt| rt.destroy_stream(id as u128));
|
||||
if let Some(stream) = runtime(|rt| rt.get_stream(id as u128)) {
|
||||
let mut stream = stream.lock();
|
||||
(stream.close)(&mut stream);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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 nop(_stream: &mut Stream) {}
|
||||
|
||||
pub(super) fn stream_file(stack: &mut Stack) -> Result<Stream, Error> {
|
||||
fn stream_file(stack: &mut Stack) -> Result<Stream, Error> {
|
||||
let truncate = stack.pop().lock_ro().is_truthy();
|
||||
require_on_stack!(path, Str, stack, "FILE new-stream");
|
||||
Ok(Stream::new(
|
||||
|
@ -326,23 +276,34 @@ pub(super) fn stream_file(stack: &mut Stack) -> Result<Stream, Error> {
|
|||
.truncate(truncate)
|
||||
.open(path)
|
||||
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?,
|
||||
nop,
|
||||
))
|
||||
}
|
||||
|
||||
pub(super) fn stream_tcp(stack: &mut Stack) -> Result<Stream, Error> {
|
||||
fn stream_tcp(stack: &mut Stack) -> Result<Stream, Error> {
|
||||
require_int_on_stack!(port, stack, "TCP new-stream");
|
||||
require_on_stack!(ip, Str, stack, "TCP new-stream");
|
||||
fn close_tcp(stream: &mut Stream) {
|
||||
unsafe {
|
||||
let f = ((stream.reader.as_mut() as *mut dyn Read).cast() as *mut TcpStream)
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
let _ = f.shutdown(Shutdown::Both);
|
||||
}
|
||||
}
|
||||
Ok(Stream::new(
|
||||
TcpStream::connect((ip, port as u16))
|
||||
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?,
|
||||
close_tcp,
|
||||
))
|
||||
}
|
||||
|
||||
pub(super) fn stream_udp(stack: &mut Stack) -> Result<Stream, Error> {
|
||||
fn stream_udp(stack: &mut Stack) -> Result<Stream, Error> {
|
||||
require_int_on_stack!(port, stack, "UDP new-stream");
|
||||
require_on_stack!(ip, Str, stack, "UDP new-stream");
|
||||
require_int_on_stack!(self_port, stack, "UDP new-stream");
|
||||
require_on_stack!(self_ip, Str, stack, "UDP new-stream");
|
||||
fn close_udp(_stream: &mut Stream) {}
|
||||
let sock = UdpSocket::bind((self_ip, self_port as u16))
|
||||
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?;
|
||||
sock.connect((ip, port as u16))
|
||||
|
@ -362,11 +323,12 @@ pub(super) fn stream_udp(stack: &mut Stack) -> Result<Stream, Error> {
|
|||
self.0.recv(buf)
|
||||
}
|
||||
}
|
||||
Ok(Stream::new(UdpRW(sock)))
|
||||
Ok(Stream::new(UdpRW(sock), close_udp))
|
||||
}
|
||||
|
||||
pub(super) fn stream_cmd(stack: &mut Stack) -> Result<Stream, Error> {
|
||||
fn stream_cmd(stack: &mut Stack) -> Result<Stream, Error> {
|
||||
require_on_stack!(a, Array, stack, "CMD new-stream");
|
||||
fn close_cmd(_stream: &mut Stream) {}
|
||||
let mut args = Vec::new();
|
||||
for item in a.iter() {
|
||||
if let Value::Str(ref s) = item.lock_ro().native {
|
||||
|
@ -374,7 +336,7 @@ pub(super) fn stream_cmd(stack: &mut Stack) -> Result<Stream, Error> {
|
|||
}
|
||||
}
|
||||
if args.is_empty() {
|
||||
return stack.err(ErrorKind::InvalidCall("CMD new-stream".to_owned()));
|
||||
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
|
||||
}
|
||||
let mut command = process::Command::new(&args[0])
|
||||
.args(&args[1..])
|
||||
|
@ -386,26 +348,40 @@ pub(super) fn stream_cmd(stack: &mut Stack) -> Result<Stream, Error> {
|
|||
Ok(Stream::new_split(
|
||||
command.stdout.take().unwrap(),
|
||||
command.stdin.take().unwrap(),
|
||||
close_cmd,
|
||||
))
|
||||
}
|
||||
|
||||
pub(super) fn get_stream_peer(stack: &mut Stack) -> OError {
|
||||
require_on_stack!(id, Mega, stack, "get-stream-peer");
|
||||
let Some((addr, port)) = runtime(|rt| -> Result<_, Error> {
|
||||
Ok(rt
|
||||
.get_stream(id as u128)
|
||||
.ok_or(stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))?
|
||||
.lock_ro()
|
||||
.extra
|
||||
.peer
|
||||
.clone())
|
||||
})?
|
||||
else {
|
||||
stack.push(Value::Null.spl());
|
||||
stack.push(Value::Null.spl());
|
||||
return Ok(());
|
||||
};
|
||||
stack.push(addr.spl());
|
||||
stack.push((port as i32).spl());
|
||||
Ok(())
|
||||
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);
|
||||
*IS_INITIALIZED.lock() = true;
|
||||
}
|
||||
|
||||
type Fn = fn(&mut Stack) -> OError;
|
||||
let fns: [(&str, Fn, u32); 7] = [
|
||||
("new-stream", new_stream, 1),
|
||||
("write-stream", write_stream, 1),
|
||||
("write-all-stream", write_all_stream, 0),
|
||||
("flush-stream", flush_stream, 0),
|
||||
("read-stream", read_stream, 1),
|
||||
("read-all-stream", read_all_stream, 0),
|
||||
("close-stream", close_stream, 0),
|
||||
];
|
||||
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,55 +0,0 @@
|
|||
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,106 +0,0 @@
|
|||
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)
|
||||
}
|
|
@ -8,11 +8,11 @@ def program-name
|
|||
|
||||
def print'pre-std &print =print'pre-std
|
||||
func print { |
|
||||
_str !!- print'pre-std inline-callable
|
||||
_str print'pre-std call
|
||||
}
|
||||
|
||||
func println { |
|
||||
_str !!- "\n" concat print
|
||||
print "\n" print
|
||||
}
|
||||
|
||||
construct error {
|
||||
|
@ -30,60 +30,58 @@ construct FrameInfo {
|
|||
|
||||
construct _str_ext {
|
||||
;
|
||||
new { any | :mkinstance:construct }
|
||||
mkinstance { any | with this ;
|
||||
null clone this settype
|
||||
new { any | with this ;
|
||||
null clone this settype:construct
|
||||
}
|
||||
to-bytes { [int] | str-to-bytes }
|
||||
split { str | with splitter this ;
|
||||
splitter:to-bytes this:to-bytes:split:map<{ | bytes-to-str }>
|
||||
def bytes splitter:to-bytes =bytes
|
||||
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 ;
|
||||
replacee:to-bytes replacement:to-bytes this:to-bytes:replace:to-str
|
||||
}
|
||||
find { idx | with search this ;
|
||||
search _barray this _barray :find
|
||||
}
|
||||
contains { bool | with search this ;
|
||||
search this:find null eq not
|
||||
def bytes replacee:to-bytes =bytes
|
||||
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
|
||||
match bytes:len eq if {
|
||||
replacement _array :to-stack
|
||||
}
|
||||
} ] _str
|
||||
}
|
||||
starts-with { bool | with beginning this ;
|
||||
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 ;
|
||||
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
|
||||
|
||||
construct _mega-ext {
|
||||
|
@ -95,30 +93,12 @@ construct _mega-ext {
|
|||
mswap { .. | mswap }
|
||||
foreach { | with callable this ;
|
||||
def i 0 =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
|
||||
while { i this lt } { i callable call i ++ =i }
|
||||
}
|
||||
} include _mega-ext in mega
|
||||
|
||||
construct _array-ext {
|
||||
;
|
||||
another { array | with this ;
|
||||
this gettype =this
|
||||
this "array" eq if {
|
||||
anew
|
||||
}
|
||||
this "bytearray" eq if {
|
||||
banew
|
||||
}
|
||||
}
|
||||
get { any | array-get }
|
||||
sget { any|null | with idx this ;
|
||||
idx this:len lt idx -1 gt and dup if {
|
||||
|
@ -139,84 +119,13 @@ construct _array-ext {
|
|||
i ++ =i
|
||||
}
|
||||
}
|
||||
foreach { | with callable this ;
|
||||
foreach { | with callable this ;
|
||||
def i 0 =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 }
|
||||
sub { [any] | with begin end this ;
|
||||
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
|
||||
this (end begin - anew) begin 0 (end begin -) acopy
|
||||
}
|
||||
starts-with { bool | with beginning this ;
|
||||
this:len beginning:len lt if {
|
||||
|
@ -225,18 +134,11 @@ construct _array-ext {
|
|||
}
|
||||
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 ;
|
||||
this:len -- this:get
|
||||
}
|
||||
=last { | with this ;
|
||||
this:len -- this:set;
|
||||
this:len -- this:set
|
||||
}
|
||||
0 { any | with this ;
|
||||
0 this:get
|
||||
|
@ -268,9 +170,7 @@ construct _array-ext {
|
|||
=4 { | with this ;
|
||||
4 this:set;
|
||||
}
|
||||
}
|
||||
include _array-ext in array
|
||||
include _array-ext in bytearray
|
||||
} include _array-ext in array
|
||||
|
||||
construct _func-ext {
|
||||
args
|
||||
|
@ -318,7 +218,6 @@ construct List {
|
|||
to-stack { .. | :array:to-stack }
|
||||
to-str { str | :array:to-str }
|
||||
sub { [any] | :array:sub }
|
||||
clear { | :=array<0 anew> }
|
||||
}
|
||||
construct _GrowingArray {
|
||||
;
|
||||
|
@ -376,7 +275,7 @@ construct ArrayIter {
|
|||
construct _IterableArray {
|
||||
;
|
||||
iter { ArrayIter | with this ;
|
||||
this gettype:ends-with<"array"> dup if {
|
||||
this gettype "array" eq dup if {
|
||||
pop
|
||||
this ArrayIter:new
|
||||
2 stop
|
||||
|
@ -388,7 +287,6 @@ construct _IterableArray {
|
|||
include _Iter in ArrayIter
|
||||
include _IterableArray in List
|
||||
include _IterableArray in array
|
||||
include _IterableArray in bytearray
|
||||
|
||||
construct MicroMap {
|
||||
pairs
|
||||
|
@ -433,20 +331,6 @@ construct MicroMap {
|
|||
foreach { | with callable this ;
|
||||
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 {
|
||||
|
@ -481,47 +365,26 @@ construct Range {
|
|||
}
|
||||
|
||||
construct RangeIter {
|
||||
upper step
|
||||
range
|
||||
idx
|
||||
;
|
||||
construct { this | with range this ;
|
||||
range:lower this:=idx
|
||||
range:upper this:=upper
|
||||
range:step this:=step
|
||||
range this:=range
|
||||
0 this:=idx
|
||||
this
|
||||
}
|
||||
next { mega | with this ;
|
||||
!!-
|
||||
this:idx dup this:step + this:=idx
|
||||
dup this:upper lt not if {
|
||||
pop null
|
||||
}
|
||||
this:idx dup ++ this:=idx this:range:item
|
||||
}
|
||||
}
|
||||
|
||||
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 { }
|
||||
|
||||
func aadd { array | with arr1 arr2 ;
|
||||
|
||||
def newarr arr1:len arr2:len + arr1:another =newarr
|
||||
def newarr arr1:len arr2:len + anew =newarr
|
||||
|
||||
arr1 newarr 0 0 arr1:len acopy;
|
||||
arr2 newarr 0 arr1:len arr2:len acopy;
|
||||
|
@ -535,9 +398,9 @@ func concat { str | with a b ;
|
|||
|
||||
func nconcat { str | with amt ;
|
||||
_array
|
||||
(amt 1 -):foreach<{ | pop
|
||||
(amt 1 -):foreach <{ { | pop
|
||||
swap _array swap aadd
|
||||
}>
|
||||
} }
|
||||
_str
|
||||
}
|
||||
|
||||
|
@ -612,10 +475,6 @@ func assert-eq { any any | with a b ;
|
|||
a b
|
||||
}
|
||||
|
||||
func awrap { array | with item ;
|
||||
item 1 anew =item 0 item:set; item
|
||||
}
|
||||
|
||||
func [ { shadow |
|
||||
"array" "shadow" settype
|
||||
}
|
||||
|
@ -681,21 +540,8 @@ func update-types { |
|
|||
update-types
|
||||
|
||||
"Adds a field to a namespace and initially sets it to the field's name.";
|
||||
func register-field { | with field-name namespace-name ;
|
||||
def namespace-path
|
||||
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>
|
||||
func register-field { | with field-name namespace-name namespace ;
|
||||
field-name namespace-name dyn-def-field;
|
||||
namespace namespace-name settype ("=" namespace-name concat) dyn-call
|
||||
}
|
||||
|
|
@ -15,26 +15,28 @@ construct Stream {
|
|||
this
|
||||
}
|
||||
read-one { mega | with this ;
|
||||
def buf 1 banew =buf
|
||||
while { buf this:id read-stream pop not } { }
|
||||
def buf 1 anew =buf
|
||||
while { buf this:id read-stream not } { }
|
||||
0 buf:get _mega
|
||||
}
|
||||
"the buffer is written to in-place.";
|
||||
read { mega [int] | with buf this ;
|
||||
buf gettype "mega" eq if { buf banew =buf }
|
||||
buf this:id read-stream
|
||||
buf gettype "mega" eq if { buf anew =buf }
|
||||
buf this:id read-stream buf
|
||||
}
|
||||
"the buffer is written to in-place.";
|
||||
read-exact { [int] | with buf this ;
|
||||
buf gettype "mega" eq if { buf banew =buf }
|
||||
buf gettype "mega" eq if { buf anew =buf }
|
||||
buf this:id read-all-stream buf
|
||||
}
|
||||
read-to-end { [int] | with buf this ;
|
||||
buf gettype "mega" eq if { buf banew =buf }
|
||||
def full 0 anew =full
|
||||
buf gettype "mega" eq if { buf anew =buf }
|
||||
def read
|
||||
0 banew while { buf this:id read-stream pop _mega dup =read } {
|
||||
(0 read buf:sub) aadd
|
||||
while { buf this:id read-stream pop _mega dup =read } {
|
||||
full (0 read buf:sub) aadd =full
|
||||
}
|
||||
full
|
||||
}
|
||||
write { mega | with buf this ;
|
||||
buf this:id write-stream
|
||||
|
@ -45,29 +47,9 @@ construct Stream {
|
|||
flush { | with this ;
|
||||
this:id flush-stream
|
||||
}
|
||||
shutdown-input { | with this ;
|
||||
this:id shutdown-input-stream
|
||||
}
|
||||
close { | with this ;
|
||||
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 {
|
||||
|
@ -84,7 +66,7 @@ construct StreamType {
|
|||
|
||||
def stream-types 0 anew =stream-types
|
||||
|
||||
construct _StreamTypes {
|
||||
construct _StreamType {
|
||||
;
|
||||
construct { this | with this ;
|
||||
{ | with type ;
|
||||
|
@ -97,7 +79,7 @@ construct _StreamTypes {
|
|||
|
||||
func register-stream-type { | with id ;
|
||||
stream-types [ id ] aadd =stream-types
|
||||
id _StreamTypes dyn-def-field
|
||||
id _StreamType dyn-def-field
|
||||
}
|
||||
|
||||
"tcp" register-stream-type
|
||||
|
@ -105,6 +87,6 @@ func register-stream-type { | with id ;
|
|||
"file" register-stream-type
|
||||
"cmd" register-stream-type
|
||||
|
||||
func StreamTypes { _StreamTypes |
|
||||
_StreamTypes:new
|
||||
func StreamTypes { _StreamType |
|
||||
_StreamType:new
|
||||
}
|
171
test.spl
171
test.spl
|
@ -1,17 +1,11 @@
|
|||
[
|
||||
"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
|
||||
|
||||
func main { int | with args ;
|
||||
|
||||
def thing
|
||||
|
||||
1 anew =thing
|
||||
|
@ -114,18 +108,18 @@ func main { int | with args ;
|
|||
|
||||
def file "test.txt" 1 StreamTypes:file:create =file
|
||||
"hi\n" :to-bytes file:write-exact;
|
||||
file:close; null =file
|
||||
file:close null =file
|
||||
|
||||
"" println
|
||||
"testing split" println
|
||||
{ | println } (" " "hello how are you" :split):foreach
|
||||
"" println
|
||||
|
||||
use net:http:Request
|
||||
catch {
|
||||
"testing http" println;
|
||||
use net:http:Request
|
||||
"testing http" println
|
||||
def req "data.tudbut.de" 80 "GET" "/spltest" Request:new =req
|
||||
req:send:body _str println;
|
||||
req:send:body _str println
|
||||
} { with e ;
|
||||
e:message println
|
||||
"it seems the internet is not available" println
|
||||
|
@ -148,158 +142,19 @@ func main { int | with args ;
|
|||
|
||||
"testing messages" println
|
||||
def bus messaging:Bus:new =bus
|
||||
bus:subscribe<"testmsg1" { | with message ; message:name print " called1 1" println }>
|
||||
bus:subscribe<"testmsg1" { | with message ; message:name print " called1 2" println }>
|
||||
bus:subscribe<"testmsg2" { | with message ; message:name print " called2 1" println }>
|
||||
bus:subscribe<"testmsg2" { | with message ; message:name print " called2 2" println }>
|
||||
bus:subscribe <{ "testmsg1" { | with message ; message:name print " called1 1" println } }
|
||||
bus:subscribe <{ "testmsg1" { | with message ; message:name print " called1 2" println } }
|
||||
bus:subscribe <{ "testmsg2" { | with message ; message:name print " called2 1" println } }
|
||||
bus:subscribe <{ "testmsg2" { | with message ; message:name print " called2 2" println } }
|
||||
"testmsg1" bus:publish
|
||||
"testmsg2" bus:publish
|
||||
"testmsg1" 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
|
||||
}
|
||||
|
||||
func cached-test { mega | 1 "cached-test" cache<{ mega | with i ;
|
||||
func cached-test { mega | 1 "cached-test" cache <{ { mega | with i ;
|
||||
i 2 *
|
||||
"calculated " i _str concat println
|
||||
}>}
|
||||
} } }
|
||||
|
|
1
test.txt
Normal file
1
test.txt
Normal file
|
@ -0,0 +1 @@
|
|||
hi
|
Loading…
Reference in a new issue