Compare commits

...

19 commits
v0.4.2 ... main

Author SHA1 Message Date
57e868b1e4
in block example 2025-02-20 01:03:12 +01:00
9657502261
bump 2025-02-20 00:54:44 +01:00
473ad86d9e
fix a scoping issue 2025-02-20 00:52:25 +01:00
80252e4335
better macro usage 2025-02-20 00:23:40 +01:00
5b501c7a8c synchronization 2025-01-30 07:19:36 +01:00
a8d5ab8bda
optimizations, in blocks 2025-01-15 10:28:01 +01:00
b6949a2860
nix 2025-01-12 17:52:38 +01:00
0ffa0a1432
make json parsing (and string concat) a few dozen times faster 2025-01-12 17:52:22 +01:00
5aa29427ed
json parsing 2025-01-11 00:16:37 +01:00
e64e80eb44
add json parsing 2025-01-10 22:18:51 +01:00
805a5f3e74
fix import in oxidizer 2025-01-10 22:18:44 +01:00
c71dd4d637
improve matching 2025-01-10 22:18:25 +01:00
a597a54f35
add help 2025-01-09 20:06:17 +01:00
c4782fec5d
oops 2024-12-09 17:22:16 +01:00
b1722ed788
oops 2024-12-09 17:13:04 +01:00
6f2de9a704
logo improvements 2024-12-09 11:50:42 +01:00
06c26c910e
logo 2024-12-09 10:30:43 +01:00
12d91bcd10
linkedlist fixes, expect 2024-12-09 09:57:05 +01:00
7214d8797a
slightly speed up insertion sort 2024-12-02 08:39:37 +01:00
24 changed files with 762 additions and 58 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use nix

1
.gitignore vendored
View file

@ -1 +1,2 @@
/target
/.direnv

9
Cargo.lock generated
View file

@ -1,6 +1,12 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "ident_concat"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d8a30341e754d424ef08dbc16d24564ed3438dc6ffcf72cdbd91529496e362e"
[[package]]
name = "multicall"
@ -18,6 +24,7 @@ checksum = "b03f7fbd470aa8b3ad163c85cce8bccfc11cc9c44ef12da0a4eddd98bd307352"
name = "spl"
version = "0.4.2"
dependencies = [
"ident_concat",
"multicall",
"readformat",
]

View file

@ -1,6 +1,6 @@
[package]
name = "spl"
version = "0.4.2"
version = "0.4.3"
edition = "2021"
description = "Stack Pogramming Language: A simple, concise scripting language."
license = "MIT"
@ -10,3 +10,4 @@ authors = ["TudbuT"]
[dependencies]
readformat = "0.1"
multicall = "0.1"
ident_concat = "0.3"

View file

@ -1,5 +1,7 @@
# "Stack Programming Language" =SPL
![SPL logo](https://git.tudbut.de/TudbuT/spl/raw/branch/main/logo.png)
SPL is a simple, concise, concatenative scripting language.
Example:

11
inblock.spl Normal file
View file

@ -0,0 +1,11 @@
func main { exitcode | with args ;
"Hello, World" in {
= :readf1<"Hello, {}!"> => &nop
= :readf1<"Hello, {}"> => &nop
} if {
"name is " print println
}
0
}

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
logo.xcf Normal file

Binary file not shown.

9
shell.nix Normal file
View file

@ -0,0 +1,9 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
nativeBuildInputs = with pkgs; [
cargo
helix
rust-analyzer
cargo-watch
];
}

View file

@ -71,5 +71,11 @@ construct HashMap {
this:size ++ this:=size
}
}
to-array { [[k,v]] | with this ;
this:buckets:iter:flatten:collect
}
iter { Iterator[[k,v]] | with this ;
this:buckets:iter:flatten
}
}

View file

