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]
|
[package]
|
||||||
name = "spl"
|
name = "spl"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Stack Pogramming Language: A simple, concise scripting language."
|
description = "Stack Pogramming Language: A simple, concise scripting language."
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
42
README.md
42
README.md
|
@ -11,6 +11,9 @@ func main { mega | with args ;
|
||||||
&print swap:foreach
|
&print swap:foreach
|
||||||
"" println
|
"" println
|
||||||
println<"and with that, we're done">
|
println<"and with that, we're done">
|
||||||
|
[ ^hello ^pattern-matching ] => [ ^hello &=args ] if {
|
||||||
|
args println "prints pattern-matching";
|
||||||
|
}
|
||||||
0
|
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
|
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.
|
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.
|
||||||
|
|
||||||
|
## 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 ;
|
construct { this | with server stream this ;
|
||||||
server this:=server
|
server this:=server
|
||||||
stream this:=stream
|
stream this:=stream
|
||||||
0 anew this:=head
|
0 banew this:=head
|
||||||
0 anew this:=body
|
0 banew this:=body
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
read-head { this | with this ;
|
read-head { this | with this ;
|
||||||
def read
|
def read
|
||||||
def buf 1024 anew =buf
|
def buf 1024 banew =buf
|
||||||
def found
|
def found
|
||||||
while {
|
while {
|
||||||
buf this:stream:read pop =read
|
buf this:stream:read pop dup =read
|
||||||
"\r\n\r\n" :to-bytes buf:find dup =found not read and
|
"\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
|
buf:sub<found 4 + buf:len> this:=body
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
parse-head { this | with 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
|
def iter this:head:iter =iter
|
||||||
iter:next:readf<"{} {} HTTP/{}"> dup if {
|
iter:next:readf<"{} {} HTTP/{}"> dup if {
|
||||||
dup:to-stack this:=version this:=raw-path this:=method
|
dup:to-stack this:=version this:=raw-path this:=method
|
||||||
|
@ -100,7 +108,7 @@ construct net:http:server:Request {
|
||||||
read-body { this | with this ;
|
read-body { this | with this ;
|
||||||
this:headers:get<"content-length"> dup if { _mega with content-length ;
|
this:headers:get<"content-length"> dup if { _mega with content-length ;
|
||||||
def read
|
def read
|
||||||
def buf 1024 anew =buf
|
def buf 1024 banew =buf
|
||||||
while {
|
while {
|
||||||
this:body:len content-length lt read and
|
this:body:len content-length lt read and
|
||||||
} {
|
} {
|
||||||
|
@ -146,7 +154,7 @@ construct net:http:server:Request {
|
||||||
}
|
}
|
||||||
finish { | with this ;
|
finish { | with this ;
|
||||||
this:wrote-body not if {
|
this:wrote-body not if {
|
||||||
0 anew this:write-body;
|
0 banew this:write-body;
|
||||||
}
|
}
|
||||||
this:stream:close;
|
this:stream:close;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
"base.spl" import
|
"base.spl" import
|
||||||
|
"../stream.spl" import
|
||||||
|
|
||||||
"_static_ext_server" net:http:server:register
|
"_static_ext_server" net:http:server:register
|
||||||
"_static_ext_Request" 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 {
|
construct net:http:server:_static_ext_server {
|
||||||
|
bufsize
|
||||||
|
client-cache
|
||||||
cached-files
|
cached-files
|
||||||
;
|
;
|
||||||
get-cached-files { cached-files | with this ;
|
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 {
|
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 ;
|
serve-file { this | with filepath path type this ;
|
||||||
this:path path eq if {
|
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
|
this
|
||||||
}
|
}
|
||||||
serve-file-cached { this | with filepath path type this ;
|
serve-file-cached { this | with filepath path type this ;
|
||||||
this:path path eq if {
|
this:path path eq if {
|
||||||
filepath this:server:get-cached-files:get dup not 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
|
this
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,12 @@ construct _Iter {
|
||||||
foreach { | with callable this ;
|
foreach { | with callable this ;
|
||||||
!!-
|
!!-
|
||||||
while { !!- this:next dup null eq not } {
|
while { !!- this:next dup null eq not } {
|
||||||
!!- callable call
|
!!- callable callp
|
||||||
}
|
}
|
||||||
pop
|
pop
|
||||||
}
|
}
|
||||||
collect { array | with this ;
|
collect { array | with this ;
|
||||||
[ { any | } this:foreach ]
|
[ { | } this:foreach ]
|
||||||
}
|
}
|
||||||
map { MapIter | with map-function this ;
|
map { MapIter | with map-function this ;
|
||||||
map-function this MapIter:new
|
map-function this MapIter:new
|
||||||
|
|
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 ;
|
func or { a||b | with a b ;
|
||||||
0
|
b
|
||||||
a if { pop 1 }
|
a if { pop a }
|
||||||
b if { pop 1 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func alit-end { array | with type ;
|
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 ;
|
func main { mega | with args ;
|
||||||
"Welcome to the SPL REPL!" println
|
"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
|
"REPL" =program-name
|
||||||
while { 1 } {
|
while { 1 } {
|
||||||
catch {
|
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 ;
|
{ with err ;
|
||||||
err:message dup null eq if {
|
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
|
replacee:to-bytes replacement:to-bytes this:to-bytes:replace:to-str
|
||||||
}
|
}
|
||||||
find { idx | with search this ;
|
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 ;
|
starts-with { bool | with beginning this ;
|
||||||
beginning:to-bytes this:to-bytes:starts-with
|
beginning:to-bytes this:to-bytes:starts-with
|
||||||
}
|
}
|
||||||
|
ends-with { bool | with ending this ;
|
||||||
|
ending:to-bytes this:to-bytes:ends-with
|
||||||
|
}
|
||||||
|
escape { formatted | with this ;
|
||||||
|
"\"" this:replace<"\\" "\\\\">:replace<"\"" "\\\"">:replace<"\n" "\\n">:replace<"\r" "\\r"> concat "\"" concat
|
||||||
|
}
|
||||||
_char { int | with this ;
|
_char { int | with this ;
|
||||||
0 (this _array):get
|
0 (this _array):get
|
||||||
}
|
}
|
||||||
|
@ -54,7 +63,7 @@ construct _str_ext {
|
||||||
!!- pat this str-readf
|
!!- pat this str-readf
|
||||||
}
|
}
|
||||||
readf1 { str | with pat this ;
|
readf1 { str | with pat this ;
|
||||||
!!- pat this:readf 0:get
|
!!- pat this:readf dup null eq not if { :0 }
|
||||||
}
|
}
|
||||||
uppercase { str | with this ;
|
uppercase { str | with this ;
|
||||||
this _array :cmap<{ | with chr ;
|
this _array :cmap<{ | with chr ;
|
||||||
|
@ -89,15 +98,27 @@ construct _mega-ext {
|
||||||
while { !!- i this lt } { !!- i callable call i 1 + =i }
|
while { !!- i this lt } { !!- i callable call i 1 + =i }
|
||||||
}
|
}
|
||||||
fforeach { | with callable this ;
|
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 ;
|
_str_radix { str | with radix this ;
|
||||||
this radix mega-to-str-radix
|
this radix mega-to-str-radix
|
||||||
}
|
}
|
||||||
|
iter { MegaIter | with this ;
|
||||||
|
this MegaIter:new
|
||||||
|
}
|
||||||
} include _mega-ext in mega
|
} include _mega-ext in mega
|
||||||
|
|
||||||
construct _array-ext {
|
construct _array-ext {
|
||||||
;
|
;
|
||||||
|
another { array | with this ;
|
||||||
|
this gettype =this
|
||||||
|
this "array" eq if {
|
||||||
|
anew
|
||||||
|
}
|
||||||
|
this "bytearray" eq if {
|
||||||
|
banew
|
||||||
|
}
|
||||||
|
}
|
||||||
get { any | array-get }
|
get { any | array-get }
|
||||||
sget { any|null | with idx this ;
|
sget { any|null | with idx this ;
|
||||||
idx this:len lt idx -1 gt and dup if {
|
idx this:len lt idx -1 gt and dup if {
|
||||||
|
@ -135,7 +156,7 @@ construct _array-ext {
|
||||||
}
|
}
|
||||||
to-str { str | bytes-to-str }
|
to-str { str | bytes-to-str }
|
||||||
sub { [any] | with begin end this ;
|
sub { [any] | with begin end this ;
|
||||||
this (end begin - anew) begin 0 (end begin -) acopy
|
this (end begin - this:another) begin 0 (end begin -) acopy
|
||||||
}
|
}
|
||||||
split { arr | with splitter this ;
|
split { arr | with splitter this ;
|
||||||
def i 0 =i
|
def i 0 =i
|
||||||
|
@ -197,6 +218,9 @@ construct _array-ext {
|
||||||
}
|
}
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
contains { bool | with search this ;
|
||||||
|
search awrap this:find null eq not
|
||||||
|
}
|
||||||
starts-with { bool | with beginning this ;
|
starts-with { bool | with beginning this ;
|
||||||
this:len beginning:len lt if {
|
this:len beginning:len lt if {
|
||||||
0
|
0
|
||||||
|
@ -204,6 +228,13 @@ construct _array-ext {
|
||||||
}
|
}
|
||||||
0 beginning:len this:sub beginning eq
|
0 beginning:len this:sub beginning eq
|
||||||
}
|
}
|
||||||
|
ends-with { bool | with ending this ;
|
||||||
|
this:len ending:len lt if {
|
||||||
|
0
|
||||||
|
2 stop
|
||||||
|
}
|
||||||
|
this:len ending:len - this:len this:sub ending eq
|
||||||
|
}
|
||||||
last { | with this ;
|
last { | with this ;
|
||||||
this:len -- this:get
|
this:len -- this:get
|
||||||
}
|
}
|
||||||
|
@ -240,7 +271,9 @@ construct _array-ext {
|
||||||
=4 { | with this ;
|
=4 { | with this ;
|
||||||
4 this:set;
|
4 this:set;
|
||||||
}
|
}
|
||||||
} include _array-ext in array
|
}
|
||||||
|
include _array-ext in array
|
||||||
|
include _array-ext in bytearray
|
||||||
|
|
||||||
construct _func-ext {
|
construct _func-ext {
|
||||||
args
|
args
|
||||||
|
@ -288,6 +321,7 @@ construct List {
|
||||||
to-stack { .. | :array:to-stack }
|
to-stack { .. | :array:to-stack }
|
||||||
to-str { str | :array:to-str }
|
to-str { str | :array:to-str }
|
||||||
sub { [any] | :array:sub }
|
sub { [any] | :array:sub }
|
||||||
|
clear { | :=array<0 anew> }
|
||||||
}
|
}
|
||||||
construct _GrowingArray {
|
construct _GrowingArray {
|
||||||
;
|
;
|
||||||
|
@ -345,7 +379,7 @@ construct ArrayIter {
|
||||||
construct _IterableArray {
|
construct _IterableArray {
|
||||||
;
|
;
|
||||||
iter { ArrayIter | with this ;
|
iter { ArrayIter | with this ;
|
||||||
this gettype "array" eq dup if {
|
this gettype:ends-with<"array"> dup if {
|
||||||
pop
|
pop
|
||||||
this ArrayIter:new
|
this ArrayIter:new
|
||||||
2 stop
|
2 stop
|
||||||
|
@ -357,6 +391,7 @@ construct _IterableArray {
|
||||||
include _Iter in ArrayIter
|
include _Iter in ArrayIter
|
||||||
include _IterableArray in List
|
include _IterableArray in List
|
||||||
include _IterableArray in array
|
include _IterableArray in array
|
||||||
|
include _IterableArray in bytearray
|
||||||
|
|
||||||
construct MicroMap {
|
construct MicroMap {
|
||||||
pairs
|
pairs
|
||||||
|
@ -401,6 +436,20 @@ construct MicroMap {
|
||||||
foreach { | with callable this ;
|
foreach { | with callable this ;
|
||||||
callable this:pairs:foreach
|
callable this:pairs:foreach
|
||||||
}
|
}
|
||||||
|
clear { | with this ;
|
||||||
|
this:pairs:clear;
|
||||||
|
}
|
||||||
|
to-str { str | with this ;
|
||||||
|
"{ "
|
||||||
|
{ | with item ;
|
||||||
|
"'" concat
|
||||||
|
0 item:get dup null eq if { "key is null" panic } _str concat
|
||||||
|
"': '" concat
|
||||||
|
1 item:get dup null eq if { "value is null" panic } _str concat
|
||||||
|
"', " concat
|
||||||
|
} this:foreach
|
||||||
|
"}" concat
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
construct Range {
|
construct Range {
|
||||||
|
@ -455,11 +504,27 @@ construct RangeIter {
|
||||||
|
|
||||||
include _Iter in RangeIter
|
include _Iter in RangeIter
|
||||||
|
|
||||||
|
construct MegaIter {
|
||||||
|
i d
|
||||||
|
;
|
||||||
|
construct { this | with d this ;
|
||||||
|
d this:=d
|
||||||
|
0 this:=i
|
||||||
|
this
|
||||||
|
}
|
||||||
|
next { i | with this ;
|
||||||
|
this:i dup ++ this:=i
|
||||||
|
dup this:d lt not if { pop null }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
include _Iter in MegaIter
|
||||||
|
|
||||||
|
|
||||||
construct shadow { }
|
construct shadow { }
|
||||||
|
|
||||||
func aadd { array | with arr1 arr2 ;
|
func aadd { array | with arr1 arr2 ;
|
||||||
|
|
||||||
def newarr arr1:len arr2:len + anew =newarr
|
def newarr arr1:len arr2:len + arr1:another =newarr
|
||||||
|
|
||||||
arr1 newarr 0 0 arr1:len acopy;
|
arr1 newarr 0 0 arr1:len acopy;
|
||||||
arr2 newarr 0 arr1:len arr2:len acopy;
|
arr2 newarr 0 arr1:len arr2:len acopy;
|
||||||
|
@ -494,6 +559,8 @@ func cache { ... | with arg-amt id body ;
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func panic-handler { | "to be overridden"; }
|
||||||
|
|
||||||
def do-not-dump 0 =do-not-dump
|
def do-not-dump 0 =do-not-dump
|
||||||
func handle-panic { | with msg trace ;
|
func handle-panic { | with msg trace ;
|
||||||
program-name dup if {
|
program-name dup if {
|
||||||
|
@ -504,6 +571,7 @@ func handle-panic { | with msg trace ;
|
||||||
&println trace:foreach
|
&println trace:foreach
|
||||||
"\nPanic message:" println
|
"\nPanic message:" println
|
||||||
" " print msg println
|
" " print msg println
|
||||||
|
panic-handler
|
||||||
def map env =map
|
def map env =map
|
||||||
"SPL_PANIC_DUMP" env:get dup if {
|
"SPL_PANIC_DUMP" env:get dup if {
|
||||||
"Dumping because SPL_PANIC_DUMP is set." println
|
"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
|
def _'has-been-called 0 =_'has-been-called
|
||||||
func _ { |
|
func _ { |
|
||||||
_'has-been-called not if {
|
_'has-been-called not if {
|
||||||
|
|
|
@ -15,28 +15,26 @@ construct Stream {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
read-one { mega | with this ;
|
read-one { mega | with this ;
|
||||||
def buf 1 anew =buf
|
def buf 1 banew =buf
|
||||||
while { buf this:id read-stream pop not } { }
|
while { buf this:id read-stream pop not } { }
|
||||||
0 buf:get _mega
|
0 buf:get _mega
|
||||||
}
|
}
|
||||||
"the buffer is written to in-place.";
|
"the buffer is written to in-place.";
|
||||||
read { mega [int] | with buf this ;
|
read { mega [int] | with buf this ;
|
||||||
buf gettype "mega" eq if { buf anew =buf }
|
buf gettype "mega" eq if { buf banew =buf }
|
||||||
buf this:id read-stream
|
buf this:id read-stream
|
||||||
}
|
}
|
||||||
"the buffer is written to in-place.";
|
"the buffer is written to in-place.";
|
||||||
read-exact { [int] | with buf this ;
|
read-exact { [int] | with buf this ;
|
||||||
buf gettype "mega" eq if { buf anew =buf }
|
buf gettype "mega" eq if { buf banew =buf }
|
||||||
buf this:id read-all-stream buf
|
buf this:id read-all-stream buf
|
||||||
}
|
}
|
||||||
read-to-end { [int] | with buf this ;
|
read-to-end { [int] | with buf this ;
|
||||||
def full 0 anew =full
|
buf gettype "mega" eq if { buf banew =buf }
|
||||||
buf gettype "mega" eq if { buf anew =buf }
|
|
||||||
def read
|
def read
|
||||||
while { buf this:id read-stream pop _mega dup =read } {
|
0 banew while { buf this:id read-stream pop _mega dup =read } {
|
||||||
full (0 read buf:sub) aadd =full
|
(0 read buf:sub) aadd
|
||||||
} pop
|
}
|
||||||
full
|
|
||||||
}
|
}
|
||||||
write { mega | with buf this ;
|
write { mega | with buf this ;
|
||||||
buf this:id write-stream
|
buf this:id write-stream
|
||||||
|
@ -47,6 +45,9 @@ construct Stream {
|
||||||
flush { | with this ;
|
flush { | with this ;
|
||||||
this:id flush-stream
|
this:id flush-stream
|
||||||
}
|
}
|
||||||
|
shutdown-input { | with this ;
|
||||||
|
this:id shutdown-input-stream
|
||||||
|
}
|
||||||
close { | with this ;
|
close { | with this ;
|
||||||
this:id close-stream
|
this:id close-stream
|
||||||
}
|
}
|
||||||
|
|
70
src/lexer.rs
70
src/lexer.rs
|
@ -78,6 +78,10 @@ fn read_block_dyn(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
"def" => {
|
"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())));
|
words.push(Word::Key(Keyword::Def(str_words[i + 1].to_owned())));
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
@ -121,7 +125,20 @@ fn read_block_dyn(
|
||||||
run_as_base: false,
|
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())));
|
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)));
|
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" => {
|
"inline-callable" => {
|
||||||
words.push(Word::Key(Keyword::InlineCallable));
|
words.push(Word::Key(Keyword::InlineCallable));
|
||||||
}
|
}
|
||||||
|
@ -255,6 +320,9 @@ fn read_block_dyn(
|
||||||
x if x.starts_with('\"') => {
|
x if x.starts_with('\"') => {
|
||||||
words.push(Word::Const(Value::Str(x[1..].to_owned())));
|
words.push(Word::Const(Value::Str(x[1..].to_owned())));
|
||||||
}
|
}
|
||||||
|
x if x.starts_with('^') => {
|
||||||
|
words.push(Word::Const(Value::Str(x[1..].to_owned())));
|
||||||
|
}
|
||||||
x if x.chars().all(|c| c.is_numeric() || c == '_' || c == '-')
|
x if x.chars().all(|c| c.is_numeric() || c == '_' || c == '-')
|
||||||
&& !x.starts_with('_')
|
&& !x.starts_with('_')
|
||||||
&& x.contains(char::is_numeric) =>
|
&& x.contains(char::is_numeric) =>
|
||||||
|
|
43
src/lib.rs
43
src/lib.rs
|
@ -101,6 +101,24 @@ nofmt! {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! require_mut_on_stack {
|
||||||
|
($name:tt, $type:tt, $stack:expr, $fn:literal) => {
|
||||||
|
let binding = $stack.pop();
|
||||||
|
let Value::$type(ref mut $name) = binding.lock().native else {
|
||||||
|
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! require_mut {
|
||||||
|
($name:tt, $type:tt, $binding:expr, $stack:expr, $fn:literal) => {
|
||||||
|
let Value::$type(ref mut $name) = $binding.lock().native else {
|
||||||
|
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! require_int_on_stack {
|
macro_rules! require_int_on_stack {
|
||||||
($name:tt, $stack:expr, $fn:literal) => {
|
($name:tt, $stack:expr, $fn:literal) => {
|
||||||
|
@ -133,6 +151,31 @@ nofmt! {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
macro_rules! require_byte_array_on_stack {
|
||||||
|
($name:tt, $stack:expr, $fn:literal) => {
|
||||||
|
let binding = $stack.pop();
|
||||||
|
let $name = match binding.lock_ro().native {
|
||||||
|
Value::Array(ref x) => x
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|x| {
|
||||||
|
Ok(match &x.lock_ro().native {
|
||||||
|
Value::Int(x) => *x as u8,
|
||||||
|
Value::Long(x) => *x as u8,
|
||||||
|
Value::Mega(x) => *x as u8,
|
||||||
|
_ => $stack.err(ErrorKind::InvalidType(
|
||||||
|
x.lock_ro().kind.lock_ro().get_name(),
|
||||||
|
"byte".to_owned(),
|
||||||
|
))?,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
Value::ByteArray(ref x) => x.clone(),
|
||||||
|
_ => return $stack.err(ErrorKind::InvalidCall($fn.to_owned())),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[macro_export]
|
||||||
macro_rules! require_mut_array_on_stack {
|
macro_rules! require_mut_array_on_stack {
|
||||||
($name:tt, $stack:expr, $fn:literal) => {
|
($name:tt, $stack:expr, $fn:literal) => {
|
||||||
let binding = $stack.pop();
|
let binding = $stack.pop();
|
||||||
|
|
|
@ -115,6 +115,7 @@ impl Runtime {
|
||||||
let _ = rt.make_type("func".to_owned(), Ok); // infallible
|
let _ = rt.make_type("func".to_owned(), Ok); // infallible
|
||||||
let _ = rt.make_type("array".to_owned(), Ok); // infallible
|
let _ = rt.make_type("array".to_owned(), Ok); // infallible
|
||||||
let _ = rt.make_type("str".to_owned(), Ok); // infallible
|
let _ = rt.make_type("str".to_owned(), Ok); // infallible
|
||||||
|
let _ = rt.make_type("bytearray".to_owned(), Ok); // infallible
|
||||||
stdlib::register(&mut rt);
|
stdlib::register(&mut rt);
|
||||||
rt
|
rt
|
||||||
}
|
}
|
||||||
|
@ -473,6 +474,9 @@ impl Stack {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(&mut self, func: &AFunc) -> OError {
|
pub fn call(&mut self, func: &AFunc) -> OError {
|
||||||
|
if func.origin.is_dummy() {
|
||||||
|
return self.fast_call(func);
|
||||||
|
}
|
||||||
let f = if let Some(ref cname) = func.fname {
|
let f = if let Some(ref cname) = func.fname {
|
||||||
Frame::new_in(
|
Frame::new_in(
|
||||||
func.origin.clone(),
|
func.origin.clone(),
|
||||||
|
@ -515,14 +519,13 @@ impl Stack {
|
||||||
frame = self.frames.first().unwrap().clone();
|
frame = self.frames.first().unwrap().clone();
|
||||||
}
|
}
|
||||||
let tmpname = name.clone();
|
let tmpname = name.clone();
|
||||||
let tmpframe = frame.clone();
|
|
||||||
frame.functions.lock().insert(
|
frame.functions.lock().insert(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
Arc::new(Func {
|
Arc::new(Func {
|
||||||
ret_count: 1,
|
ret_count: 1,
|
||||||
origin: frame.clone(),
|
origin: Arc::new(Frame::dummy()),
|
||||||
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
|
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
|
||||||
stack.push(tmpframe.get_var(tmpname.clone(), stack)?);
|
stack.push(stack.get_var(tmpname.clone())?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}))),
|
}))),
|
||||||
run_as_base: false,
|
run_as_base: false,
|
||||||
|
@ -531,15 +534,14 @@ impl Stack {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let tmpname = name.clone();
|
let tmpname = name.clone();
|
||||||
let tmpframe = frame.clone();
|
|
||||||
frame.functions.lock().insert(
|
frame.functions.lock().insert(
|
||||||
"=".to_owned() + &name,
|
"=".to_owned() + &name,
|
||||||
Arc::new(Func {
|
Arc::new(Func {
|
||||||
ret_count: 0,
|
ret_count: 0,
|
||||||
origin: frame.clone(),
|
origin: Arc::new(Frame::dummy()),
|
||||||
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
|
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
|
||||||
let v = stack.pop();
|
let v = stack.pop();
|
||||||
tmpframe.set_var(tmpname.clone(), v, stack)
|
stack.set_var(tmpname.clone(), v)
|
||||||
}))),
|
}))),
|
||||||
run_as_base: false,
|
run_as_base: false,
|
||||||
fname: Some("RUNTIME".to_owned()),
|
fname: Some("RUNTIME".to_owned()),
|
||||||
|
@ -568,11 +570,33 @@ impl Stack {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_var(&self, name: String, obj: AMObject) -> OError {
|
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> {
|
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) {
|
pub fn push(&mut self, obj: AMObject) {
|
||||||
|
@ -771,6 +795,7 @@ pub enum Value {
|
||||||
Double(f64),
|
Double(f64),
|
||||||
Func(AFunc),
|
Func(AFunc),
|
||||||
Array(Vec<AMObject>),
|
Array(Vec<AMObject>),
|
||||||
|
ByteArray(Vec<u8>),
|
||||||
Str(String),
|
Str(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1058,14 +1083,15 @@ impl Object {
|
||||||
pub fn is_truthy(&self) -> bool {
|
pub fn is_truthy(&self) -> bool {
|
||||||
match &self.native {
|
match &self.native {
|
||||||
Value::Null => self.kind.lock_ro().id != 0,
|
Value::Null => self.kind.lock_ro().id != 0,
|
||||||
Value::Int(x) => x > &0,
|
Value::Int(x) => *x > 0,
|
||||||
Value::Long(x) => x > &0,
|
Value::Long(x) => *x > 0,
|
||||||
Value::Mega(x) => x > &0,
|
Value::Mega(x) => *x > 0,
|
||||||
Value::Float(_) => true,
|
Value::Float(x) => x.is_finite(),
|
||||||
Value::Double(_) => true,
|
Value::Double(x) => x.is_finite(),
|
||||||
Value::Func(_) => true,
|
Value::Func(_) => true,
|
||||||
Value::Array(_) => true,
|
Value::Array(_) => true,
|
||||||
Value::Str(x) => !x.is_empty(),
|
Value::Str(x) => !x.is_empty(),
|
||||||
|
Value::ByteArray(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1125,6 +1151,7 @@ impl From<Value> for Object {
|
||||||
Value::Func(_) => x.get_type_by_id(6),
|
Value::Func(_) => x.get_type_by_id(6),
|
||||||
Value::Array(_) => x.get_type_by_id(7),
|
Value::Array(_) => x.get_type_by_id(7),
|
||||||
Value::Str(_) => x.get_type_by_id(8),
|
Value::Str(_) => x.get_type_by_id(8),
|
||||||
|
Value::ByteArray(_) => x.get_type_by_id(9),
|
||||||
}
|
}
|
||||||
.expect("runtime uninitialized: default types not set.")
|
.expect("runtime uninitialized: default types not set.")
|
||||||
}),
|
}),
|
||||||
|
|
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)),
|
"null" => words.push(Word::Const(Value::Null)),
|
||||||
"array" => panic!("invalid sasm const: array - not all Values can be consts!"),
|
"array" => panic!("invalid sasm const: array - not all Values can be consts!"),
|
||||||
|
"bytearray" => {
|
||||||
|
let mut array = Vec::new();
|
||||||
|
let inp = line[2].chars().collect::<Vec<_>>();
|
||||||
|
fn v(c: char) -> u8 {
|
||||||
|
if c > '0' && c <= '9' {
|
||||||
|
c as u8 - '0' as u8
|
||||||
|
} else if c > 'a' && c <= 'f' {
|
||||||
|
c as u8 - 'a' as u8
|
||||||
|
} else {
|
||||||
|
panic!("invalid sasm const: const bytearray [nonbytearray]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in (0..inp.len() / 2).map(|x| x * 2) {
|
||||||
|
let a = inp[i];
|
||||||
|
let b = inp[i + 1];
|
||||||
|
array.push(v(a) * 0x10 + v(b));
|
||||||
|
}
|
||||||
|
words.push(Word::Const(Value::ByteArray(array)));
|
||||||
|
}
|
||||||
_ => panic!("invalid sasm const: {}", line[1]),
|
_ => panic!("invalid sasm const: {}", line[1]),
|
||||||
},
|
},
|
||||||
"call" => {
|
"call" => {
|
||||||
|
@ -356,6 +375,25 @@ fn sasm_write_func(words: Words) -> String {
|
||||||
text.replace("\0", "\0\x01").replace("\n", "\0\0")
|
text.replace("\0", "\0\x01").replace("\n", "\0\0")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Value::ByteArray(b) => {
|
||||||
|
fn c(v: u8) -> char {
|
||||||
|
if v > 16 {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
(if v < 10 {
|
||||||
|
'0' as u8 + v
|
||||||
|
} else {
|
||||||
|
'a' as u8 + v - 10
|
||||||
|
}) as char
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut out = String::with_capacity(b.len() * 2);
|
||||||
|
for b in b {
|
||||||
|
out.push(c(b / 0x10));
|
||||||
|
out.push(c(b % 0x10));
|
||||||
|
}
|
||||||
|
output += &format!("const bytearray {out}\n");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Word::Call(name, rem, ra) => {
|
Word::Call(name, rem, ra) => {
|
||||||
output += "call ";
|
output += "call ";
|
||||||
|
|
347
src/std_fns.rs
347
src/std_fns.rs
|
@ -1,9 +1,8 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::{HashMap, VecDeque},
|
||||||
env::{args, vars},
|
env::{self, args, vars},
|
||||||
fs,
|
fs,
|
||||||
io::{stdin, stdout, Write},
|
io::{stdin, stdout, Write},
|
||||||
mem,
|
|
||||||
ops::{Add, Div, Mul, Rem, Sub},
|
ops::{Add, Div, Mul, Rem, Sub},
|
||||||
process::{self, Stdio},
|
process::{self, Stdio},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
@ -96,12 +95,27 @@ pub fn settype(stack: &mut Stack) -> OError {
|
||||||
let o = stack.pop();
|
let o = stack.pop();
|
||||||
let kind = runtime(|rt| rt.get_type_by_name(&s))
|
let kind = runtime(|rt| rt.get_type_by_name(&s))
|
||||||
.ok_or_else(|| stack.error(ErrorKind::TypeNotFound(s)))?;
|
.ok_or_else(|| stack.error(ErrorKind::TypeNotFound(s)))?;
|
||||||
|
set_type_internal(&o, kind);
|
||||||
|
stack.push(o);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn settypeid(stack: &mut Stack) -> OError {
|
||||||
|
let Value::Int(i) = stack.pop().lock_ro().native.clone() else {
|
||||||
|
return stack.err(ErrorKind::InvalidCall("settype".to_owned()));
|
||||||
|
};
|
||||||
|
let o = stack.pop();
|
||||||
|
let kind = runtime(|rt| rt.get_type_by_id(i as u32))
|
||||||
|
.ok_or_else(|| stack.error(ErrorKind::TypeNotFound(format!(";{i}"))))?;
|
||||||
|
set_type_internal(&o, kind);
|
||||||
|
stack.push(o);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_type_internal(o: &Arc<Mut<Object>>, kind: Arc<Mut<Type>>) {
|
||||||
let mut obj = o.lock();
|
let mut obj = o.lock();
|
||||||
kind.lock_ro().write_into(&mut obj);
|
kind.lock_ro().write_into(&mut obj);
|
||||||
obj.kind = kind;
|
obj.kind = kind;
|
||||||
mem::drop(obj);
|
|
||||||
stack.push(o);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gettype(stack: &mut Stack) -> OError {
|
pub fn gettype(stack: &mut Stack) -> OError {
|
||||||
|
@ -110,6 +124,23 @@ pub fn gettype(stack: &mut Stack) -> OError {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gettypeid(stack: &mut Stack) -> OError {
|
||||||
|
let o = stack.pop();
|
||||||
|
stack.push(Value::Int(o.lock_ro().kind.lock_ro().get_id() as i32).spl());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn barray_new(stack: &mut Stack) -> OError {
|
||||||
|
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
|
||||||
|
return stack.err(ErrorKind::InvalidCall("banew".to_owned()));
|
||||||
|
};
|
||||||
|
if i < 0 {
|
||||||
|
return stack.err(ErrorKind::InvalidCall("banew".to_owned()));
|
||||||
|
}
|
||||||
|
stack.push(Value::ByteArray(vec![0u8; i as usize]).spl());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn array_new(stack: &mut Stack) -> OError {
|
pub fn array_new(stack: &mut Stack) -> OError {
|
||||||
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
|
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
|
||||||
return stack.err(ErrorKind::InvalidCall("anew".to_owned()));
|
return stack.err(ErrorKind::InvalidCall("anew".to_owned()));
|
||||||
|
@ -123,36 +154,46 @@ pub fn array_new(stack: &mut Stack) -> OError {
|
||||||
|
|
||||||
pub fn array_len(stack: &mut Stack) -> OError {
|
pub fn array_len(stack: &mut Stack) -> OError {
|
||||||
let binding = stack.pop();
|
let binding = stack.pop();
|
||||||
let Value::Array(ref a) = binding.lock_ro().native else {
|
let len = match binding.lock_ro().native {
|
||||||
return stack.err(ErrorKind::InvalidCall("array-len".to_owned()));
|
Value::Array(ref a) => a.len(),
|
||||||
|
Value::ByteArray(ref a) => a.len(),
|
||||||
|
_ => return stack.err(ErrorKind::InvalidCall("array-len".to_owned())),
|
||||||
};
|
};
|
||||||
stack.push(Value::Mega(a.len() as i128).spl());
|
stack.push(Value::Mega(len as i128).spl());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn array_get(stack: &mut Stack) -> OError {
|
pub fn array_get(stack: &mut Stack) -> OError {
|
||||||
let binding = stack.pop();
|
let binding = stack.pop();
|
||||||
let Value::Array(ref a) = binding.lock_ro().native else {
|
let Value::Mega(i) = stack.pop().lock_ro().native else {
|
||||||
return stack.err(ErrorKind::InvalidCall("array-get".to_owned()));
|
return stack.err(ErrorKind::InvalidCall("array-get".to_owned()));
|
||||||
};
|
};
|
||||||
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
|
let o = match binding.lock_ro().native {
|
||||||
return stack.err(ErrorKind::InvalidCall("array-get".to_owned()));
|
Value::Array(ref a) => a.get(i as usize).cloned(),
|
||||||
|
Value::ByteArray(ref a) => a.get(i as usize).map(|x| Value::Int(*x as i32).spl()),
|
||||||
|
_ => return stack.err(ErrorKind::InvalidCall("array-get".to_owned())),
|
||||||
};
|
};
|
||||||
stack.push(a.get(i as usize).ok_or_else(array!(stack, i))?.clone());
|
stack.push(o.ok_or_else(array!(stack, i))?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn array_set(stack: &mut Stack) -> OError {
|
pub fn array_set(stack: &mut Stack) -> OError {
|
||||||
let binding = stack.pop();
|
let binding = &stack.pop();
|
||||||
let Value::Array(ref mut a) = binding.lock().native else {
|
let binding = &mut binding.lock().native;
|
||||||
return stack.err(ErrorKind::InvalidCall("array-set".to_owned()));
|
require_on_stack!(i, Mega, stack, "array-set");
|
||||||
};
|
|
||||||
let Value::Mega(i) = stack.pop().lock_ro().native.clone() else {
|
|
||||||
return stack.err(ErrorKind::InvalidCall("array-set".to_owned()));
|
|
||||||
};
|
|
||||||
let o = stack.pop();
|
let o = stack.pop();
|
||||||
stack.push(a.get(i as usize).ok_or_else(array!(stack, i))?.clone());
|
if let Value::Array(ref mut a) = binding {
|
||||||
*a.get_mut(i as usize).ok_or_else(array!(stack, i))? = o;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,6 +268,8 @@ pub fn plus(stack: &mut Stack) -> OError {
|
||||||
Mega,
|
Mega,
|
||||||
Long,
|
Long,
|
||||||
Int,
|
Int,
|
||||||
|
Float,
|
||||||
|
Double,
|
||||||
)
|
)
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -245,6 +288,8 @@ pub fn minus(stack: &mut Stack) -> OError {
|
||||||
Mega,
|
Mega,
|
||||||
Long,
|
Long,
|
||||||
Int,
|
Int,
|
||||||
|
Float,
|
||||||
|
Double,
|
||||||
)
|
)
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -263,6 +308,8 @@ pub fn slash(stack: &mut Stack) -> OError {
|
||||||
Mega,
|
Mega,
|
||||||
Long,
|
Long,
|
||||||
Int,
|
Int,
|
||||||
|
Float,
|
||||||
|
Double,
|
||||||
)
|
)
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -281,6 +328,8 @@ pub fn star(stack: &mut Stack) -> OError {
|
||||||
Mega,
|
Mega,
|
||||||
Long,
|
Long,
|
||||||
Int,
|
Int,
|
||||||
|
Float,
|
||||||
|
Double,
|
||||||
)
|
)
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -295,7 +344,7 @@ pub fn percent(stack: &mut Stack) -> OError {
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
rem,
|
rem,
|
||||||
stack.err(ErrorKind::InvalidCall("star".to_owned())),
|
stack.err(ErrorKind::InvalidCall("percent".to_owned())),
|
||||||
Mega,
|
Mega,
|
||||||
Long,
|
Long,
|
||||||
Int,
|
Int,
|
||||||
|
@ -320,6 +369,7 @@ pub fn to_int(stack: &mut Stack) -> OError {
|
||||||
Value::Str(x) => x
|
Value::Str(x) => x
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "int".to_owned())))?,
|
.map_err(|_| stack.error(ErrorKind::Parse(x, "int".to_owned())))?,
|
||||||
|
Value::ByteArray(x) => x.len() as i32,
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -341,6 +391,7 @@ pub fn to_long(stack: &mut Stack) -> OError {
|
||||||
Value::Str(x) => x
|
Value::Str(x) => x
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "long".to_owned())))?,
|
.map_err(|_| stack.error(ErrorKind::Parse(x, "long".to_owned())))?,
|
||||||
|
Value::ByteArray(x) => x.len() as i64,
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -362,6 +413,7 @@ pub fn to_mega(stack: &mut Stack) -> OError {
|
||||||
Value::Str(x) => x
|
Value::Str(x) => x
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "mega".to_owned())))?,
|
.map_err(|_| stack.error(ErrorKind::Parse(x, "mega".to_owned())))?,
|
||||||
|
Value::ByteArray(x) => x.len() as i128,
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -383,6 +435,7 @@ pub fn to_float(stack: &mut Stack) -> OError {
|
||||||
Value::Str(x) => x
|
Value::Str(x) => x
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "float".to_owned())))?,
|
.map_err(|_| stack.error(ErrorKind::Parse(x, "float".to_owned())))?,
|
||||||
|
Value::ByteArray(_) => type_err!(stack, "bytearray", "float"),
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -404,6 +457,7 @@ pub fn to_double(stack: &mut Stack) -> OError {
|
||||||
Value::Str(x) => x
|
Value::Str(x) => x
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| stack.error(ErrorKind::Parse(x, "double".to_owned())))?,
|
.map_err(|_| stack.error(ErrorKind::Parse(x, "double".to_owned())))?,
|
||||||
|
Value::ByteArray(_) => type_err!(stack, "bytearray", "double"),
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -426,6 +480,11 @@ pub fn to_array(stack: &mut Stack) -> OError {
|
||||||
.chars()
|
.chars()
|
||||||
.map(|x| Value::Int(x as u32 as i32).spl())
|
.map(|x| Value::Int(x as u32 as i32).spl())
|
||||||
.collect(),
|
.collect(),
|
||||||
|
Value::ByteArray(x) => x
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|x| Value::Int(x as i32).spl())
|
||||||
|
.collect(),
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -466,6 +525,46 @@ pub fn to_str(stack: &mut Stack) -> OError {
|
||||||
fixed
|
fixed
|
||||||
}
|
}
|
||||||
Value::Str(x) => x,
|
Value::Str(x) => x,
|
||||||
|
Value::ByteArray(x) => String::from_utf8(x).map_err(|_| {
|
||||||
|
stack.error(ErrorKind::InvalidType(
|
||||||
|
"!utf8".to_owned(),
|
||||||
|
"utf8".to_owned(),
|
||||||
|
))
|
||||||
|
})?,
|
||||||
|
})
|
||||||
|
.spl(),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bytearray(stack: &mut Stack) -> OError {
|
||||||
|
let o = stack.pop().lock_ro().native.clone();
|
||||||
|
stack.push(
|
||||||
|
Value::ByteArray(match o {
|
||||||
|
Value::Null => type_err!(stack, "null", "array"),
|
||||||
|
Value::Int(_) => type_err!(stack, "int", "array"),
|
||||||
|
Value::Long(_) => type_err!(stack, "long", "array"),
|
||||||
|
Value::Mega(_) => type_err!(stack, "mega", "array"),
|
||||||
|
Value::Float(_) => type_err!(stack, "float", "array"),
|
||||||
|
Value::Double(_) => type_err!(stack, "double", "array"),
|
||||||
|
Value::Func(_) => type_err!(stack, "func", "array"),
|
||||||
|
Value::Array(x) => x
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|x| {
|
||||||
|
Ok(match &x.lock_ro().native {
|
||||||
|
Value::Int(x) => *x as u8,
|
||||||
|
Value::Long(x) => *x as u8,
|
||||||
|
Value::Mega(x) => *x as u8,
|
||||||
|
_ => stack.err(ErrorKind::InvalidType(
|
||||||
|
x.lock_ro().kind.lock_ro().get_name(),
|
||||||
|
"byte".to_owned(),
|
||||||
|
))?,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
Value::Str(x) => x.into_bytes(),
|
||||||
|
Value::ByteArray(x) => x,
|
||||||
})
|
})
|
||||||
.spl(),
|
.spl(),
|
||||||
);
|
);
|
||||||
|
@ -605,6 +704,7 @@ pub fn alit_end(stack: &mut Stack) -> OError {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: rewrite
|
||||||
pub fn import(stack: &mut Stack) -> OError {
|
pub fn import(stack: &mut Stack) -> OError {
|
||||||
let Value::Str(mut s) = stack.pop().lock_ro().native.clone() else {
|
let Value::Str(mut s) = stack.pop().lock_ro().native.clone() else {
|
||||||
return stack.err(ErrorKind::InvalidCall("import".to_owned()));
|
return stack.err(ErrorKind::InvalidCall("import".to_owned()));
|
||||||
|
@ -712,17 +812,31 @@ pub fn command(stack: &mut Stack) -> OError {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
|
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
|
||||||
}
|
}
|
||||||
process::Command::new(&args[0])
|
stack.push(
|
||||||
.args(&args[1..])
|
Value::Long(
|
||||||
.stdin(Stdio::inherit())
|
process::Command::new(&args[0])
|
||||||
.stdout(Stdio::inherit())
|
.args(&args[1..])
|
||||||
.stderr(Stdio::inherit())
|
.stdin(Stdio::null())
|
||||||
.spawn()
|
.stdout(Stdio::null())
|
||||||
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?;
|
.stderr(Stdio::null())
|
||||||
|
.spawn()
|
||||||
|
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?
|
||||||
|
.id() as i64,
|
||||||
|
)
|
||||||
|
.spl(),
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn command_wait(stack: &mut Stack) -> OError {
|
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 binding = stack.pop();
|
||||||
let Value::Array(ref a) = binding.lock_ro().native else {
|
let Value::Array(ref a) = binding.lock_ro().native else {
|
||||||
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
|
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
|
||||||
|
@ -742,9 +856,9 @@ pub fn command_wait(stack: &mut Stack) -> OError {
|
||||||
Value::Int(
|
Value::Int(
|
||||||
process::Command::new(&args[0])
|
process::Command::new(&args[0])
|
||||||
.args(&args[1..])
|
.args(&args[1..])
|
||||||
.stdin(Stdio::inherit())
|
.stdin(stdio())
|
||||||
.stdout(Stdio::inherit())
|
.stdout(stdio())
|
||||||
.stderr(Stdio::inherit())
|
.stderr(stdio())
|
||||||
.spawn()
|
.spawn()
|
||||||
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?
|
.map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?
|
||||||
.wait()
|
.wait()
|
||||||
|
@ -759,29 +873,18 @@ pub fn command_wait(stack: &mut Stack) -> OError {
|
||||||
|
|
||||||
pub fn str_to_bytes(stack: &mut Stack) -> OError {
|
pub fn str_to_bytes(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(s, Str, stack, "str-to-bytes");
|
require_on_stack!(s, Str, stack, "str-to-bytes");
|
||||||
stack.push(
|
stack.push(Value::ByteArray(s.bytes().collect()).spl());
|
||||||
Value::Array(
|
|
||||||
s.bytes()
|
|
||||||
.into_iter()
|
|
||||||
.map(|x| Value::Int(x as i32).spl())
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
.spl(),
|
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bytes_to_str(stack: &mut Stack) -> OError {
|
pub fn bytes_to_str(stack: &mut Stack) -> OError {
|
||||||
require_array_on_stack!(a, stack, "str-to-bytes");
|
if stack.peek().lock_ro().kind.lock_ro().get_name() == "bytearray" {
|
||||||
let mut chars = Vec::new();
|
require_on_stack!(a, ByteArray, stack, "bytes-to-str");
|
||||||
for item in a.iter() {
|
stack.push(Value::Str(String::from_utf8_lossy(&a[..]).into_owned()).spl());
|
||||||
if let Value::Int(x) = item.lock_ro().native.clone().try_mega_to_int() {
|
return Ok(());
|
||||||
chars.push(x as u8);
|
|
||||||
} else {
|
|
||||||
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -790,7 +893,8 @@ pub fn acopy(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(idx_dest, Mega, stack, "acopy");
|
require_on_stack!(idx_dest, Mega, stack, "acopy");
|
||||||
require_on_stack!(idx_src, Mega, stack, "acopy");
|
require_on_stack!(idx_src, Mega, stack, "acopy");
|
||||||
let dest_array = stack.pop();
|
let dest_array = stack.pop();
|
||||||
{
|
let kind = dest_array.lock_ro().kind.lock_ro().get_name();
|
||||||
|
if kind == "array" {
|
||||||
require_mut_array!(dest, dest_array, stack, "acopy");
|
require_mut_array!(dest, dest_array, stack, "acopy");
|
||||||
require_array_on_stack!(src, stack, "acopy");
|
require_array_on_stack!(src, stack, "acopy");
|
||||||
let offset = idx_dest - idx_src;
|
let offset = idx_dest - idx_src;
|
||||||
|
@ -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();
|
*dest.get_mut((i + offset) as usize).unwrap() = src.get(i as usize).unwrap().clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if kind == "bytearray" {
|
||||||
|
require_mut!(dest, ByteArray, dest_array, stack, "acopy");
|
||||||
|
require_byte_array_on_stack!(src, stack, "acopy");
|
||||||
|
let len = len as usize;
|
||||||
|
let idx_src = idx_src as usize;
|
||||||
|
let idx_dest = idx_dest as usize;
|
||||||
|
(&mut dest[idx_dest..idx_dest + len]).clone_from_slice(&src[idx_src..idx_src + len]);
|
||||||
|
}
|
||||||
stack.push(dest_array);
|
stack.push(dest_array);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -906,7 +1018,8 @@ pub fn str_to_mega_radix(stack: &mut Stack) -> OError {
|
||||||
pub fn mega_to_str_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_int_on_stack!(radix, stack, "mega-to-str-radix");
|
||||||
require_on_stack!(mega, Mega, 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 neg = mega < 0;
|
||||||
let mut mega = mega;
|
let mut mega = mega;
|
||||||
if neg {
|
if neg {
|
||||||
|
@ -930,9 +1043,122 @@ pub fn mega_to_str_radix(stack: &mut Stack) -> OError {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn properties(stack: &mut Stack) -> OError {
|
||||||
|
let o = stack.pop();
|
||||||
|
let o = o.lock_ro();
|
||||||
|
let additional: Vec<AMObject> = vec![
|
||||||
|
Value::Array(vec![
|
||||||
|
":".to_owned().spl(),
|
||||||
|
o.kind.lock_ro().get_name().spl(),
|
||||||
|
])
|
||||||
|
.spl(),
|
||||||
|
Value::Array(vec![";".to_owned().spl(), o.native.clone().spl()]).spl(),
|
||||||
|
];
|
||||||
|
stack.push(
|
||||||
|
Value::Array(
|
||||||
|
o.property_map
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| Value::Array(vec![k.clone().spl(), v.clone()]).spl())
|
||||||
|
.chain(additional.into_iter())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
.spl(),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_properties(stack: &mut Stack) -> OError {
|
||||||
|
require_array_on_stack!(props, stack, "from-properties");
|
||||||
|
let mut map = HashMap::with_capacity(props.len());
|
||||||
|
for prop in props {
|
||||||
|
require_array!(prop, prop, stack, "from-properties");
|
||||||
|
if prop.len() != 2 {
|
||||||
|
stack.err(ErrorKind::InvalidCall("from-properties".to_string()))?;
|
||||||
|
}
|
||||||
|
let Value::Str(ref s) = prop[0].lock_ro().native else {
|
||||||
|
return Err(stack.error(ErrorKind::InvalidCall("from-properties".to_string())));
|
||||||
|
};
|
||||||
|
map.insert(s.to_owned(), prop[1].clone());
|
||||||
|
}
|
||||||
|
let Value::Str(kind) = map
|
||||||
|
.get(":")
|
||||||
|
.ok_or(stack.error(ErrorKind::InvalidCall("from-properties".to_string())))?
|
||||||
|
.lock_ro()
|
||||||
|
.native
|
||||||
|
.clone()
|
||||||
|
else {
|
||||||
|
return Err(stack.error(ErrorKind::InvalidCall("from-properties".to_string())));
|
||||||
|
};
|
||||||
|
let kind = runtime(|rt| rt.get_type_by_name(&kind))
|
||||||
|
.ok_or(stack.error(ErrorKind::TypeNotFound(kind.to_owned())))?;
|
||||||
|
let native = map
|
||||||
|
.get(";")
|
||||||
|
.ok_or(stack.error(ErrorKind::InvalidCall("from-properties".to_owned())))?
|
||||||
|
.lock_ro()
|
||||||
|
.native
|
||||||
|
.clone();
|
||||||
|
map.remove(";");
|
||||||
|
map.remove(":");
|
||||||
|
stack.push(Arc::new(Mut::new(Object {
|
||||||
|
kind,
|
||||||
|
native,
|
||||||
|
property_map: map,
|
||||||
|
})));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_files(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(dir, Str, stack, "list-files");
|
||||||
|
stack.push(
|
||||||
|
match fs::read_dir(&dir)
|
||||||
|
.map_err(|_| stack.error(ErrorKind::IO(format!("Not a directory: {}", &dir))))
|
||||||
|
{
|
||||||
|
Ok(it) => Value::Array(
|
||||||
|
it.filter(|x| x.is_ok())
|
||||||
|
.map(|x| {
|
||||||
|
if let Ok(x) = x {
|
||||||
|
Value::Str(x.file_name().to_string_lossy().into_owned()).spl()
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
.spl(),
|
||||||
|
Err(_) => Value::Null.spl(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_file(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(file, Str, stack, "delete-file");
|
||||||
|
stack.push(Value::Int(if fs::remove_file(file).is_ok() { 1 } else { 0 }).spl());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_dir(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(dir, Str, stack, "delete-dir");
|
||||||
|
stack.push(
|
||||||
|
Value::Int(if fs::remove_dir_all(dir).is_ok() {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
})
|
||||||
|
.spl(),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chdir(stack: &mut Stack) -> OError {
|
||||||
|
require_on_stack!(dir, Str, stack, "chdir");
|
||||||
|
env::set_current_dir(dir).map_err(|e| stack.error(ErrorKind::IO(e.to_string())))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
||||||
type Fn = fn(&mut Stack) -> OError;
|
type Fn = fn(&mut Stack) -> OError;
|
||||||
let fns: [(&str, Fn, u32); 59] = [
|
let fns: [(&str, Fn, u32); 70] = [
|
||||||
("pop", pop, 0),
|
("pop", pop, 0),
|
||||||
("dup", dup, 2),
|
("dup", dup, 2),
|
||||||
("dup2", dup2, 3),
|
("dup2", dup2, 3),
|
||||||
|
@ -941,7 +1167,10 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
||||||
("mswap", mswap, 2),
|
("mswap", mswap, 2),
|
||||||
("print", print, 0),
|
("print", print, 0),
|
||||||
("gettype", gettype, 1),
|
("gettype", gettype, 1),
|
||||||
|
("gettypeid", gettypeid, 1),
|
||||||
("settype", settype, 1),
|
("settype", settype, 1),
|
||||||
|
("settypeid", settypeid, 1),
|
||||||
|
("banew", barray_new, 1),
|
||||||
("anew", array_new, 1),
|
("anew", array_new, 1),
|
||||||
("array-len", array_len, 1),
|
("array-len", array_len, 1),
|
||||||
("array-get", array_get, 1),
|
("array-get", array_get, 1),
|
||||||
|
@ -964,6 +1193,7 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
||||||
("_double", to_double, 1),
|
("_double", to_double, 1),
|
||||||
("_array", to_array, 1),
|
("_array", to_array, 1),
|
||||||
("_str", to_str, 1),
|
("_str", to_str, 1),
|
||||||
|
("_barray", to_bytearray, 1),
|
||||||
("call", call, 0),
|
("call", call, 0),
|
||||||
("callp", callp, 0),
|
("callp", callp, 0),
|
||||||
("trace", trace, 1),
|
("trace", trace, 1),
|
||||||
|
@ -979,8 +1209,9 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
||||||
("alit-end", alit_end, 1),
|
("alit-end", alit_end, 1),
|
||||||
("import", import, 0),
|
("import", import, 0),
|
||||||
("readln", readln, 1),
|
("readln", readln, 1),
|
||||||
("command", command, 0),
|
("command", command, 1),
|
||||||
("command-wait", command_wait, 1),
|
("command-wait", command_wait, 1),
|
||||||
|
("command-wait-silent", command_wait_silent, 1),
|
||||||
("str-to-bytes", str_to_bytes, 1),
|
("str-to-bytes", str_to_bytes, 1),
|
||||||
("bytes-to-str", bytes_to_str, 1),
|
("bytes-to-str", bytes_to_str, 1),
|
||||||
("acopy", acopy, 1),
|
("acopy", acopy, 1),
|
||||||
|
@ -992,6 +1223,12 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
||||||
("str-readf", str_readf, 1),
|
("str-readf", str_readf, 1),
|
||||||
("str-to-mega-radix", str_to_mega_radix, 1),
|
("str-to-mega-radix", str_to_mega_radix, 1),
|
||||||
("mega-to-str-radix", mega_to_str_radix, 1),
|
("mega-to-str-radix", mega_to_str_radix, 1),
|
||||||
|
("properties", properties, 1),
|
||||||
|
("from-properties", from_properties, 1),
|
||||||
|
("list-files", list_files, 1),
|
||||||
|
("delete-file", delete_file, 1),
|
||||||
|
("delete-dir", delete_dir, 1),
|
||||||
|
("chdir", chdir, 0),
|
||||||
];
|
];
|
||||||
for f in fns {
|
for f in fns {
|
||||||
r.define_func(
|
r.define_func(
|
||||||
|
|
|
@ -10,11 +10,14 @@ pub const MESSAGING: &str = include_str!("../spl/messaging.spl");
|
||||||
pub const ASSEMBLE: &str = include_str!("../spl/assemble.spl");
|
pub const ASSEMBLE: &str = include_str!("../spl/assemble.spl");
|
||||||
pub const ISBPL: &str = include_str!("../spl/isbpl.spl");
|
pub const ISBPL: &str = include_str!("../spl/isbpl.spl");
|
||||||
pub const REPL: &str = include_str!("../spl/repl.spl");
|
pub const REPL: &str = include_str!("../spl/repl.spl");
|
||||||
|
pub const FAST: &str = include_str!("../spl/fast.spl");
|
||||||
pub const PURE: &str = include_str!("../spl/pure.spl");
|
pub const PURE: &str = include_str!("../spl/pure.spl");
|
||||||
pub const TIME: &str = include_str!("../spl/time.spl");
|
pub const TIME: &str = include_str!("../spl/time.spl");
|
||||||
pub const SERVER: &str = include_str!("../spl/server.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: &str = include_str!("../spl/httpserver/base.spl");
|
||||||
pub const HTTP_SERVER_STATIC: &str = include_str!("../spl/httpserver/static.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 const NOP: &str = "";
|
||||||
|
|
||||||
pub fn register(runtime: &mut Runtime) {
|
pub fn register(runtime: &mut Runtime) {
|
||||||
|
@ -29,11 +32,14 @@ pub fn register(runtime: &mut Runtime) {
|
||||||
insert("assemble.spl", ASSEMBLE);
|
insert("assemble.spl", ASSEMBLE);
|
||||||
insert("isbpl.spl", ISBPL);
|
insert("isbpl.spl", ISBPL);
|
||||||
insert("repl.spl", REPL);
|
insert("repl.spl", REPL);
|
||||||
|
insert("fast.spl", FAST);
|
||||||
insert("pure.spl", PURE);
|
insert("pure.spl", PURE);
|
||||||
insert("time.spl", TIME);
|
insert("time.spl", TIME);
|
||||||
insert("server.spl", SERVER);
|
insert("server.spl", SERVER);
|
||||||
insert("httpserver/base.spl", HTTP_SERVER);
|
insert("httpserver/base.spl", HTTP_SERVER);
|
||||||
insert("httpserver/static.spl", HTTP_SERVER_STATIC);
|
insert("httpserver/static.spl", HTTP_SERVER_STATIC);
|
||||||
|
insert("linkedlist.spl", LINKEDLIST);
|
||||||
|
insert("json.spl", JSON);
|
||||||
insert("nop.spl", NOP);
|
insert("nop.spl", NOP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Fn = fn(&mut Stack) -> OError;
|
type Fn = fn(&mut Stack) -> OError;
|
||||||
let fns: [(&str, Fn, u32); 11] = [
|
let fns: [(&str, Fn, u32); 12] = [
|
||||||
("new-stream", new_stream, 1),
|
("new-stream", new_stream, 1),
|
||||||
("write-stream", write_stream, 1),
|
("write-stream", write_stream, 1),
|
||||||
("write-all-stream", write_all_stream, 0),
|
("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),
|
("accept-server-stream", accept_server_stream, 1),
|
||||||
("close-server-stream", close_server_stream, 0),
|
("close-server-stream", close_server_stream, 0),
|
||||||
("get-stream-peer", get_stream_peer, 2),
|
("get-stream-peer", get_stream_peer, 2),
|
||||||
|
("shutdown-input-stream", shutdown_input_stream, 0),
|
||||||
];
|
];
|
||||||
for f in fns {
|
for f in fns {
|
||||||
r.define_func(
|
r.define_func(
|
||||||
|
|
|
@ -86,6 +86,16 @@ impl Stream {
|
||||||
f(&mut self.extra);
|
f(&mut self.extra);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn shutdown_write(&mut self) {
|
||||||
|
let mut bx = Box::new(IgnoreWrite());
|
||||||
|
self.writer = unsafe {
|
||||||
|
(bx.as_mut() as *mut (dyn Write + Send + Sync + 'static))
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
self._writer_storage = Some(bx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Read for Stream {
|
impl Read for Stream {
|
||||||
|
@ -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
|
impl<T> From<T> for StreamType
|
||||||
where
|
where
|
||||||
T: Fn(&mut Stack) -> Result<Stream, Error> + Sync + Send + 'static,
|
T: Fn(&mut Stack) -> Result<Stream, Error> + Sync + Send + 'static,
|
||||||
|
@ -155,23 +176,16 @@ pub fn new_stream(stack: &mut Stack) -> OError {
|
||||||
|
|
||||||
pub fn write_stream(stack: &mut Stack) -> OError {
|
pub fn write_stream(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(id, Mega, stack, "write-stream");
|
require_on_stack!(id, Mega, stack, "write-stream");
|
||||||
require_array_on_stack!(a, stack, "write-stream");
|
require_byte_array_on_stack!(a, stack, "write-stream");
|
||||||
let stream = runtime(|rt| {
|
let stream = runtime(|rt| {
|
||||||
rt.get_stream(id as u128)
|
rt.get_stream(id as u128)
|
||||||
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
||||||
})?;
|
})?;
|
||||||
let mut fixed = Vec::with_capacity(a.len());
|
|
||||||
for item in a.iter() {
|
|
||||||
match item.lock_ro().native {
|
|
||||||
Value::Int(x) => fixed.push(x as u8),
|
|
||||||
_ => type_err!(stack, "!int", "int"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stack.push(
|
stack.push(
|
||||||
Value::Mega(
|
Value::Mega(
|
||||||
stream
|
stream
|
||||||
.lock()
|
.lock()
|
||||||
.write(&fixed[..])
|
.write(&a)
|
||||||
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))? as i128,
|
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))? as i128,
|
||||||
)
|
)
|
||||||
.spl(),
|
.spl(),
|
||||||
|
@ -182,21 +196,14 @@ pub fn write_stream(stack: &mut Stack) -> OError {
|
||||||
|
|
||||||
pub fn write_all_stream(stack: &mut Stack) -> OError {
|
pub fn write_all_stream(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(id, Mega, stack, "write-all-stream");
|
require_on_stack!(id, Mega, stack, "write-all-stream");
|
||||||
require_array_on_stack!(a, stack, "write-all-stream");
|
require_byte_array_on_stack!(a, stack, "write-all-stream");
|
||||||
let stream = runtime(|rt| {
|
let stream = runtime(|rt| {
|
||||||
rt.get_stream(id as u128)
|
rt.get_stream(id as u128)
|
||||||
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
.ok_or_else(|| stack.error(ErrorKind::VariableNotFound(format!("__stream-{id}"))))
|
||||||
})?;
|
})?;
|
||||||
let mut fixed = Vec::with_capacity(a.len());
|
|
||||||
for item in a.iter() {
|
|
||||||
match item.lock_ro().native {
|
|
||||||
Value::Int(x) => fixed.push(x as u8),
|
|
||||||
_ => type_err!(stack, "!int", "int"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stream
|
stream
|
||||||
.lock()
|
.lock()
|
||||||
.write_all(&fixed[..])
|
.write_all(&a)
|
||||||
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?;
|
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?;
|
||||||
black_box(&stream.lock_ro()._writer_storage);
|
black_box(&stream.lock_ro()._writer_storage);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -219,12 +226,13 @@ pub fn flush_stream(stack: &mut Stack) -> OError {
|
||||||
pub fn read_stream(stack: &mut Stack) -> OError {
|
pub fn read_stream(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(id, Mega, stack, "read-stream");
|
require_on_stack!(id, Mega, stack, "read-stream");
|
||||||
let array = stack.pop();
|
let array = stack.pop();
|
||||||
{
|
let kind = array.lock_ro().kind.lock_ro().get_name();
|
||||||
|
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");
|
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()];
|
let mut vec = vec![0; a.len()];
|
||||||
stack.push(
|
stack.push(
|
||||||
Value::Mega(
|
Value::Mega(
|
||||||
|
@ -242,6 +250,19 @@ pub fn read_stream(stack: &mut Stack) -> OError {
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if kind == "bytearray" {
|
||||||
|
require_mut!(a, ByteArray, array, stack, "read-stream");
|
||||||
|
stack.push(
|
||||||
|
Value::Mega(
|
||||||
|
stream
|
||||||
|
.lock()
|
||||||
|
.read(a)
|
||||||
|
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?
|
||||||
|
as i128,
|
||||||
|
)
|
||||||
|
.spl(),
|
||||||
|
);
|
||||||
|
}
|
||||||
stack.push(array);
|
stack.push(array);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -249,12 +270,13 @@ pub fn read_stream(stack: &mut Stack) -> OError {
|
||||||
pub fn read_all_stream(stack: &mut Stack) -> OError {
|
pub fn read_all_stream(stack: &mut Stack) -> OError {
|
||||||
require_on_stack!(id, Mega, stack, "read-all-stream");
|
require_on_stack!(id, Mega, stack, "read-all-stream");
|
||||||
let array = stack.pop();
|
let array = stack.pop();
|
||||||
{
|
let kind = array.lock_ro().kind.lock_ro().get_name();
|
||||||
|
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");
|
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()];
|
let mut vec = vec![0; a.len()];
|
||||||
stream
|
stream
|
||||||
.lock()
|
.lock()
|
||||||
|
@ -266,6 +288,13 @@ pub fn read_all_stream(stack: &mut Stack) -> OError {
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if kind == "bytearray" {
|
||||||
|
require_mut!(a, ByteArray, array, stack, "read-stream");
|
||||||
|
stream
|
||||||
|
.lock()
|
||||||
|
.read_exact(a)
|
||||||
|
.map_err(|x| stack.error(ErrorKind::IO(format!("{x:?}"))))?;
|
||||||
|
}
|
||||||
stack.push(array);
|
stack.push(array);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -276,6 +305,16 @@ pub fn close_stream(stack: &mut Stack) -> OError {
|
||||||
Ok(())
|
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> {
|
pub(super) fn stream_file(stack: &mut Stack) -> Result<Stream, Error> {
|
||||||
let truncate = stack.pop().lock_ro().is_truthy();
|
let truncate = stack.pop().lock_ro().is_truthy();
|
||||||
require_on_stack!(path, Str, stack, "FILE new-stream");
|
require_on_stack!(path, Str, stack, "FILE new-stream");
|
||||||
|
@ -335,7 +374,7 @@ pub(super) fn stream_cmd(stack: &mut Stack) -> Result<Stream, Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return stack.err(ErrorKind::InvalidCall("command".to_owned()));
|
return stack.err(ErrorKind::InvalidCall("CMD new-stream".to_owned()));
|
||||||
}
|
}
|
||||||
let mut command = process::Command::new(&args[0])
|
let mut command = process::Command::new(&args[0])
|
||||||
.args(&args[1..])
|
.args(&args[1..])
|
||||||
|
|
120
test.spl
120
test.spl
|
@ -1,10 +1,12 @@
|
||||||
[
|
[
|
||||||
"#stream.spl" import
|
"spl/stream.spl" import
|
||||||
"#http.spl" import
|
"spl/http.spl" import
|
||||||
"#messaging.spl" import
|
"spl/messaging.spl" import
|
||||||
"#server.spl" import
|
"spl/server.spl" import
|
||||||
"#time.spl" import
|
"spl/time.spl" import
|
||||||
"#httpserver/base.spl" import
|
"spl/httpserver/base.spl" import
|
||||||
|
"spl/linkedlist.spl" import
|
||||||
|
"spl/json.spl" import
|
||||||
|
|
||||||
|
|
||||||
"SPL tester" =program-name
|
"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;
|
"hello! this is a test of URL encoding!" net:http:urlencode dup println;
|
||||||
net:http:urldecode 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 {
|
] dup :len 0 eq not if {
|
||||||
"" println
|
"" println
|
||||||
"!! something went wrong somewhere. the stack is not empty." println
|
"!! something went wrong somewhere. the stack is not empty." println
|
||||||
dyn-__dump
|
dyn-__dump
|
||||||
}
|
}
|
||||||
|
"you now have a chance to connect too: localhost :4075 :4076 - stopping in 5 seconds..." println;
|
||||||
|
5000 time:sleep;
|
||||||
100
|
100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
test.txt
1
test.txt
|
@ -1 +0,0 @@
|
||||||
hi
|
|
Loading…
Add table
Reference in a new issue