make json parsing (and string concat) a few dozen times faster
This commit is contained in:
parent
5aa29427ed
commit
0ffa0a1432
6 changed files with 182 additions and 52 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
|||
/target
|
||||
/.direnv
|
||||
|
|
|
|||
86
spl/json.spl
86
spl/json.spl
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
103
src/std_fns.rs
103
src/std_fns.rs
|
|
@ -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(
|
||||
|
|
|
|||
2
test.spl
2
test.spl
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue