make json parsing (and string concat) a few dozen times faster

This commit is contained in:
Tove 2025-01-12 17:52:22 +01:00
parent 5aa29427ed
commit 0ffa0a1432
Signed by: TudbuT
GPG key ID: B3CF345217F202D3
6 changed files with 182 additions and 52 deletions

1
.gitignore vendored
View file

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

View file

@ -75,61 +75,84 @@ construct json namespace {
}
null
}
parse { [^ok,json:Object]/[^err,error] | with input this ;
input this:parse-stringy-raw =>? [ ^ok def raw ] if {
[ ^ok raw json:Object:new:from ]
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,[[k,v]]/[v]]/[^err,error] | with input this ;
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
mode LinkedList:new dup =mode :push-front
pop [ ^ok [
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 "\"" :_char eq and if {
in-escape not char cdquot eq and if {
in-string not =in-string
2 stop
}
in-escape if {
char "\"" :_char eq
char "\\" :_char eq or if {
char cdquot eq
char cbackslash eq or if {
char awrap _str concat
}
char "n" :_char eq if {
char cn eq if {
"\n" concat
}
char "t" :_char eq if {
char ct eq if {
"\t" concat
}
char "r" :_char eq if {
char cr eq if {
"\r" concat
}
0 =in-escape
2 stop
}
in-string char "\\" :_char eq and if {
in-string char cbackslash eq and if {
1 =in-escape
2 stop
}
in-string not if {
char " " :_char eq
char "\t" :_char eq or
char "\n" :_char eq or if {
char cspace eq
char ctab eq or
char clf eq or if {
3 stop
}
char ":" :_char eq if {
char ccol eq if {
1 =exists
""
3 stop
}
char "," :_char eq if {
char ccomma eq if {
mode:value ^obj eq if {
] [ ""
}
@ -139,34 +162,37 @@ construct json namespace {
0 =exists
3 stop
}
char "{" :_char eq if {
char ccbo eq if {
pop [ [ ""
^obj mode:push-front
3 stop
}
char "}" :_char eq if {
exists not dup if { pop pop } "remove the empty KV and string";
not if { ] }
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 "[" :_char eq if {
char csbo eq if {
pop [ ""
^arr mode:push-front
3 stop
}
char "]" :_char eq if {
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
>
mode:value ^obj eq if { ] }
exists not if { pop pop }
exists if { mode:value ^obj eq if { ] } }
] ]
}
error if {
@ -220,14 +246,14 @@ construct json:Object {
get-int { mega/null | with key this ;
this:map:get<key> with object ;
null
object gettype "string" eq if {
object gettype "str" eq if {
pop object _mega
}
}
get-double { double/null | with key this ;
this:map:get<key> with object ;
null
object gettype "string" eq if {
object gettype "str" eq if {
pop object _double
}
}
@ -265,6 +291,9 @@ construct json:Object {
construct json:Array {
;
array { array | with this ;
this:map
}
construct { this | with this ;
List:new this:=map
this
@ -278,6 +307,9 @@ construct json:Array {
null this:map:push;
this
}
len { length | with this ;
this:map:len
}
}
include json:Object in json:Array

View file

@ -603,7 +603,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 ;

View file

@ -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,
};
@ -589,16 +589,14 @@ impl Stack {
}
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 +635,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 {
@ -1475,12 +1473,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);
}

View file

@ -1,5 +1,5 @@
use std::{
collections::{HashMap, VecDeque},
collections::{HashMap, LinkedList, VecDeque},
env::{self, args, vars},
fs,
io::{stdin, stdout, Write},
@ -10,7 +10,7 @@ use std::{
time::{Duration, SystemTime},
};
use readformat::readf;
use readformat::{readf, readf1};
use crate::{dyn_fns, mutex::Mut, runtime::*, sasm::sasm_write, *};
@ -1176,6 +1176,104 @@ 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 register(r: &mut Stack, o: Arc<Frame>) {
type Fn = fn(&mut Stack) -> OError;
let fns: [(&str, Fn, u32); 71] = [
@ -1250,6 +1348,7 @@ pub fn register(r: &mut Stack, o: Arc<Frame>) {
("delete-file", delete_file, 1),
("delete-dir", delete_dir, 1),
("chdir", chdir, 0),
// ("json-parse", json_parse, 1),
];
for f in fns {
r.define_func(

View file

@ -446,7 +446,7 @@ func main { int | with args ;
object json:stringy<0> dup =object println
object json:parse =>? [ ^ok &=object ] not if {
object json:parse =>? [ ^ok ^obj &=object ] not if {
:sjson<0> println
}