Compare commits

..

No commits in common. "main" and "v0.3.0" have entirely different histories.
main ... v0.3.0

37 changed files with 375 additions and 2172 deletions

9
Cargo.lock generated
View file

@ -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",
]

View file

@ -1,6 +1,6 @@
[package]
name = "spl"
version = "0.4.0"
version = "0.3.0"
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"

View file

@ -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.

View file

@ -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.

View file

@ -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
}

View file

@ -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 {

View file

@ -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;

View file

@ -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 { }

View file

@ -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

View file

@ -1,4 +1,4 @@
"messaging bus, aka event bus";
"messaging bus, aka event bus"
construct messaging namespace {
Message

View file

@ -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
View 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
}
}
}

View file

@ -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 } }>
}

View file

@ -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
}
}

View file

@ -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

View file

@ -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

View file

@ -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
}

View file

@ -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
}

View file

@ -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;
}
}

View file

@ -1,9 +0,0 @@
construct time namespace {
;
unixms { mega | with this ;
0 sleeptime
}
sleep { mega | with this ; _mega sleeptime }
}

View file

@ -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;

View file

@ -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,

View file

@ -76,11 +76,12 @@ pub fn start_file_in_runtime(path: &str) -> Result<Stack, Error> {
/// Include the standard library in a runtime-stack-pair, where the runtime has been .set().
pub fn add_std(stack: &mut Stack) -> OError {
let f = find_in_splpath("std.spl");
let words = match f {
Ok(f) => lex(f.ends_with(".isbpl"), fs::read_to_string(f).unwrap()),
Err(content) => lex(false, content),
}
.map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?;
let words = f
.map_err(|x| stack.error(ErrorKind::LexError(format!("{x}"))))
.and_then(|f| {
lex(f.ends_with(".isbpl"), fs::read_to_string(f).unwrap())
.map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))
})?;
words.exec(stack)
}
@ -101,24 +102,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 +134,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();

View file

@ -1,5 +1,5 @@
use spl::{
lex, oxidizer::RustAppBuilder, sasm::sasm_write, start_file_in_runtime, Runtime, SetRuntime,
find_in_splpath, lex, oxidizer::RustAppBuilder, start_file_in_runtime, Runtime, SetRuntime,
};
use std::{env::args, fs};
@ -7,15 +7,12 @@ use std::{env::args, fs};
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" {
let arg = &args
.next()
.unwrap_or_else(|| find_in_splpath("repl.spl").expect("no file to be run"));
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...");

View file

@ -171,17 +171,13 @@ impl RustAppBuilder {
if let Err(x) = start_file_in_runtime(
&args()
.nth(1)
.unwrap_or("default_file".to_owned()),
.unwrap_or_else(|| find_in_splpath("default_file").expect("no file to be run")),
) {
println!("{x:?}");
}
Runtime::reset();
}
}
.to_owned()
.replace("default_file", &self.default_file)
.replace("runtime_init", &runtime_init)
+ &code,
}.to_owned().replace("default_file", &self.default_file).replace("runtime_init", &runtime_init) + &code,
)?;
Command::new("cargo")
.arg("build")

View file

@ -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();
}

View file

@ -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 ";

View file

@ -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(

View file

@ -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);
}
}

View file

@ -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(),
}),
);
}
}

View file

@ -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(),
}),
);
}
}

View file

@ -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)
}

View file

@ -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
}
@ -654,8 +513,7 @@ func _ { |
func call-main-on-file { | with file ;
catch {
"@" (0 (file _array):get 0 ("#" _array):get eq) if { pop "" } file concat
import
"@" file concat import
update-types
argv main exit
} { with err ;
@ -681,21 +539,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
}

View file

@ -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
View file

@ -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
View file

@ -0,0 +1 @@
hi