Wondering what’s next for npm?Check out our public roadmap! »

    nvison

    1.0.22 • Public • Published

    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_plain_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

    Install

    npm i nvison

    DownloadsWeekly Downloads

    21

    Version

    1.0.22

    License

    ISC

    Unpacked Size

    26.2 kB

    Total Files

    4

    Last publish

    Collaborators

    • avatar