@ -18,6 +18,8 @@ construct net:http:server:_static_ext_server {
}
construct net:http:server:_static_ext_Request {
bufsize
client-cache
;
get-bufsize { bufsize | with this ;
this:bufsize net:http:server:bufsize or

View file

@ -23,6 +23,9 @@ construct _Iter {
map { MapIter | with map-function this ;
map-function this MapIter:new
}
flatten { FlatIter | with this ;
this FlatIter:new
}
reduce { ReduceIter | with reduce-function this ;
reduce-function this ReduceIter:new
}
@ -227,3 +230,38 @@ construct EnumerationIter {
}
include _Iter in EnumerationIter
construct FlatIter {
origin
subiter
;
construct { this | with origin this ;
origin this:=origin
this
}
cont { | with this ;
this:origin:next dup null eq if {
this:=origin
1 2 stop
} :iter this:=subiter
0
}
next { item/null | with this ;
this:origin not if {
null 2 stop
}
this:subiter not if {
this:cont if {
null 3 stop
}
}
while { this:subiter:next dup null eq } {
pop
this:cont if {
null 4 stop
}
}
}
}
include _Iter in FlatIter

View file

@ -1,6 +1,11 @@
"linkedlist.spl" import
"hashmap.spl" import
construct json namespace {
_StringyJSON
Object
Array
;
props-to-sjson { s | with spaces props this ;
def value, comma
@ -40,8 +45,275 @@ construct json namespace {
:join<comma> concat
"}" concat
}
stringy { s | with object spaces this ;
object gettype "str" eq if {
object:escape
2 stop
}
object gettype json:Array eq if {
"[" spaces if { " " concat }
object:map
:iter
:map<| json:stringy<spaces> >
:join<"," spaces if { " " concat }> concat
spaces if { " " concat } "]" concat
2 stop
}
object gettype json:Object eq if {
"{" spaces if { " " concat }
object:map
:iter
:map<| =>! [ def key, value ]
key:escape
":" spaces if { " " concat } concat
value json:stringy<spaces> concat
>
:join<"," spaces if { " " concat }>
concat
spaces if { " " concat } "}" concat
2 stop
}
null
}
parse { [^ok,json:Object/json:Array]/[^err,error] | with input this ;
input this:parse-stringy-raw =>? [ ^ok def mode, raw ] if {
def ty
mode ^obj eq if { json:Object =ty }
mode ^arr eq if { json:Array =ty }
[
^ok
raw ty:new:from
]
}
}
parse-stringy-raw { [^ok,^obj/^arr,[[k,v]]/[v]]/[^err,error] | with input this ;
def error
[ ^err ^no-object ]
def mode
def cdquot, cbackslash, cn, ct, cr, cspace, ctab, clf, ccol, ccomma, ccbo, ccbc, csbo, csbc
"\"" :_char =cdquot
"\\" :_char =cbackslash
"n" :_char =cn
"t" :_char =ct
"r" :_char =cr
" " :_char =cspace
"\t" :_char =ctab
"\n" :_char =clf
":" :_char =ccol
"," :_char =ccomma
"{" :_char =ccbo
"}" :_char =ccbc
"[" :_char =csbo
"]" :_char =csbc
input:readf1<"{{}}"> dup if { ^obj =mode }
input:readf1<"[{}]"> dup if { ^arr =mode } or => def items if {
def in-string, in-escape, exists
pop [ ^ok mode [
mode LinkedList:new dup =mode :push-front
mode:value ^obj eq if { [ } ""
items _array :foreach<| with char ;
error if { 2 stop } "fail fast";
in-escape not char cdquot eq and if {
in-string not =in-string
2 stop
}
in-escape if {
char cdquot eq
char cbackslash eq or if {
char awrap _str concat
}
char cn eq if {
"\n" concat
}
char ct eq if {
"\t" concat
}
char cr eq if {
"\r" concat
}
0 =in-escape
2 stop
}
in-string char cbackslash eq and if {
1 =in-escape
2 stop
}
in-string not if {
char cspace eq
char ctab eq or
char clf eq or if {
3 stop
}
char ccol eq if {
1 =exists
""
3 stop
}
char ccomma eq if {
mode:value ^obj eq if {
] [ ""
}
mode:value ^arr eq if {
""
}
0 =exists
3 stop
}
char ccbo eq if {
pop [ [ ""
^obj mode:push-front
3 stop
}
char ccbc eq if {
exists not if { pop pop } "remove the empty KV and string";
exists if { ] } "close KV";
]
mode:pop-front;
1 =exists
3 stop
}
char csbo eq if {
pop [ ""
^arr mode:push-front
3 stop
}
char csbc eq if {
exists not if { pop } "remove the empty string";
]
mode:pop-front;
1 =exists
3 stop
}
}
1 =exists
char awrap _str concat
>
exists not if { pop pop }
exists if { mode:value ^obj eq if { ] } }
] ]
}
error if {
pop [ ^err error ]
}
}
}
construct json:Object {
map
;
construct { this | with this ;
HashMap:new this:=map
this
}
from { this | with rawdata this ;
rawdata:foreach<| with item ;
item:1 item:0 this:map:set;
>
this
}
get-object { json:Object/null | with key this ;
this:map:get<key> with object ;
object gettype json:Object eq not if {
object gettype "array" eq if {
object json:Object:new:from dup =object this:map:set<key>;
2 stop
}
null =object
}
object
}
get-array { json:Array/null | with key this ;
this:map:get<key> with object ;
object gettype json:Array eq not if {
object gettype "array" eq if {
object json:Array:new:from dup =object this:map:set<key>;
2 stop
}
null =object
}
object
}
get-str { str/null | with key this ;
this:map:get<key> with object ;
object gettype "str" eq not if {
null =object
}
object
}
get-int { mega/null | with key this ;
this:map:get<key> with object ;
null
object gettype "str" eq if {
pop object _mega
}
}
get-double { double/null | with key this ;
this:map:get<key> with object ;
null
object gettype "str" eq if {
pop object _double
}
}
set-object { | with value key this ;
value gettype json:Object eq not if {
"not a json:Object" throw
}
value this:map:set<key>;
}
set-array { | with value key this ;
value gettype json:Array eq not if {
"not a json:Array" throw
}
value this:map:set<key>;
}
set-str { | with value key this ;
value gettype "str" eq not if {
"not a string" throw
}
value this:map:set<key>;
}
set-int { | with value key this ;
value gettype "mega" eq not if {
"not a mega" throw
}
value _str this:map:set<key>;
}
set-double { | with value key this ;
value gettype "double" eq not if {
"not a double" throw
}
value _str this:map:set<key>;
}
}
construct json:Array {
;
array { array | with this ;
this:map
}
construct { this | with this ;
List:new this:=map
this
}
from { this | with rawdata this ;
rawdata this:=map
this
}
add { index this | with this ;
this:map:len
null this:map:push;
this
}
len { length | with this ;
this:map:len
}
}
include json:Object in json:Array
construct json:_StringyJSON {
;
sjson { s | with spaces this ;

View file

@ -5,7 +5,7 @@ construct LinkedList {
;
construct { this | }
len { mega | with this ;
def i this:value null eq not =i
def i this:value null eq not _mega =i
while { this:next null eq not } {
this:next =this
i ++ =i
@ -115,6 +115,30 @@ construct LinkedList {
iter { LinkedListIter | with this ;
this LinkedListIter:new
}
append { this | with array this ;
def i 0 =i
def e this:last-entry =e
this:value null eq if {
0 array:get this:=value;
i ++ =i
}
while { i array:len lt } {
LinkedList:new dup e:=next =e
i array:get e:=value
i ++ =i
}
this
}
to-array { array | with this ;
def i 0 =i
def a this:len anew =a
while { i a:len lt } {
this:value i a:set;
this:next =this
i ++ =i
}
a
}
}
construct LinkedListIter {

View file

@ -4,7 +4,7 @@ construct sort namespace {
insertion { array | with array this ;
"for nearly-sorted";
def i, j, len
0 =i
1 =i
array:len =len
while { i len lt } {

View file

@ -35,6 +35,9 @@ construct _str_ext {
null clone this settype
}
to-bytes { [int] | str-to-bytes }
len { mega | with this ;
this array-len
}
split { str | with splitter this ;
splitter:to-bytes this:to-bytes:split:map<{ | bytes-to-str }>
}
@ -84,6 +87,28 @@ construct _str_ext {
_mega_radix { mega | with radix this ;
this radix str-to-mega-radix
}
leftpad { str | with length char this ;
this
this:len length lt if {
pop
char gettype "str" eq if {
char :_char =char
}
def a length this:len - anew =a
a:cmap<| pop char > this _array aadd _str
}
}
rightpad { str | with length char this ;
this
this:len length lt if {
pop
char gettype "str" eq if {
char :_char =char
}
def a length this:len - anew =a
this _array a:cmap<| pop char > aadd _str
}
}
} include _str_ext in str
construct _num {
@ -603,7 +628,7 @@ func aadd { array | with arr1 arr2 ;
}
func concat { str | with a b ;
a _array b _array aadd _str
a _barray b _barray aadd _str
}
func nconcat { str | with amt ;
@ -812,7 +837,19 @@ func _ { |
}
}
func nop { | }
func call-main-on-file { | with file ;
file "--help" eq if {
"spl [default]: spl [options] <file> [args]" println;
"" println;
"options:" println;
" --help prints this" println;
" --build build an SPL with given natives file" println;
" --run run with natives" println;
" --buildrun run with natives and keep the built SPL" println;
0 exit
}
catch {
"@" (0 (file _array):get 0 ("#" _array):get eq) if { pop "" } file concat
import
@ -837,6 +874,11 @@ construct unwrapping extension {
"null cannot be unwrapped." panic
}
}
expect { this | swap with message ;
dup null eq if {
pop message panic
}
}
unwrap-or { this | swap with callable ;
dup null eq if {
pop callable call
@ -863,3 +905,20 @@ func register-field { | with field-name namespace-name ;
iter:next dyn-call
iter:foreach<&dyn-objcall>
}
construct Synchronizer {
sync
;
construct { this | with this ;
0 this:=sync
this
}
do { | with callable this ;
"will increment the sync field if and only if it is zero. when it was incremented (returned zero), the loop will exit";
while { this "sync" atomic-get-inc-if-zero } { yield }
callable:call
0 this:=sync
}
}

View file

@ -235,6 +235,34 @@ fn read_block_dyn(
}
i += 3;
}
"in" if !compat => {
let mut blk = read_block(&str_words[i + 2..], false, compat)?;
i += 2 + blk.2;
let mut pre = Words {
words: vec![
Word::Key(Keyword::With(vec!["__in-item".to_owned()])),
Word::Key(Keyword::Func(
"=".to_string(),
1,
Words {
words: vec![
Word::Key(Keyword::If(Words {
words: vec![
Word::Const(Value::Mega(1)),
Word::Const(Value::Mega(3)),
Word::Call("stop".to_owned(), false, 0),
],
})),
Word::Call("__in-item".to_owned(), false, 0),
],
},
)),
Word::Const(Value::Mega(0)),
],
};
pre.words.append(&mut blk.1.words);
words.push(Word::Key(Keyword::In(pre)));
}
"use" => {
let item = str_words[i + 1].to_owned();
i += 1;
@ -292,23 +320,36 @@ fn read_block_dyn(
let throwing = cword.contains('!');
if str_words[i + 1] == "[" {
i += 1;
let mut block =
let 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);
for word in block.1.words {
if let Word::Key(Keyword::Def(ref varname)) = &word {
words.push(Word::Key(Keyword::Def(varname.clone())));
words.push(Word::Call("=".to_owned() + &varname, false, 1));
} else {
words.push(word);
}
}
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,
);
if str_words[i + 1] == "def" {
i += 1;
words.push(Word::Key(Keyword::Def(str_words[i + 1].clone())));
words.push(Word::Call("=".to_owned() + &str_words[i + 1], false, 1));
} 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));
@ -413,6 +454,11 @@ pub fn parse(mut input: String) -> Vec<String> {
let mut raw = 0;
for line in input.split('\n') {
let line = line.trim();
if line.starts_with("//") {
continue;
}
let mut in_string = false;
let mut escaping = false;
let mut was_in_string = false;
@ -429,6 +475,12 @@ pub fn parse(mut input: String) -> Vec<String> {
if c == 'r' {
s += "\r";
}
if c == 't' {
s += "\t";
}
if c == 'e' {
s += "\u{1b}";
}
if c == '"' {
s += "\"";
}

View file

@ -39,6 +39,7 @@ pub mod stream;
pub use lexer::*;
pub use runtime::*;
use ident_concat::ident;
use std::fs;
/// Creates a runtime, lexes and executes some SPL code from a file, returning the stack that was
@ -104,8 +105,8 @@ 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 {
let ident!($name binding) = $stack.pop();
let Value::$type(ref mut $name) = ident!($name binding).lock().native else {
return $stack.err(ErrorKind::InvalidCall($fn.to_owned()))
};
};
@ -146,15 +147,15 @@ nofmt! {
#[macro_export]
macro_rules! require_array_on_stack {
($name:tt, $stack:expr, $fn:literal) => {
let binding = $stack.pop();
require_array!($name, binding, $stack, $fn)
let ident!($name binding) = $stack.pop();
require_array!($name, ident!($name binding), $stack, $fn)
};
}
#[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 {
let ident!($name binding) = $stack.pop();
let $name = match ident!($name binding).lock_ro().native {
Value::Array(ref x) => x
.iter()
.cloned()
@ -178,8 +179,8 @@ nofmt! {
#[macro_export]
macro_rules! require_mut_array_on_stack {
($name:tt, $stack:expr, $fn:literal) => {
let binding = $stack.pop();
require_mut_array!($name, binding, $stack, $fn)
let ident!($name binding) = $stack.pop();
require_mut_array!($name, ident!($name binding), $stack, $fn)
};
}
}

View file

@ -138,7 +138,11 @@ impl RustAppBuilder {
.arg(format!("spl@{}", env!("CARGO_PKG_VERSION")))
.current_dir(format!("{tmp}/spl-{name}"))
.stdout(Stdio::null())
.stderr(Stdio::null())
.stderr(if output {
Stdio::inherit()
} else {
Stdio::null()
})
.spawn()
.unwrap()
.wait()?;
@ -160,7 +164,7 @@ impl RustAppBuilder {
fs::write(
format!("{tmp}/spl-{name}/src/main.rs"),
stringify! {
use spl::{runtime::*, *};
use ::spl::{runtime::*, *};
use std::env::args;

View file

@ -266,7 +266,7 @@ pub struct FrameInfo {
#[derive(Clone, Debug)]
pub struct Frame {
parent: Option<Arc<Frame>>,
pub variables: Mut<HashMap<String, AMObject>>,
pub variables: Mut<HashMap<String, Arc<Mut<AMObject>>>>,
pub functions: Mut<HashMap<String, AFunc>>,
pub origin: FrameInfo,
pub redirect_to_base: bool,
@ -364,8 +364,8 @@ impl Frame {
pub fn set_var(&self, name: String, obj: AMObject, stack: &Stack) -> OError {
let mut frame = self;
loop {
if let Some(x) = frame.variables.lock().get_mut(&name) {
*x = obj;
if let Some(x) = frame.variables.lock_ro().get(&name) {
*(x.lock()) = obj;
return Ok(());
}
if let Some(ref x) = frame.parent {
@ -380,7 +380,7 @@ impl Frame {
let mut frame = self;
loop {
if let Some(x) = frame.variables.lock_ro().get(&name) {
return Ok(x.clone());
return Ok(x.lock_ro().clone());
}
if let Some(ref x) = frame.parent {
frame = x;
@ -436,8 +436,8 @@ impl Frame {
#[derive(Clone, Debug)]
pub struct Stack {
frames: Vec<Arc<Frame>>,
object_stack: Vec<AMObject>,
objcall_stack: Vec<AMObject>,
object_stack: LinkedList<AMObject>,
objcall_stack: LinkedList<AMObject>,
files: Vec<String>,
pub return_accumultor: u32,
}
@ -472,8 +472,8 @@ impl Stack {
let o = Arc::new(Frame::root());
let mut r = Stack {
frames: vec![o.clone()],
object_stack: Vec::new(),
objcall_stack: Vec::new(),
object_stack: LinkedList::new(),
objcall_stack: LinkedList::new(),
files: Vec::new(),
return_accumultor: 0,
};
@ -489,8 +489,8 @@ impl Stack {
let o = Arc::new(Frame::root_in(frame_info));
let mut r = Stack {
frames: vec![o.clone()],
object_stack: Vec::new(),
objcall_stack: Vec::new(),
object_stack: LinkedList::new(),
objcall_stack: LinkedList::new(),
files: Vec::new(),
return_accumultor: 0,
};
@ -555,14 +555,15 @@ impl Stack {
if frame.redirect_to_base {
frame = self.frames.first().unwrap().clone();
}
let tmpname = name.clone();
let variable = Arc::new(Mut::new(Value::Null.spl()));
let var = variable.clone();
frame.functions.lock().insert(
name.clone(),
Arc::new(Func {
ret_count: 1,
origin: Arc::new(Frame::dummy()),
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
stack.push(stack.get_var(tmpname.clone())?);
stack.push(var.lock_ro().clone());
Ok(())
}))),
run_as_base: false,
@ -570,7 +571,7 @@ impl Stack {
name: name.clone(),
}),
);
let tmpname = name.clone();
let var = variable.clone();
frame.functions.lock().insert(
"=".to_owned() + &name,
Arc::new(Func {
@ -578,27 +579,26 @@ impl Stack {
origin: Arc::new(Frame::dummy()),
to_call: FuncImpl::NativeDyn(Arc::new(Box::new(move |stack| {
let v = stack.pop();
stack.set_var(tmpname.clone(), v)
*(var.lock()) = v;
Ok(())
}))),
run_as_base: false,
fname: Some("RUNTIME".to_owned()),
name: "=".to_owned() + &name,
}),
);
frame.variables.lock().insert(name, Value::Null.spl());
frame.variables.lock().insert(name, variable);
}
pub fn pop_until(&mut self, obj: AMObject) -> Vec<AMObject> {
let Some((idx, ..)) = self
.object_stack
.iter()
.enumerate()
.rfind(|o| *o.1.lock_ro() == *obj.lock_ro())
else {
return Vec::new();
};
let items = self.object_stack[idx + 1..].to_vec();
self.object_stack = self.object_stack[0..idx].to_vec();
let mut items = Vec::new();
while let Some(item) = self.object_stack.pop_back() {
if obj == item {
break;
}
items.push(item);
}
items.reverse();
items
}
@ -637,18 +637,18 @@ impl Stack {
}
pub fn push(&mut self, obj: AMObject) {
self.object_stack.push(obj)
self.object_stack.push_back(obj)
}
pub fn peek(&mut self) -> AMObject {
self.object_stack
.last()
.back()
.cloned()
.unwrap_or(Value::Null.spl())
}
pub fn pop(&mut self) -> AMObject {
self.object_stack.pop().unwrap_or(Value::Null.spl())
self.object_stack.pop_back().unwrap_or(Value::Null.spl())
}
pub fn get_origin(&self) -> FrameInfo {
@ -829,6 +829,8 @@ pub enum Keyword {
/// Defines function <name> with <type> impl type
/// equivalent to "<content>" "<name>" "<type>" dyn-func-of
FuncOf(String, String, FuncImplType),
/// in { = ... = ... = ... }
In(Words),
}
/// Any SPL value that is not a construct.
@ -1475,12 +1477,12 @@ impl Words {
}
Keyword::ObjPush => {
let o = stack.pop();
stack.objcall_stack.push(o);
stack.objcall_stack.push_back(o);
}
Keyword::ObjPop => {
let o = stack
.objcall_stack
.pop()
.pop_back()
.expect("invalid word generation. objpop without objpush!");
stack.push(o);
}
@ -1498,6 +1500,16 @@ impl Words {
}),
)
}),
Keyword::In(blk) => {
stack.call(&Arc::new(Func {
ret_count: 0,
to_call: FuncImpl::SPL(blk.clone()),
origin: stack.get_frame(),
fname: None,
name: "in-block".to_owned(),
run_as_base: false,
}))?;
}
},
Word::Const(x) => {
if option_env!("SPLDEBUG").is_some() {

View file

@ -84,6 +84,7 @@ fn sasm_parse<'a>(line: &str, words: &mut Vec<Word>, lines: &mut impl Iterator<I
sasm_read_func(lines),
))),
"if" => words.push(Word::Key(Keyword::If(sasm_read_func(lines)))),
"in" => words.push(Word::Key(Keyword::In(sasm_read_func(lines)))),
"with" => words.push(Word::Key(Keyword::With(
line.into_iter().skip(1).map(ToOwned::to_owned).collect(),
))),
@ -310,6 +311,11 @@ fn sasm_write_func(words: Words) -> String {
output += &sasm_write_func(blk).replace("\n", "\n\t").trim_end();
output += "\nend\n";
}
Keyword::In(blk) => {
output += "in\n\t";
output += &sasm_write_func(blk).replace("\n", "\n\t").trim_end();
output += "\nend\n";
}
Keyword::With(items) => {
output += "with";
for item in items {

View file

@ -155,6 +155,7 @@ pub fn array_new(stack: &mut Stack) -> OError {
pub fn array_len(stack: &mut Stack) -> OError {
let binding = stack.pop();
let len = match binding.lock_ro().native {
Value::Str(ref a) => a.chars().count(),
Value::Array(ref a) => a.len(),
Value::ByteArray(ref a) => a.len(),
_ => return stack.err(ErrorKind::InvalidCall("array-len".to_owned())),
@ -1013,6 +1014,11 @@ pub fn time(stack: &mut Stack) -> OError {
Ok(())
}
pub fn thread_yield(_stack: &mut Stack) -> OError {
thread::yield_now();
Ok(())
}
pub fn str_readf(stack: &mut Stack) -> OError {
require_on_stack!(string, Str, stack, "str-readf");
require_on_stack!(pat, Str, stack, "str-readf");
@ -1176,9 +1182,120 @@ pub fn chdir(stack: &mut Stack) -> OError {
Ok(())
}
pub fn json_parse(_stack: &mut Stack) -> OError {
/*
require_on_stack!(input, Str, stack, "json_parse");
let objstr = readf1("{{}}", &input);
let arrstr = readf1("[{}]", &input);
enum Mode {
Obj,
Arr,
}
use Mode::*;
let primary_mode = if arrstr.is_some() { Arr } else { Obj };
let Some(input) = (if arrstr.is_some() { arrstr } else { objstr }) else {
let errarr = vec![
Value::Str("err".to_owned()).spl(),
Value::Str("no-obj".to_owned()).spl(),
];
stack.push(Value::Array(errarr).spl());
return Ok(());
};
let mut mode = LinkedList::new();
mode.push_back(primary_mode);
let mut values = Vec::new();
let mut in_string = false;
let mut in_escape = false;
let mut exists = false;
let mut key = String::new();
let mut string = String::new();
let mut object = None;
for c in input.chars() {
if !in_escape && c != '"' {
in_string = !in_string;
continue;
}
if in_escape {
if c == '"' || c == '\\' {
string.push(c);
}
match c {
'n' => string.push('\n'),
't' => string.push('\t'),
'r' => string.push('\r'),
_ => (),
}
in_escape = false;
continue;
}
if in_string && c == '\\' {
in_escape = true;
continue;
}
if !in_string {
if c.is_whitespace() {
continue;
}
if c == ':' {
exists = true;
(key, string) = (string, String::new());
continue;
}
if c == ',' {
let o = if let Some(o) = object {
o
} else {
Value::Str(string).spl()
};
match mode.back() {
Some(Obj) => {
values.push(
Value::Array(vec![Value::Str(key).spl(), Value::Str(string).spl()])
.spl(),
);
}
Some(Arr) => values.push(),
None => stack.err(ErrorKind::Custom("json-parse"))?,
}
}
}
}
stack.push(
Value::Array(vec![
Value::Str("ok".to_owned()).spl(),
Value::Str(
match primary_mode {
Obj => "obj",
Arr => "arr",
}
.to_owned(),
)
.spl(),
values,
])
.spl(),
);*/
Ok(())
}
pub fn atomic_get_inc_if_zero(stack: &mut Stack) -> OError {
require_on_stack!(field, Str, stack, "atomic-get-inc-if-zero");
let o = stack.pop();
let o = o.lock_ro();
let f = o.field(&field, stack)?;
let mut f = f.lock();
stack.push(f.clone().spl());
if f.native == Value::Mega(0) {
f.native = Value::Mega(1);
}
Ok(())
}
pub fn register(r: &mut Stack, o: Arc<Frame>) {
type Fn = fn(&mut Stack) -> OError;
let fns: [(&str, Fn, u32); 71] = [
let fns: [(&str, Fn, u32); 73] = [
("pop", pop, 0),
("dup", dup, 2),
("dup2", dup2, 3),
@ -1241,6 +1358,7 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
("write-file-sasm", write_file_sasm, 1),
("fork", fork, 0),
("sleeptime", time, 0),
("yield", thread_yield, 0),
("str-readf", str_readf, 1),
("str-to-mega-radix", str_to_mega_radix, 1),
("mega-to-str-radix", mega_to_str_radix, 1),
@ -1250,6 +1368,8 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
("delete-file", delete_file, 1),
("delete-dir", delete_dir, 1),
("chdir", chdir, 0),
("atomic-get-inc-if-zero", atomic_get_inc_if_zero, 1),
// ("json-parse", json_parse, 1),
];
for f in fns {
r.define_func(

View file

@ -417,6 +417,81 @@ func main { int | with args ;
pop
"result should be 493 538 503 (arbitrary numbers)" println
"" println
"testing json:parse" println
"object:" println
!{{ "hello": "world", "another": "property", "obj": { "hi":"lol", "abcdefg": "hijklmnop" }, "arr": [ "a", "b", "c" ] }} dup println json:parse-stringy-raw :sjson<0> println
"array:" println
!{[ "hello", "world", "another", "property", { "hi": "lol", "abcdefg": "hijklmnop" }, [ "a", "b", "c" ] ]} dup println json:parse-stringy-raw :sjson<0> println
"object object:" println
def object, subobject
json:Object:new =object
"world" "hello" object:set-str;
5 "megaproperty" object:set-int;
object json:stringy<0> println
json:Object:new =subobject
subobject "subobj" object:set-object;
"world" "hi" subobject:set-str;
json:Array:new =subobject
subobject "arr" object:set-array;
"good morning!" subobject:add:set-str;
"may the network of evil rise" subobject:add:set-str;
object json:stringy<0> dup =object println
object json:parse =>? [ ^ok &=object ] not if {
:sjson<0> println
}
object:get-object<"subobj">:get-str<"hi"> println;
"" println;
"matching Hello, world using in block" println;
"Hello, world" in {
= "match against Hello, {}!" println :readf1<"Hello, {}!"> => &nop
= "match against Hello, {}" println :readf1<"Hello, {}"> => &nop
= "fallback" println pop "Unknown" 1
} if {
println
}
"" println;
"testing sync (should be 5)" println;
def x 5 =x
def sync Synchronizer:new =sync
10 :foreach<|
fork<| 10 :foreach<| sync:do<| x ++ =x > > >
fork<| 10 :foreach<| sync:do<| x -- =x > > >
>
1000 time:sleep;
x println
"nonsynced for comparison:" println
def x 5 =x
10 :foreach<|
fork<| 10 :foreach<| x ++ =x > >
fork<| 10 :foreach<| x -- =x > >
>
1000 time:sleep;
x println

1
test.txt Normal file
View file

@ -0,0 +1 @@
hi