nvison
v1.0.28
Published
another json-like structure
Downloads
24
Maintainers
Readme
nvison
- another json
- undefined
- bin
- hex
- oct
- scientific-notation
- bigInt
- Infinity
- NaN
- concatable-string
- comments
- hash
- ref
- optional-quote
- configurable-colons
- configurable-commas
- configurable-quotes
- configurable-array-blk
- configurable-object-blk
- post-dfs-generator
install
- npm install nvison
requirements
- node need V15+ , coz it USE Event-Target
usage
const ison = require("nvison");
parse
examples
undefined
var code = `[undefined,null,true,false]`
var j = ison.parse_from_str(code)
/*
> j
[ undefined, null, true, false ]
>
*/
number
var code = `
[
175 ,0x1101, 0b1101, 0o1101,
-123.12e3, -1.1e2.5, .1E3.13,
1234567891234567889123456789n,
+Infinity, -Infinity, NaN
]
`
var j = ison.parse_from_str(code)
/*
> j
[
175,
4353,
13,
577,
-123120,
-347.85054261852173,
134.89628825916535,
1234567891234567889123456789n,
Infinity,
-Infinity,
NaN
]
>
*/
string
var code = `
[
abc-def,
'def\tsquoted',
"d quoted",
\`tock-quoted-line\ntock-quoted-line\`,
str"@auto-concat@"str
]
`
var j = ison.parse_from_str(code)
/*
[
'abc-def',
'def\tsquoted',
'd quoted',
'tock-quoted-line\ntock-quoted-line',
'str@auto-concat@str'
]
*/
commas
- commas is optional
- support mixed-style
- by default it is "," ";" and white-spaces
- its configurable: must-be-one-char
var code = `
[1 2 3 a b c]
[1,2,3,a,b,c]
[1;2;3;a;b;c]
[1 2,3;a b,c]
`
var j = ison.parse_from_str(code)
/*
[
[ 1, 2, 3, 'a', 'b', 'c' ],
[ 1, 2, 3, 'a', 'b', 'c' ],
[ 1, 2, 3, 'a', 'b', 'c' ],
[ 1, 2, 3, 'a', 'b', 'c' ]
]
*/
self-define
//for example, I add a chinese period "。" as a comma
> ison.CFG.commas
Set(2) { ',', ';' }
>
> ison.CFG.commas.add("。")
> ison.CFG.commas
Set(3) { ',', ';', '。' }
>
var code = `[i,you,he; yo,tú,Él; 我。你。他;]`
var j = ison.parse_from_str(code)
/*
[
'i', 'you', 'he',
'yo', 'tú', 'Él',
'我', '你', '他'
]
*/
colons
- colons is mandatory
- support mixed-style
- by default it is ":" and "="
- its configurable,must-be-one-char
var code = `
{a:b,c:d,e:f}
{a=b,c=d,e=f}
{a:b c=d,e=f; g:h}
`
var j = ison.parse_from_str(code)
/*
[
{ a: 'b', c: 'd', e: 'f' },
{ a: 'b', c: 'd', e: 'f' },
{ a: 'b', c: 'd', e: 'f', g: 'h' }
]
*/
self-define
ison.CFG.colons
Set(2) { ':', '=' }
ison.CFG.colons.add("|")
Set(3) { ':', '=', '|' }
var code = `
{ a|b,c|d,e|f }
`
var j = ison.parse_from_str(code)
/*
{ a: 'b', c: 'd', e: 'f' }
*/
Error auto recover
- colon before key will be treated as whitespace
- multi-colons before value will be treated as one
- colons following value will be dropped
- unfinished key:value pair will be dropped
//colon before key will be treated as whitespace
{ a:b, : key : value} -> { a: 'b', key: 'value' }
//multi-colons before value will be treated as one
{ a:b, key ::::value } -> { a: 'b', key: 'value' }
[100, ::::xy] -> [ 100, 'xy' ]
//colons following value will be dropped
[abc::: 123] -> [ 'abc', 123 ],
{k:abc:::, k2:v2} -> { k: 'abc', k2: 'v2' }
[abc : 666] -> [ 'abc', 666 ]
{k:v, key:value : 100:200} -> { '100': 200, k: 'v', key: 'value' }
//unfinished k:v will be dropped
{k:abc:, k2, k3:v3} -> { k: 'abc', k3: 'v3' }
{k:abc:, k2:, k3:v3} -> { k: 'abc', k3: 'v3' }
{k:abc:, k2 :,k3:v3} -> { k: 'abc', k3: 'v3' }
quotes
- quotes is optional if without escape
- support mixed-style
- by default it is "'" and '"' and '`'
- its configurable,must-be-one-char, except '
', '
' is reservered for future-using - currently NOT support string-literal-template
- auto concat
> ison.CFG.quotes
Set(3) { '"', "'", '`' }
> ison.CFG.reserved
[ '/', '*', '#', '&', '`' ]
> ison.CFG.quotes.add("%")
> ison.CFG.quotes
Set(4) { '"', "'", '`', '%' }
>
var code = `
[
a"\t\v\t"b,
"cde",
fgh,
'ijk',
%lmnopq%,
\`rst \n uvw \n xyz\`
]
`
var j = ison.parse_from_str(code)
/*
[ 'a\t\x0B\tb', 'cde', 'fgh', 'ijk', 'lmnopq', 'rst \n uvw \n xyz' ]
*/
unclosed quotes will be dropped
{ k0:v0, key : "val-\t-.... -> {k0:v0}
if you want the lefted parted for continue, use
require("nvison-parse-internal")
D {
....
lefted: {
type: 3,
data: { rslt: 'val-\t-....\n\n\n', lefted: '', state: 3, quote: '"' }
},
....
}
comments
- two kind : line-comment(//....\n) and block-comment(/...../)
- four position supported: @after_key, @before_val(properties), @after_val, @before_val(elements)
valid and invalid comment
// VALID
{
.....
/*text-node-comment*/
key /*key-comment*/ : /* before-val-comment*/ value /*after-val-comment*/ ,
/*text-node-comment*/
key1 : value1,
/*text-node-comment*/
.....
}
[
.....
/*text-node-comment*/
element0 /*after-val-comment*/ ,
/*text-node-comment*/
....
]
//VALID
//INVALID
[ abc/*in-value-comment-not-supported*/def, ....]
it will be treated as [ abc, def ....]
{ kkkk/*in-key-comment-not-supported*/ey : value, ...}
it will be treadted as { ey:value,....} ,
the pre-part of kkkkey(kkkk) will be dropped
//INVALID
var code = `
{
"total_rows": 129 , //line comment here
/*
block comment here
*/
"offset": 0, //line comment here
"rows": [
{
/*text comment*/
"id": /*@before_val comment*/ "change1_0.6995461115147918",
/*text comment*/
"key" : "change1_0.6995461115147918" /*@after_val comment*/ ,
/*text comment*/
"value" /*key comment*/ : {
"rev": "1-e240bae28c7bb3667f02760f6398d508"
},
/*text comment*/
"doc" /*key comment*/ : {
"_id": "change1_0.6995461115147918",
"_rev": "1-e240bae28c7bb3667f02760f6398d508",
"hello": 1
}
}
]
}
`
var j = ison.parse_from_str(code)
/*
{
total_rows: 129,
offset: 0,
rows: [
{
id: 'change1_0.6995461115147918',
key: 'change1_0.6995461115147918',
value: [Object],
doc: [Object]
}
]
}
*/
array
- support mixed-style
- by default it is "[]" and '()' and '<>'
- its configurable,must-be-one-char-pair
ison.CFG.array_blks
> Map(3) { '[' => ']', '(' => ')', '<' => '>' }
ison.CFG.array_blks.add("【","】")
ison.CFG.array_blks.add("《","》")
> Map(5) { '[' => ']', '(' => ')', '<' => '>', '【' => '】', '《' => '》' }
var code = `
defun last-state <rewindable>
【
let
[
(size <rewind-count rewindable>)
]
[
if<zerop size> <values nil nil>
(
values
<aref 《rewind-store rewindable》 《1 - size》>
t
)
]
】
`
var j = ison.parse_from_str(code)
/*
[
'defun',
'last-state',
[ 'rewindable' ],
[ 'let', [ [Array] ], [ 'if', [Array], [Array], [Array] ] ]
]
*/
dict(object)
- support mixed-style
- by default it is "{}"
- its configurable,must-be-one-char-pair
ison.CFG.obj_blks
ison.CFG.obj_blks.add('^','$')
var code = `
{
a:b;
c=d;
e= {
f:[1 2 3],
g= ^ key:g,value:200 $
}
}
`
var j = ison.parse_from_str(code)
/*
{
"a": "b",
"c": "d",
"e": {
"f": [
1,
2,
3
],
"g": {
"key": "g",
"value": 200
}
}
}
*/
hash and ref
- pass {enable_ref:true}
- hash can add more-than-one "key" to a value, for reference
- ref will search along the "scope" chain, recursively
- ref can NOT ref "behind" to right-sibling and children and descendants
- coz ison originally is used for streaming parse, it did NOT known what will come
- hash and ref performance is BAD when the ison-file is too Big,such as 4M+
valid and invalid
//valid
{ #root
key0: {k0:v0, k1:&root} //ref to ancestor
key1: value1,
key2: &key0 //ref to preceding-sibling
} //will be {key0: {k0:v0,k1:<Circular>}, key1:value1,key2:{k0:v0,k1:<Circular>}}
{
....
key0 : value0 #hash-after-val ,
key1 : &hash-after-val
....
} // will be {key0:value0, key1:value0}
/* very very long string */ #comment
[ &comment, &comment ,&comment] //valid to ref to text-node-position-comment
//invalid
{
key0 #hash-after-key-will-be-treated-as-comment : value0
}
{
key0 : #hash-before-value-will-be-treated-as-comment value0
}
{
key0: &fsib, //ref to following-sibling have no effect
key1: value1,
key2: value2 #fsib,
key3: {
k30:&des, //ref to descendant have no effect
k31: [
des0,
des1,
des2 #des
]
}
}
{
k : /*before_val*/ #hash v //hash between colon and value have no effect
}
example 1
var code = `
{
arr: [1,2,3,4] #ary0 ,
x: &ary0 ,
y: {
k0:abcdefghijklmnopqrstuvwxyz,
k1:&ary0,
k2:&x, //ref to x, it will be reursively point to [1,2,3,4]
k3:&k0, //along the scope(ArrayBlock or ObjectBlock), it will be point to abcdefghijklmnopqrstuvwxyz
k4:&0 //if the ref is a number, it will be the sibling-position, it will be point to abcdefghijklmnopqrstuvwxyz
}
}
`
var j = ison.parse_from_str(code,{enable_ref:true})
/*
{
arr: [ 1, 2, 3, 4 ],
x: [ 1, 2, 3, 4 ],
y: {
k0: 'abcdefghijklmnopqrstuvwxyz',
k1: [ 1, 2, 3, 4 ],
k2: [ 1, 2, 3, 4 ],
k3: 'abcdefghijklmnopqrstuvwxyz',
k4: 'abcdefghijklmnopqrstuvwxyz'
}
}
*/
> j.x === j.arr
true
> j.y.k1 === j.arr
true
> j.y.k2 === j.arr
true
>
example 2
var code = `
//use hash '#' to set a alias for ref
"https://127.0.0.1/img.jpg" #jpg
"https://127.0.0.1/img.png" #png
//multi-hash permitted : the below has two hash
\`this is a very long
very long string
...string\` #long-string #so-long
[
// different hashes point to the same value
&long-string;
&so-long;
/*html-like, use ref '&' to get-value-of value-with-hash */
{
div@1: {
attribs:{style={}}
} #div ,
div@2: &div,
div@3: &div,
div@4: &div,
img@1: &jpg,
img@2: &png,
} #body;
/*repeat the body*/
&body;
] #html
`
var j = ison.parse_from_str(code,{enable_ref:true})
/*
> j
[
'https://127.0.0.1/img.jpg',
'https://127.0.0.1/img.png',
'this is a very long\n very long string\n ...string',
[
'this is a very long\n very long string\n ...string',
'this is a very long\n very long string\n ...string',
{
'div@1': [Object],
'div@2': [Object],
'div@3': [Object],
'div@4': [Object],
'img@1': 'https://127.0.0.1/img.jpg',
'img@2': 'https://127.0.0.1/img.png'
},
{
'div@1': [Object],
'div@2': [Object],
'div@3': [Object],
'div@4': [Object],
'img@1': 'https://127.0.0.1/img.jpg',
'img@2': 'https://127.0.0.1/img.png'
}
]
]
>
*/
console.log(JSON.stringify(j,null,2))
/*
[
"https://127.0.0.1/img.jpg",
"https://127.0.0.1/img.png",
"this is a very long\n very long string\n ...string",
[
"this is a very long\n very long string\n ...string",
"this is a very long\n very long string\n ...string",
{
"div@1": {
"attribs": {
"style": {}
}
},
"div@2": {
"attribs": {
"style": {}
}
},
"div@3": {
"attribs": {
"style": {}
}
},
"div@4": {
"attribs": {
"style": {}
}
},
"img@1": "https://127.0.0.1/img.jpg",
"img@2": "https://127.0.0.1/img.png"
},
{
"div@1": {
"attribs": {
"style": {}
}
},
"div@2": {
"attribs": {
"style": {}
}
},
"div@3": {
"attribs": {
"style": {}
}
},
"div@4": {
"attribs": {
"style": {}
}
},
"img@1": "https://127.0.0.1/img.jpg",
"img@2": "https://127.0.0.1/img.png"
}
]
]
*/
generator
- in-post-dfs sequence
- async_gen only support from_file
sync gen
var code = `
{
a:[1,2,3],
b:{k0:v0,k1:v1,k2:v2},
}
`
var g = ison.gen_from_str(code)
> Array.from(g)
[
[ Symbol(empty), 1 ],
[ Symbol(empty), 2 ],
[ Symbol(empty), 3 ],
[ 'a', [ 1, 2, 3 ] ],
[ 'k0', 'v0' ],
[ 'k1', 'v1' ],
[ 'k2', 'v2' ],
[ 'b', { k0: 'v0', k1: 'v1', k2: 'v2' } ],
[ Symbol(empty), { a: [Array], b: [Object] } ]
]
async
var ag = ison.agen_from_file("./gen.ison");
(
async() => {
for await(let entry of ag) {
console.log(entry)
}
}
)();
/*
[ Symbol(empty), 1 ]
[ Symbol(empty), 2 ]
[ 'k0', 'v0' ]
[ 'k1', 'v1' ]
[
Symbol(empty),
{ a: [ 1, 2, 3 ], b: { k0: 'v0', k1: 'v1', k2: 'v2' } }
]
[ Symbol(empty), [ { a: [Array], b: [Object] } ] ]
*/
CFG
> ison.CFG
[
[fixed] {
hash: '#',
ref: '&',
tmpl_quote: '`',
slash: '/',
asterisk: '*',
line_comment: '//',
blk_comments: [ '/*', '*/' ]
},
[configurable-if-not-reserved] {
obj_blks: Map(1) { '{' => '}' },
array_blks: Map(3) { '[' => ']', '(' => ')', '<' => '>' },
quotes: Set(3) { '"', "'", '`' },
commas: Set(2) { ',', ';' },
colons: Set(2) { ':', '=' }
},
[reserved] { reserved: [ '/', '*', '#', '&', '`' ] }
]
>
> ison.OPT_DICT
{ enable_ref: false, encoding: 'utf8' }
>
APIS
parse
- function parse_from_generator(g,opt={enable_ref:false})
- function parse_from_str(s,opt={enable_ref:false})
- function parse_from_file(fn,opt={enable_ref:false,encoding:'utf8'})
- function * gen_from_generator(g,opt={enable_ref:false,encoding:'utf8'})
- function * gen_from_str(s,opt={enable_ref:false,encoding:'utf8'})
- function * gen_from_file(fn,opt={enable_ref:false,encoding:'utf8'})
- async function * agen_from_generator(ag,opt={enable_ref:false,encoding:'utf8'})
- async function * agen_from_file(fn,opt={enable_ref:false,encoding:'utf8'})
CLI
parse
npm install nvison -g
plain without color
with input-file
Usage: nvison_parse [options] <file>
If <file> is not provided, then STDIN is used.
Options:
-c, --config [file] config default see below
-e, --encoding default utf8
-o, --output [file] Output to the specified file, otherwise STDOUT
-h, --help Output usage information
---default config---
{
quotes: [ '"', "'", '`', '·' ],
colons: [ ':', '=', ':' ],
commas: [ ',', ';', ',', ';', '。', '、' ],
array_blks: [
[ '[', ']' ],
[ '(', ')' ],
[ '<', '>' ],
[ '【', '】' ],
[ '(', ')' ],
[ '‘', '’' ],
[ '“', '”' ]
],
obj_blks: [ [ '{', '}' ], [ '《', '》' ] ]
}
---default config---
using STDIN
nvison_plain_parse
{
a:b;
c=d;
e= {
f:[1 2 3],
g= { key:g,value:200 }
}
}
# ctrl+D press ctrl+D <===============
{
"a": "b",
"c": "d",
"e": {
"f": [
1,
2,
3
],
"g": {
"key": "g",
"value": 200
}
}
}
TODO
- stringify with self-defined color
- coz the color file is too big, currently NOT expose the API ,soon
IS IT FAST?
- NO
- its suitable for config-like or html-like file
- if file large than 4M its slow
- coz it has a internal structure ,and the parser parse the string-stream char-by-char
- for error tolerance and configurable operators
RESTRICT
- BigFloat NOT supported
- coz currently only QuickJS provide that API .
LICENSE
- ISC