Compare commits
46 commits
Author | SHA1 | Date | |
---|---|---|---|
d43a7ab384 | |||
4357a16523 | |||
8c2b6724da | |||
e55a619862 | |||
f6faf029cd | |||
dd02cc1fc4 | |||
6a4566c223 | |||
78ec4e066f | |||
9e936277c7 | |||
7e674f0ad7 | |||
200a9375ce | |||
0c5dedb44e | |||
13e32ed4f2 | |||
4c09cfe929 | |||
700fb1e266 | |||
901246abac | |||
92858f6baf | |||
fd0aed81fd | |||
c33b25f260 | |||
baa981c224 | |||
c3e2dbc1b8 | |||
215b6f2748 | |||
3445b27fa9 | |||
1b1855fe5a | |||
5c8875eb55 | |||
a8a2616a41 | |||
2bbeaebd7c | |||
35639cb2c8 | |||
c45c538952 | |||
c3fffb1e75 | |||
290630adbe | |||
e72baba154 | |||
210eaade3a | |||
51212e139a | |||
95afcf9940 | |||
a8b0a1bb53 | |||
2e126a6652 | |||
fccce8b705 | |||
eb1335ade4 | |||
ea8d3f5a6f | |||
c1475cc153 | |||
456d39399e | |||
4749c142fa | |||
c9463c6938 | |||
a9894d8a03 | |||
1c1f9a4566 |
22 changed files with 1235 additions and 147 deletions
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "spl"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
edition = "2021"
|
||||
description = "Stack Pogramming Language: A simple, concise scripting language."
|
||||
license = "MIT"
|
||||
|
|
42
README.md
42
README.md
|
@ -11,6 +11,9 @@ func main { mega | with args ;
|
|||
&print swap:foreach
|
||||
"" println
|
||||
println<"and with that, we're done">
|
||||
[ ^hello ^pattern-matching ] => [ ^hello &=args ] if {
|
||||
args println "prints pattern-matching";
|
||||
}
|
||||
0
|
||||
}
|
||||
```
|
||||
|
@ -262,3 +265,42 @@ 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.
|
||||
|
||||
## Syntactic sugar
|
||||
|
||||
There's a lot of things that look kinda tedious to do in SPL, syntactically speaking. So over time,
|
||||
I regularly add syntactic sugar:
|
||||
|
||||
- `a => b` -> `a b match`
|
||||
- `a =>? b` -> `a dup b match _'match-else-push` (match that will push the offending value on error)
|
||||
- `a =>! b` -> `a b match _'match-else-error` (match that throws an error when unable to match)
|
||||
- `a<| b c d e>` -> `a<{ | b c d e }>` -> `{ | b c d e } a`
|
||||
- `def a, b, c` -> `def a def b def c`
|
||||
|
||||
## Matching
|
||||
|
||||
The match function `match` and its accompanying sugar take in two values and essentially do a special
|
||||
compare for equality on them. Unlike everything else, which is just compared normally, arrays are
|
||||
iterated through and their items checked individually, recursively. When a callable (`{ ... | ... }`)
|
||||
is found in b, any value in a is accepted for it, and the function is called *if and only if* every
|
||||
other part of the match also succeeds.
|
||||
The function returns 1 on success, otherwise 0.
|
||||
|
||||
Example:
|
||||
```js
|
||||
def a, val
|
||||
[ ^ok "hey matcher" ] =a
|
||||
|
||||
a =>! [ ^ok &=val ]
|
||||
val println
|
||||
|
||||
a =>? [ ^ok &=val ] not if {
|
||||
"error: " swap concat panic
|
||||
}
|
||||
val println
|
||||
|
||||
a => [ ^ok &=val ] not if {
|
||||
"error" panic
|
||||
}
|
||||
val println
|
||||
```
|
||||
|
|
89
spl/fast.spl
Normal file
89
spl/fast.spl
Normal file
|
@ -0,0 +1,89 @@
|
|||
|
||||
"#stream.spl" import
|
||||
"#http.spl" import
|
||||
|
||||
func ls { files | "." list-files }
|
||||
func ls@ { files | list-files }
|
||||
func cd { | chdir }
|
||||
func cat { | read-file }
|
||||
|
||||
func output { | with thing ;
|
||||
thing gettype "array" eq
|
||||
thing gettype:ends-with<^Iter> or
|
||||
dup if {
|
||||
thing:foreach<&println>
|
||||
}
|
||||
not if {
|
||||
thing _str println
|
||||
}
|
||||
}
|
||||
|
||||
func *m { | swap :ends-with }
|
||||
func m* { | swap :starts-with }
|
||||
func # { | swap :filter }
|
||||
func ? { | swap :map }
|
||||
func times { | :iter }
|
||||
|
||||
func \ { | pop }
|
||||
|
||||
func ~@ { | env:get<"HOME"> "/" concat swap concat }
|
||||
func ~ { | env:get<"HOME"> }
|
||||
func $ { v | env:get }
|
||||
|
||||
func . { | output }
|
||||
func ex { | with s ; [ ^sh ^-c s ] command-wait; }
|
||||
func ex% { | command-wait; }
|
||||
func ex. { | with s ; [ ^sh ^-c s ] StreamTypes:cmd:create:read-to-end<1024 16 *> }
|
||||
func ex%. { | StreamTypes:cmd:create:read-to-end<1024 16 *> }
|
||||
func into { | with input file ;
|
||||
input gettype any<[ { | "array" eq } { | "bytearray" eq } ]> not if { input _str:to-bytes =input }
|
||||
file 1 StreamTypes:file:create
|
||||
dup :write-exact;<input>
|
||||
:close;
|
||||
}
|
||||
|
||||
func tcp { stream |
|
||||
StreamTypes:tcp:create
|
||||
}
|
||||
|
||||
func curl { s | bcurl:to-str }
|
||||
|
||||
func bcurl { bytes | with url ;
|
||||
1 if {
|
||||
url:readf<"{}:{}/{}"> dup if {
|
||||
=url
|
||||
url:0 url:1 _int "GET" "/" url:2 concat net:http:Request:new
|
||||
2 stop
|
||||
} pop
|
||||
url:readf<"{}:{}"> dup if {
|
||||
=url
|
||||
url:0 url:1 _int "GET" "/" net:http:Request:new
|
||||
2 stop
|
||||
} pop
|
||||
url:readf<"{}/{}"> dup if {
|
||||
=url
|
||||
url:0 80 "GET" "/" url:1 concat net:http:Request:new
|
||||
2 stop
|
||||
} pop
|
||||
url:readf<"{}"> dup if {
|
||||
=url
|
||||
url:0 80 "GET" "/" net:http:Request:new
|
||||
2 stop
|
||||
} pop
|
||||
"invalid url" panic
|
||||
} :send:body
|
||||
}
|
||||
|
||||
construct _shell_array_ext {
|
||||
idx
|
||||
;
|
||||
next { any | with this ;
|
||||
this:idx 0 or dup ++ this:=idx this:sget
|
||||
}
|
||||
}
|
||||
include _shell_array_ext in array
|
||||
include _Iter in array
|
||||
|
||||
func any { bool | with o checks ;
|
||||
0 checks:foreach<{ | o swap call if { pop 1 } }>
|
||||
}
|
|
@ -42,26 +42,34 @@ construct net:http:server:Request {
|
|||
construct { this | with server stream this ;
|
||||
server this:=server
|
||||
stream this:=stream
|
||||
0 anew this:=head
|
||||
0 anew this:=body
|
||||
0 banew this:=head
|
||||
0 banew this:=body
|
||||
this
|
||||
}
|
||||
read-head { this | with this ;
|
||||
def read
|
||||
def buf 1024 anew =buf
|
||||
def buf 1024 banew =buf
|
||||
def found
|
||||
while {
|
||||
buf this:stream:read pop =read
|
||||
"\r\n\r\n" :to-bytes buf:find dup =found not read and
|
||||
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> aadd this:=head
|
||||
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
|
||||
this:head buf:sub<0 found>:replace<"\r" :to-bytes 0 banew> aadd:to-str this:=head
|
||||
buf:sub<found 4 + buf:len> this:=body
|
||||
this
|
||||
}
|
||||
parse-head { this | with this ;
|
||||
this:head:split<"\r\n"> this:=head
|
||||
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
|
||||
|
@ -100,7 +108,7 @@ construct net:http:server:Request {
|
|||
read-body { this | with this ;
|
||||
this:headers:get<"content-length"> dup if { _mega with content-length ;
|
||||
def read
|
||||
def buf 1024 anew =buf
|
||||
def buf 1024 banew =buf
|
||||
while {
|
||||
this:body:len content-length lt read and
|
||||
} {
|
||||
|
@ -146,7 +154,7 @@ construct net:http:server:Request {
|
|||
}
|
||||
finish { | with this ;
|
||||
this:wrote-body not if {
|
||||
0 anew this:write-body;
|
||||
0 banew this:write-body;
|
||||
}
|
||||
this:stream:close;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
"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 ;
|
||||
|
@ -13,18 +19,25 @@ construct net:http:server:_static_ext_server {
|
|||
|
||||
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 read-file this:write-ok:write-content-type<type>:write-str-body:finish;
|
||||
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 read-file dup this:server:cached-files:set;<filepath>
|
||||
pop filepath StreamTypes:file:create<0>:read-to-end<this:get-bufsize> dup this:server:cached-files:set;<filepath>
|
||||
}
|
||||
this:write-ok:write-content-type<type>:write-str-body:finish;
|
||||
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
|
||||
}
|
||||
|
|
|
@ -13,12 +13,12 @@ construct _Iter {
|
|||
foreach { | with callable this ;
|
||||
!!-
|
||||
while { !!- this:next dup null eq not } {
|
||||
!!- callable call
|
||||
!!- callable callp
|
||||
}
|
||||
pop
|
||||
}
|
||||
collect { array | with this ;
|
||||
[ { any | } this:foreach ]
|
||||
[ { | } this:foreach ]
|
||||
}
|
||||
map { MapIter | with map-function this ;
|
||||
map-function this MapIter:new
|
||||
|
|
49
spl/json.spl
Normal file
49
spl/json.spl
Normal file
|
@ -0,0 +1,49 @@
|
|||
|
||||
construct json namespace {
|
||||
_StringyJSON
|
||||
;
|
||||
props-to-sjson { s | with spaces props this ;
|
||||
def value, comma
|
||||
"," spaces if { " " concat } =comma
|
||||
|
||||
props:last:get<1> =value
|
||||
value null eq not if {
|
||||
value gettype "array" eq if {
|
||||
"[" value
|
||||
:iter
|
||||
:map<| 0 swap properties this:props-to-sjson>
|
||||
:join<comma> concat
|
||||
"]" concat
|
||||
3 stop
|
||||
}
|
||||
"\""
|
||||
value _str :replace<"\\" "\\\\">:replace<"\"" "\\\""> concat
|
||||
"\"" concat
|
||||
2 stop
|
||||
}
|
||||
|
||||
"{"
|
||||
props
|
||||
:iter
|
||||
:filter<{ b | :to-stack pop with key ; key ":" eq not key ";" eq not and }>
|
||||
:map<{ s |
|
||||
:to-stack with key value ;
|
||||
"\""
|
||||
key :replace<"\\" "\\\\">:replace<"\"" "\\\""> concat
|
||||
"\":" concat spaces if { " " concat }
|
||||
spaces value properties this:props-to-sjson concat
|
||||
}>
|
||||
:join<comma> concat
|
||||
"}" concat
|
||||
}
|
||||
}
|
||||
|
||||
construct json:_StringyJSON {
|
||||
;
|
||||
sjson { s | with spaces this ;
|
||||
spaces this properties json:props-to-sjson
|
||||
}
|
||||
}
|
||||
|
||||
include json:_StringyJSON in array
|
||||
include json:_StringyJSON in str
|
134
spl/linkedlist.spl
Normal file
134
spl/linkedlist.spl
Normal file
|
@ -0,0 +1,134 @@
|
|||
|
||||
construct LinkedList {
|
||||
next
|
||||
value
|
||||
;
|
||||
construct { this | }
|
||||
len { mega | with this ;
|
||||
def i this:value null eq not =i
|
||||
while { this:next null eq not } {
|
||||
this:next =this
|
||||
i ++ =i
|
||||
}
|
||||
i
|
||||
}
|
||||
push { | with item this ;
|
||||
item:unwrap;
|
||||
this:value null eq if {
|
||||
item this:=value;
|
||||
2 stop
|
||||
}
|
||||
LinkedList:new
|
||||
dup :=value;<item>
|
||||
this:last-entry:=next;
|
||||
}
|
||||
last-entry { list | with this ;
|
||||
while { this:next null eq not } {
|
||||
this:next =this
|
||||
}
|
||||
this
|
||||
}
|
||||
peek { value | with this ;
|
||||
this:last-entry:value
|
||||
}
|
||||
pop { item | with this ;
|
||||
def item this =item
|
||||
null
|
||||
while { item:next null eq not } {
|
||||
pop item
|
||||
item:next =item
|
||||
}
|
||||
"if theres no next item in this";
|
||||
dup null eq if {
|
||||
pop
|
||||
this:value (null this:=value;) 2 stop
|
||||
}
|
||||
:=next;<null>
|
||||
item:value
|
||||
}
|
||||
push-front { | with item this ;
|
||||
item:unwrap;
|
||||
this:value null eq if {
|
||||
item this:=value;
|
||||
2 stop
|
||||
}
|
||||
"append new next identical to this, then make this contain new value";
|
||||
def new LinkedList:new =new
|
||||
this:next new:=next;
|
||||
this:value new:=value;
|
||||
item this:=value;
|
||||
new this:=next;
|
||||
}
|
||||
insert { | with item index this ;
|
||||
item:unwrap;
|
||||
index 0 eq if {
|
||||
item this:push-front 2 stop
|
||||
}
|
||||
def list this =list
|
||||
while { index 1 gt } {
|
||||
list:next =list
|
||||
index -- =index
|
||||
}
|
||||
item list:next:push-front
|
||||
}
|
||||
pop-front { item | with this ;
|
||||
this:value
|
||||
this:next null eq dup if {
|
||||
null this:=value
|
||||
}
|
||||
not if {
|
||||
this:next:value this:=value
|
||||
this:next:next this:=next
|
||||
}
|
||||
}
|
||||
remove { item | with index this ;
|
||||
index 0 eq if {
|
||||
this:pop-front 2 stop
|
||||
}
|
||||
def list this =list
|
||||
while { index 1 gt } {
|
||||
list:next =list
|
||||
index -- =index
|
||||
}
|
||||
list:next:pop-front
|
||||
}
|
||||
get { item | with index this ;
|
||||
while { index 0 gt } {
|
||||
this:next =this
|
||||
index -- =index
|
||||
}
|
||||
this:value
|
||||
}
|
||||
set { item | with value index this ;
|
||||
while { index 0 gt } {
|
||||
this:next =this
|
||||
index -- =index
|
||||
}
|
||||
this:value value this:=value
|
||||
}
|
||||
foreach { | with callable this ;
|
||||
while { this null eq not } {
|
||||
this:value dup null eq not if { callable:call; }
|
||||
this:next =this
|
||||
}
|
||||
}
|
||||
iter { LinkedListIter | with this ;
|
||||
this LinkedListIter:new
|
||||
}
|
||||
}
|
||||
|
||||
construct LinkedListIter {
|
||||
cur-list
|
||||
;
|
||||
construct { this | with list this ;
|
||||
list this:=cur-list
|
||||
this
|
||||
}
|
||||
next { item | with this ;
|
||||
this:cur-list dup null eq if { 2 stop }
|
||||
:value
|
||||
this:cur-list:next this:=cur-list;
|
||||
}
|
||||
}
|
||||
|
||||
include _Iter in LinkedListIter
|
|
@ -23,9 +23,8 @@ func and { a&&b | with a b ;
|
|||
}
|
||||
|
||||
func or { a||b | with a b ;
|
||||
0
|
||||
a if { pop 1 }
|
||||
b if { pop 1 }
|
||||
b
|
||||
a if { pop a }
|
||||
}
|
||||
|
||||
func alit-end { array | with type ;
|
||||
|
|
45
spl/repl.spl
45
spl/repl.spl
|
@ -1,12 +1,31 @@
|
|||
|
||||
"fast.spl" import
|
||||
|
||||
func main { mega | with args ;
|
||||
"Welcome to the SPL REPL!" println
|
||||
"Enter any code after the cursor to execute it.\n" 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 {
|
||||
" > " print readln dyn-read exec2 "\n" print
|
||||
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 {
|
||||
|
@ -27,3 +46,25 @@ func main { mega | with args ;
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
166
spl/std.spl
166
spl/std.spl
|
@ -42,11 +42,20 @@ construct _str_ext {
|
|||
replacee:to-bytes replacement:to-bytes this:to-bytes:replace:to-str
|
||||
}
|
||||
find { idx | with search this ;
|
||||
search _array this _array :find
|
||||
search _barray this _barray :find
|
||||
}
|
||||
contains { bool | with search this ;
|
||||
search this:find null eq not
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -54,7 +63,7 @@ construct _str_ext {
|
|||
!!- pat this str-readf
|
||||
}
|
||||
readf1 { str | with pat this ;
|
||||
!!- pat this:readf 0:get
|
||||
!!- pat this:readf dup null eq not if { :0 }
|
||||
}
|
||||
uppercase { str | with this ;
|
||||
this _array :cmap<{ | with chr ;
|
||||
|
@ -89,15 +98,27 @@ construct _mega-ext {
|
|||
while { !!- i this lt } { !!- i callable call i 1 + =i }
|
||||
}
|
||||
fforeach { | with callable this ;
|
||||
0 while { !!- dup2 this lt } { !!- callable call 1 + }
|
||||
0 while { !!- dup2 this lt } { !!- callable callp 1 + }
|
||||
}
|
||||
_str_radix { str | with radix this ;
|
||||
this radix mega-to-str-radix
|
||||
}
|
||||
iter { MegaIter | with this ;
|
||||
this MegaIter:new
|
||||
}
|
||||
} include _mega-ext in mega
|
||||
|
||||
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 {
|
||||
|
@ -135,7 +156,7 @@ construct _array-ext {
|
|||
}
|
||||
to-str { str | bytes-to-str }
|
||||
sub { [any] | with begin end this ;
|
||||
this (end begin - anew) begin 0 (end begin -) acopy
|
||||
this (end begin - this:another) begin 0 (end begin -) acopy
|
||||
}
|
||||
split { arr | with splitter this ;
|
||||
def i 0 =i
|
||||
|
@ -197,6 +218,9 @@ construct _array-ext {
|
|||
}
|
||||
null
|
||||
}
|
||||
contains { bool | with search this ;
|
||||
search awrap this:find null eq not
|
||||
}
|
||||
starts-with { bool | with beginning this ;
|
||||
this:len beginning:len lt if {
|
||||
0
|
||||
|
@ -204,6 +228,13 @@ 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
|
||||
}
|
||||
|
@ -240,7 +271,9 @@ construct _array-ext {
|
|||
=4 { | with this ;
|
||||
4 this:set;
|
||||
}
|
||||
} include _array-ext in array
|
||||
}
|
||||
include _array-ext in array
|
||||
include _array-ext in bytearray
|
||||
|
||||
construct _func-ext {
|
||||
args
|
||||
|
@ -288,6 +321,7 @@ construct List {
|
|||
to-stack { .. | :array:to-stack }
|
||||
to-str { str | :array:to-str }
|
||||
sub { [any] | :array:sub }
|
||||
clear { | :=array<0 anew> }
|
||||
}
|
||||
construct _GrowingArray {
|
||||
;
|
||||
|
@ -345,7 +379,7 @@ construct ArrayIter {
|
|||
construct _IterableArray {
|
||||
;
|
||||
iter { ArrayIter | with this ;
|
||||
this gettype "array" eq dup if {
|
||||
this gettype:ends-with<"array"> dup if {
|
||||
pop
|
||||
this ArrayIter:new
|
||||
2 stop
|
||||
|
@ -357,6 +391,7 @@ construct _IterableArray {
|
|||
include _Iter in ArrayIter
|
||||
include _IterableArray in List
|
||||
include _IterableArray in array
|
||||
include _IterableArray in bytearray
|
||||
|
||||
construct MicroMap {
|
||||
pairs
|
||||
|
@ -401,6 +436,20 @@ 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 {
|
||||
|
@ -455,11 +504,27 @@ construct RangeIter {
|
|||
|
||||
include _Iter in RangeIter
|
||||
|
||||
construct MegaIter {
|
||||
i d
|
||||
;
|
||||
construct { this | with d this ;
|
||||
d this:=d
|
||||
0 this:=i
|
||||
this
|
||||
}
|
||||
next { i | with this ;
|
||||
this:i dup ++ this:=i
|
||||
dup this:d lt not if { pop null }
|
||||
}
|
||||
}
|
||||
include _Iter in MegaIter
|
||||
|
||||
|
||||
construct shadow { }
|
||||
|
||||
func aadd { array | with arr1 arr2 ;
|
||||
|
||||
def newarr arr1:len arr2:len + anew =newarr
|
||||
def newarr arr1:len arr2:len + arr1:another =newarr
|
||||
|
||||
arr1 newarr 0 0 arr1:len acopy;
|
||||
arr2 newarr 0 arr1:len arr2:len acopy;
|
||||
|
@ -494,6 +559,8 @@ func cache { ... | with arg-amt id body ;
|
|||
result
|
||||
}
|
||||
|
||||
func panic-handler { | "to be overridden"; }
|
||||
|
||||
def do-not-dump 0 =do-not-dump
|
||||
func handle-panic { | with msg trace ;
|
||||
program-name dup if {
|
||||
|
@ -504,6 +571,7 @@ func handle-panic { | with msg trace ;
|
|||
&println trace:foreach
|
||||
"\nPanic message:" println
|
||||
" " print msg println
|
||||
panic-handler
|
||||
def map env =map
|
||||
"SPL_PANIC_DUMP" env:get dup if {
|
||||
"Dumping because SPL_PANIC_DUMP is set." println
|
||||
|
@ -582,6 +650,90 @@ func times { | with amount callable ;
|
|||
}
|
||||
}
|
||||
|
||||
func check-match { bool | with input output ;
|
||||
input null eq if {
|
||||
0 2 stop
|
||||
}
|
||||
output gettype "func" eq if {
|
||||
1 2 stop
|
||||
}
|
||||
input output eq if {
|
||||
1 2 stop
|
||||
}
|
||||
output gettype "array" eq if {
|
||||
def i, n
|
||||
0 =i
|
||||
output:len =n
|
||||
input:len n eq not if { 0 3 stop }
|
||||
1 while { i n lt } {
|
||||
input:get<i> output:get<i> check-match and
|
||||
i ++ =i
|
||||
}
|
||||
2 stop
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
func match-unchecked { | with input output ;
|
||||
output gettype "func" eq if {
|
||||
input output call
|
||||
2 stop
|
||||
}
|
||||
output gettype "array" eq if {
|
||||
def i, n
|
||||
0 =i
|
||||
output:len =n
|
||||
input:len n eq not if { 3 stop }
|
||||
while { i n lt } {
|
||||
input:get<i> output:get<i> match-unchecked
|
||||
i ++ =i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func match { bool | with input output ;
|
||||
input null eq if {
|
||||
0 2 stop
|
||||
}
|
||||
output gettype "func" eq if {
|
||||
input output call
|
||||
1 2 stop
|
||||
}
|
||||
input output eq if {
|
||||
1 2 stop
|
||||
}
|
||||
output gettype "array" eq if {
|
||||
def i, n
|
||||
0 =i
|
||||
output:len =n
|
||||
input:len n eq not if { 0 3 stop }
|
||||
1 while { i n lt } {
|
||||
input:get<i> output:get<i> check-match and
|
||||
i ++ =i
|
||||
} dup if {
|
||||
0 =i
|
||||
while { i n lt } {
|
||||
input:get<i> output:get<i> match-unchecked
|
||||
i ++ =i
|
||||
}
|
||||
}
|
||||
2 stop
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
func _'match-else-error { |
|
||||
not if {
|
||||
"match unsuccessful" throw
|
||||
}
|
||||
}
|
||||
|
||||
func _'match-else-push { |
|
||||
dup if {
|
||||
swap pop
|
||||
}
|
||||
}
|
||||
|
||||
def _'has-been-called 0 =_'has-been-called
|
||||
func _ { |
|
||||
_'has-been-called not if {
|
||||
|
|
|
@ -15,28 +15,26 @@ construct Stream {
|
|||
this
|
||||
}
|
||||
read-one { mega | with this ;
|
||||
def buf 1 anew =buf
|
||||
def buf 1 banew =buf
|
||||
while { buf this:id read-stream pop not } { }
|
||||
0 buf:get _mega
|
||||
}
|
||||
"the buffer is written to in-place.";
|
||||
read { mega [int] | with buf this ;
|
||||
buf gettype "mega" eq if { buf anew =buf }
|
||||
buf gettype "mega" eq if { buf banew =buf }
|
||||
buf this:id read-stream
|
||||
}
|
||||
"the buffer is written to in-place.";
|
||||
read-exact { [int] | with buf this ;
|
||||
buf gettype "mega" eq if { buf anew =buf }
|
||||
buf gettype "mega" eq if { buf banew =buf }
|
||||
buf this:id read-all-stream buf
|
||||
}
|
||||
read-to-end { [int] | with buf this ;
|
||||
def full 0 anew =full
|
||||
buf gettype "mega" eq if { buf anew =buf }
|
||||
buf gettype "mega" eq if { buf banew =buf }
|
||||
def read
|
||||
while { buf this:id read-stream pop _mega dup =read } {
|
||||
full (0 read buf:sub) aadd =full
|
||||
} pop
|
||||
full
|
||||
0 banew while { buf this:id read-stream pop _mega dup =read } {
|
||||
(0 read buf:sub) aadd
|
||||
}
|
||||
}
|
||||
write { mega | with buf this ;
|
||||
buf this:id write-stream
|
||||
|
@ -47,6 +45,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
|
||||
}
|
||||
|
|
70
src/lexer.rs
70
src/lexer.rs
|
@ -78,6 +78,10 @@ fn read_block_dyn(
|
|||
);
|
||||
}
|
||||
"def" => {
|
||||
while let Some(w) = str_words[i + 1].strip_suffix(',') {
|
||||
words.push(Word::Key(Keyword::Def(w.to_owned())));
|
||||
i += 1;
|
||||
}
|
||||
words.push(Word::Key(Keyword::Def(str_words[i + 1].to_owned())));
|
||||
i += 1;
|
||||
}
|
||||
|
@ -121,7 +125,20 @@ fn read_block_dyn(
|
|||
run_as_base: false,
|
||||
}))))
|
||||
}
|
||||
x if x.len() >= 2 && &x[0..2] == "!{" => {
|
||||
// <| .. > 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.get(0..2) == Some("!{") => {
|
||||
words.push(Word::Const(Value::Str(x[2..].to_owned())));
|
||||
}
|
||||
"<" => {
|
||||
|
@ -240,6 +257,54 @@ fn read_block_dyn(
|
|||
}
|
||||
words.push(Word::Key(Keyword::With(vars)));
|
||||
}
|
||||
"=" => {
|
||||
if str_words[i + 1] == ">" {
|
||||
i += 1;
|
||||
let cword = &str_words[i + 1];
|
||||
if cword.contains(|c| c == '?' || c == '!')
|
||||
&& !cword.contains(|c: char| c == '^' || !c.is_ascii_punctuation())
|
||||
{
|
||||
i += 1;
|
||||
}
|
||||
let pushing = if cword.contains('?') {
|
||||
words.push(Word::Call("dup".to_owned(), false, 0));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let throwing = cword.contains('!');
|
||||
if str_words[i + 1] == "[" {
|
||||
i += 1;
|
||||
let mut block =
|
||||
read_block_dyn(&str_words[i + 1..], false, "]".to_owned(), compat)?;
|
||||
i += block.2 + 1;
|
||||
words.push(Word::Call("[".to_owned(), false, 0));
|
||||
words.append(&mut block.1.words);
|
||||
words.push(Word::Call("]".to_owned(), false, 0));
|
||||
} else {
|
||||
words.append(
|
||||
&mut read_block_dyn(
|
||||
&[str_words[i + 1].clone()],
|
||||
false,
|
||||
"".to_owned(),
|
||||
false,
|
||||
)?
|
||||
.1
|
||||
.words,
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
words.push(Word::Call("match".to_owned(), false, 0));
|
||||
if throwing {
|
||||
words.push(Word::Call("_'match-else-error".to_owned(), false, 0));
|
||||
}
|
||||
if pushing {
|
||||
words.push(Word::Call("_'match-else-push".to_owned(), false, 0));
|
||||
}
|
||||
} else {
|
||||
words.push(Word::Call("=".to_owned(), false, 0));
|
||||
}
|
||||
}
|
||||
"inline-callable" => {
|
||||
words.push(Word::Key(Keyword::InlineCallable));
|
||||
}
|
||||
|
@ -255,6 +320,9 @@ fn read_block_dyn(
|
|||
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) =>
|
||||
|
|
43
src/lib.rs
43
src/lib.rs
|
@ -101,6 +101,24 @@ nofmt! {
|
|||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! require_mut_on_stack {
|
||||
($name:tt, $type:tt, $stack:expr, $fn:literal) => {
|
||||
let binding = $stack.pop();
|
||||
let Value::$type(ref mut $name) = binding.lock().native else {
|
||||
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
|
||||
};
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! require_mut {
|
||||
($name:tt, $type:tt, $binding:expr, $stack:expr, $fn:literal) => {
|
||||
let Value::$type(ref mut $name) = $binding.lock().native else {
|
||||
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! require_int_on_stack {
|
||||
($name:tt, $stack:expr, $fn:literal) => {
|
||||
|
@ -133,6 +151,31 @@ 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();
|
||||
|
|
|
@ -115,6 +115,7 @@ 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
|
||||
}
|
||||
|
@ -473,6 +474,9 @@ 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(),
|
||||
|
@ -515,14 +519,13 @@ 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: frame.clone(),
|
||||
origin: Arc::new(Frame::dummy()),
|
||||
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
|
||||
stack.push(tmpframe.get_var(tmpname.clone(), stack)?);
|
||||
stack.push(stack.get_var(tmpname.clone())?);
|
||||
Ok(())
|
||||
}))),
|
||||
run_as_base: false,
|
||||
|
@ -531,15 +534,14 @@ impl Stack {
|
|||
}),
|
||||
);
|
||||
let tmpname = name.clone();
|
||||
let tmpframe = frame.clone();
|
||||
frame.functions.lock().insert(
|
||||
"=".to_owned() + &name,
|
||||
Arc::new(Func {
|
||||
ret_count: 0,
|
||||
origin: frame.clone(),
|
||||
origin: Arc::new(Frame::dummy()),
|
||||
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
|
||||
let v = stack.pop();
|
||||
tmpframe.set_var(tmpname.clone(), v, stack)
|
||||
stack.set_var(tmpname.clone(), v)
|
||||
}))),
|
||||
run_as_base: false,
|
||||
fname: Some("RUNTIME".to_owned()),
|
||||
|
@ -568,11 +570,33 @@ impl Stack {
|
|||
}
|
||||
|
||||
pub fn set_var(&self, name: String, obj: AMObject) -> OError {
|
||||
self.get_frame().set_var(name, obj, self)
|
||||
if let Err(x) = self.get_frame().set_var(name.clone(), obj.clone(), self) {
|
||||
for i in 1..self.frames.len() {
|
||||
if self
|
||||
.peek_frame(i)
|
||||
.set_var(name.clone(), obj.clone(), self)
|
||||
.is_ok()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
return Err(x);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_var(&self, name: String) -> Result<AMObject, Error> {
|
||||
self.get_frame().get_var(name, self)
|
||||
match self.get_frame().get_var(name.clone(), self) {
|
||||
Err(x) => {
|
||||
for i in 1..self.frames.len() {
|
||||
if let Ok(x) = self.peek_frame(i).get_var(name.clone(), self) {
|
||||
return Ok(x);
|
||||
}
|
||||
}
|
||||
Err(x)
|
||||
}
|
||||
Ok(x) => Ok(x),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, obj: AMObject) {
|
||||
|
@ -771,6 +795,7 @@ pub enum Value {
|
|||
Double(f64),
|
||||
Func(AFunc),
|
||||
Array(Vec<AMObject>),
|
||||
ByteArray(Vec<u8>),
|
||||
Str(String),
|
||||
}
|
||||
|
||||
|
@ -1058,14 +1083,15 @@ impl Object {
|
|||
pub fn is_truthy(&self) -> bool {
|
||||
match &self.native {
|
||||
Value::Null => self.kind.lock_ro().id != 0,
|
||||
Value::Int(x) => x > &0,
|
||||
Value::Long(x) => x > &0,
|
||||
Value::Mega(x) => x > &0,
|
||||
Value::Float(_) => true,
|
||||
Value::Double(_) => true,
|
||||
Value::Int(x) => *x > 0,
|
||||
Value::Long(x) => *x > 0,
|
||||
Value::Mega(x) => *x > 0,
|
||||
Value::Float(x) => x.is_finite(),
|
||||
Value::Double(x) => x.is_finite(),
|
||||
Value::Func(_) => true,
|
||||
Value::Array(_) => true,
|
||||
Value::Str(x) => !x.is_empty(),
|
||||
Value::ByteArray(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1125,6 +1151,7 @@ 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.")
|
||||
}),
|
||||
|
|
38
src/sasm.rs
38
src/sasm.rs
|
@ -175,6 +175,25 @@ fn sasm_parse<'a>(line: &str, words: &mut Vec<Word>, lines: &mut impl Iterator<I
|
|||
})))),
|
||||
"null" => words.push(Word::Const(Value::Null)),
|
||||
"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" => {
|
||||
|
@ -356,6 +375,25 @@ 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 ";
|
||||
|
|
347
src/std_fns.rs
347
src/std_fns.rs
|
@ -1,9 +1,8 @@
|
|||
use std::{
|
||||
collections::VecDeque,
|
||||
env::{args, vars},
|
||||
collections::{HashMap, VecDeque},
|
||||
env::{self, args, vars},
|
||||
fs,
|
||||
io::{stdin, stdout, Write},
|
||||
mem,
|
||||
ops::{Add, Div, Mul, Rem, Sub},
|
||||
process::{self, Stdio},
|
||||
sync::Arc,
|
||||
|
@ -96,12 +95,27 @@ 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 {
|
||||
|
@ -110,6 +124,23 @@ 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()));
|
||||
|
@ -123,36 +154,46 @@ pub fn array_new(stack: &mut Stack) -> OError {
|
|||
|
||||
pub fn array_len(stack: &mut Stack) -> OError {
|
||||
let binding = stack.pop();
|
||||
let Value::Array(ref a) = binding.lock_ro().native else {
|
||||
return stack.err(ErrorKind::InvalidCall("array-len".to_owned()));
|
||||
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())),
|
||||
};
|
||||
stack.push(Value::Mega(a.len() as i128).spl());
|
||||
stack.push(Value::Mega(len as i128).spl());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn array_get(stack: &mut Stack) -> OError {
|
||||
let binding = stack.pop();
|
||||
let Value::Array(ref a) = binding.lock_ro().native else {
|
||||
let Value::Mega(i) = stack.pop().lock_ro().native else {
|
||||
return stack.err(ErrorKind::InvalidCall("array-get".to_owned()));
|
||||
};
|
||||
let Value::Mega(i) = stack.pop().lock_ro().native.clone() 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())),
|
||||
};
|
||||
stack.push(a.get(i as usize).ok_or_else(array!(stack, i))?.clone());
|
||||
stack.push(o.ok_or_else(array!(stack, i))?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn array_set(stack: &mut Stack) -> OError {
|
||||
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 binding = &stack.pop();
|
||||
let binding = &mut binding.lock().native;
|
||||
require_on_stack!(i, Mega, stack, "array-set");
|
||||
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;
|
||||
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 {
|
||||
return stack.err(ErrorKind::InvalidCall("array-set".to_owned()));
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -227,6 +268,8 @@ pub fn plus(stack: &mut Stack) -> OError {
|
|||
Mega,
|
||||
Long,
|
||||
Int,
|
||||
Float,
|
||||
Double,
|
||||
)
|
||||
.spl(),
|
||||
);
|
||||
|
@ -245,6 +288,8 @@ pub fn minus(stack: &mut Stack) -> OError {
|
|||
Mega,
|
||||
Long,
|
||||
Int,
|
||||
Float,
|
||||
Double,
|
||||
)
|
||||
.spl(),
|
||||
);
|
||||
|
@ -263,6 +308,8 @@ pub fn slash(stack: &mut Stack) -> OError {
|
|||
Mega,
|
||||
Long,
|
||||
Int,
|
||||
Float,
|
||||
Double,
|
||||
)
|
||||
.spl(),
|
||||
);
|
||||
|
@ -281,6 +328,8 @@ pub fn star(stack: &mut Stack) -> OError {
|
|||
Mega,
|
||||
Long,
|
||||
Int,
|
||||
Float,
|
||||
Double,
|
||||
)
|
||||
.spl(),
|
||||
);
|
||||
|
@ -295,7 +344,7 @@ pub fn percent(stack: &mut Stack) -> OError {
|
|||
a,
|
||||
b,
|
||||
rem,
|
||||
stack.err(ErrorKind::InvalidCall("star".to_owned())),
|
||||
stack.err(ErrorKind::InvalidCall("percent".to_owned())),
|
||||
Mega,
|
||||
Long,
|
||||
Int,
|
||||
|
@ -320,6 +369,7 @@ 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(),
|
||||
);
|
||||
|
@ -341,6 +391,7 @@ 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(),
|
||||
);
|
||||
|
@ -362,6 +413,7 @@ 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(),
|
||||
);
|
||||
|
@ -383,6 +435,7 @@ 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(),
|
||||
);
|
||||
|
@ -404,6 +457,7 @@ 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(),
|
||||
);
|
||||
|
@ -426,6 +480,11 @@ 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(),
|
||||
);
|
||||
|
@ -466,6 +525,46 @@ 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(),
|
||||
);
|
||||
|
@ -605,6 +704,7 @@ 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()));
|
||||
|
@ -712,17 +812,31 @@ pub fn command(stack: &mut Stack) -> OError {
|
|||
if args.is_empty() {
|
||||
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
|
||||
}
|
||||
process::Command::new(&args[0])
|
||||
.args(&args[1..])
|
||||
.stdin(Stdio::inherit())
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.spawn()
|
||||
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?;
|
||||
stack.push(
|
||||
Value::Long(
|
||||
process::Command::new(&args[0])
|
||||
.args(&args[1..])
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?
|
||||
.id() as i64,
|
||||
)
|
||||
.spl(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn command_wait(stack: &mut Stack) -> OError {
|
||||
command_wait_impl(stack, Stdio::inherit)
|
||||
}
|
||||
|
||||
pub fn command_wait_silent(stack: &mut Stack) -> OError {
|
||||
command_wait_impl(stack, Stdio::null)
|
||||
}
|
||||
|
||||
pub fn command_wait_impl(stack: &mut Stack, stdio: fn() -> Stdio) -> OError {
|
||||
let binding = stack.pop();
|
||||
let Value::Array(ref a) = binding.lock_ro().native else {
|
||||
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
|
||||
|
@ -742,9 +856,9 @@ pub fn command_wait(stack: &mut Stack) -> OError {
|
|||
Value::Int(
|
||||
process::Command::new(&args[0])
|
||||
.args(&args[1..])
|
||||
.stdin(Stdio::inherit())
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.stdin(stdio())
|
||||
.stdout(stdio())
|
||||
.stderr(stdio())
|
||||
.spawn()
|
||||
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?
|
||||
.wait()
|
||||
|
@ -759,29 +873,18 @@ 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::Array(
|
||||
s.bytes()
|
||||
.into_iter()
|
||||
.map(|x| Value::Int(x as i32).spl())
|
||||
.collect(),
|
||||
)
|
||||
.spl(),
|
||||
);
|
||||
stack.push(Value::ByteArray(s.bytes().collect()).spl());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn bytes_to_str(stack: &mut Stack) -> OError {
|
||||
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()));
|
||||
}
|
||||
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(());
|
||||
}
|
||||
stack.push(Value::Str(String::from_utf8_lossy(&chars[..]).into_owned()).spl());
|
||||
require_byte_array_on_stack!(a, stack, "bytes-to-str");
|
||||
stack.push(Value::Str(String::from_utf8_lossy(&a).into_owned()).spl());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -790,7 +893,8 @@ 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;
|
||||
|
@ -805,6 +909,14 @@ 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(())
|
||||
}
|
||||
|
@ -906,7 +1018,8 @@ pub fn str_to_mega_radix(stack: &mut Stack) -> OError {
|
|||
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");
|
||||
let mut result = Vec::new();
|
||||
// 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 {
|
||||
|
@ -930,9 +1043,122 @@ pub fn mega_to_str_radix(stack: &mut Stack) -> OError {
|
|||
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); 59] = [
|
||||
let fns: [(&str, Fn, u32); 70] = [
|
||||
("pop", pop, 0),
|
||||
("dup", dup, 2),
|
||||
("dup2", dup2, 3),
|
||||
|
@ -941,7 +1167,10 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
|||
("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),
|
||||
|
@ -964,6 +1193,7 @@ 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),
|
||||
|
@ -979,8 +1209,9 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
|||
("alit-end", alit_end, 1),
|
||||
("import", import, 0),
|
||||
("readln", readln, 1),
|
||||
("command", command, 0),
|
||||
("command", command, 1),
|
||||
("command-wait", command_wait, 1),
|
||||
("command-wait-silent", command_wait_silent, 1),
|
||||
("str-to-bytes", str_to_bytes, 1),
|
||||
("bytes-to-str", bytes_to_str, 1),
|
||||
("acopy", acopy, 1),
|
||||
|
@ -992,6 +1223,12 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
|||
("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(
|
||||
|
|
|
@ -10,11 +10,14 @@ 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 JSON: &str = include_str!("../spl/json.spl");
|
||||
pub const NOP: &str = "";
|
||||
|
||||
pub fn register(runtime: &mut Runtime) {
|
||||
|
@ -29,11 +32,14 @@ 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("json.spl", JSON);
|
||||
insert("nop.spl", NOP);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
|||
}
|
||||
|
||||
type Fn = fn(&mut Stack) -> OError;
|
||||
let fns: [(&str, Fn, u32); 11] = [
|
||||
let fns: [(&str, Fn, u32); 12] = [
|
||||
("new-stream", new_stream, 1),
|
||||
("write-stream", write_stream, 1),
|
||||
("write-all-stream", write_all_stream, 0),
|
||||
|
@ -37,6 +37,7 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
|||
("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(
|
||||
|
|
|
@ -86,6 +86,16 @@ impl 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 {
|
||||
|
@ -132,6 +142,17 @@ 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,
|
||||
|
@ -155,23 +176,16 @@ 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_array_on_stack!(a, stack, "write-stream");
|
||||
require_byte_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(&fixed[..])
|
||||
.write(&a)
|
||||
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))? as i128,
|
||||
)
|
||||
.spl(),
|
||||
|
@ -182,21 +196,14 @@ pub fn write_stream(stack: &mut Stack) -> OError {
|
|||
|
||||
pub fn write_all_stream(stack: &mut Stack) -> OError {
|
||||
require_on_stack!(id, Mega, stack, "write-all-stream");
|
||||
require_array_on_stack!(a, stack, "write-all-stream");
|
||||
require_byte_array_on_stack!(a, stack, "write-all-stream");
|
||||
let stream = runtime(|rt| {
|
||||
rt.get_stream(id as u128)
|
||||
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
||||
})?;
|
||||
let mut fixed = Vec::with_capacity(a.len());
|
||||
for item in a.iter() {
|
||||
match item.lock_ro().native {
|
||||
Value::Int(x) => fixed.push(x as u8),
|
||||
_ => type_err!(stack, "!int", "int"),
|
||||
}
|
||||
}
|
||||
stream
|
||||
.lock()
|
||||
.write_all(&fixed[..])
|
||||
.write_all(&a)
|
||||
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?;
|
||||
black_box(&stream.lock_ro()._writer_storage);
|
||||
Ok(())
|
||||
|
@ -219,12 +226,13 @@ pub fn flush_stream(stack: &mut Stack) -> OError {
|
|||
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(
|
||||
|
@ -242,6 +250,19 @@ 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(())
|
||||
}
|
||||
|
@ -249,12 +270,13 @@ 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()
|
||||
|
@ -266,6 +288,13 @@ 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(())
|
||||
}
|
||||
|
@ -276,6 +305,16 @@ pub fn close_stream(stack: &mut Stack) -> OError {
|
|||
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(())
|
||||
}
|
||||
|
||||
pub(super) 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");
|
||||
|
@ -335,7 +374,7 @@ pub(super) fn stream_cmd(stack: &mut Stack) -> Result<Stream, Error> {
|
|||
}
|
||||
}
|
||||
if args.is_empty() {
|
||||
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
|
||||
return stack.err(ErrorKind::InvalidCall("CMD new-stream".to_owned()));
|
||||
}
|
||||
let mut command = process::Command::new(&args[0])
|
||||
.args(&args[1..])
|
||||
|
|
120
test.spl
120
test.spl
|
@ -1,10 +1,12 @@
|
|||
[
|
||||
"#stream.spl" import
|
||||
"#http.spl" import
|
||||
"#messaging.spl" import
|
||||
"#server.spl" import
|
||||
"#time.spl" import
|
||||
"#httpserver/base.spl" import
|
||||
"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
|
||||
"spl/json.spl" import
|
||||
|
||||
|
||||
"SPL tester" =program-name
|
||||
|
@ -237,16 +239,116 @@ func main { int | with args ;
|
|||
"hello! this is a test of URL encoding!" net:http:urlencode dup println;
|
||||
net:http:urldecode println;
|
||||
|
||||
5 :foreach<{ | "" 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;
|
||||
|
||||
|
||||
^ println
|
||||
"testing match" println
|
||||
|
||||
"a -> b (0): " print "a" "b" match _str println
|
||||
"a -> a (1): " print "a" "a" match _str println
|
||||
"[a b] -> [a a] (0): " print [ "a" "b" ] [ "a" "a" ] match _str println
|
||||
"[a b] -> [a b] (1): " print [ "a" "b" ] [ "a" "b" ] match _str println
|
||||
|
||||
def mtesta, mtestb
|
||||
|
||||
"a => =mtesta (1): " print "a" &=mtesta match _str println
|
||||
"[a b] => [a =mtesta] (1): " print [ "a" "b" ] [ "a" &=mtesta ] match _str println
|
||||
"[b a] => [a =mtesta] (0): " print [ "b" "a" ] [ "a" &=mtesta ] match _str println
|
||||
"[a b] => [=mtesta a] (0): " print [ "b" "a" ] [ "a" &=mtesta ] match _str println
|
||||
"-> mtesta = (b) " mtesta _str concat println
|
||||
|
||||
"^ok => ^ok (1): " print ^ok => ^ok _str println
|
||||
"^bad => ^ok (0): " print ^bad => ^ok _str println
|
||||
"[^bad a] => [^ok =mtestb] (0): " print [ ^bad "a" ] => [ ^ok &=mtestb ] _str println
|
||||
"[^ok b] => [^ok =mtestb] (1): " print [ ^ok "b" ] => [ ^ok &=mtestb ] _str println
|
||||
|
||||
"-> mtestb = (b) " mtestb _str concat println
|
||||
|
||||
def result, val
|
||||
|
||||
[ ^ok "hello, world" ] =result
|
||||
|
||||
result =>! [ ^ok &=val ]
|
||||
val println
|
||||
|
||||
[ ^error "bad" ] =result
|
||||
|
||||
catch Custom {
|
||||
result =>! [ ^ok &println ]
|
||||
} { with e ;
|
||||
e:message "match unsuccessful" eq if {
|
||||
"err value: " print
|
||||
result =>! [ ^error &println ]
|
||||
}
|
||||
}
|
||||
|
||||
^ok =>? ^error not if { println }
|
||||
|
||||
"" println
|
||||
"json test" println
|
||||
|
||||
0 [ 0 1 2 "hi" ] properties json:props-to-sjson println
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
5 :foreach<{ | pop "" println }>
|
||||
|
||||
"you now have a chance to connect too: localhost :4075 :4076 - stopping in 5 seconds..." println;
|
||||
5000 time:sleep;
|
||||
|
||||
] 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
|
||||
}
|
||||
|
||||
|
|
1
test.txt
1
test.txt
|
@ -1 +0,0 @@
|
|||
hi
|
Loading…
Add table
Reference in a new issue