Notícias:

SMF - Just Installed!

Menu principal

Biblioteca RJSON

Iniciado por rafael, Maio 16, 2019, 06:04:29 AM

tópico anterior - próximo tópico

rafael

 ;D

Bom dia !!!

Agora são exatamente 7h da manhã, acabei de portar mais uma biblioteca json. agora com opção de identação:
Também usei as funções cod_arquivo e decod_arquivo da biblioteca anterior se me permite.

FAÇAM BOM USO!


/*
    MIT License

    Copyright (c) 2018 xiedacon

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.

    Biblioteca JSON portada por Rafael Alves Lemos
    na madrugada de 16/maio/2019 as 0h as 6h
    não tem mudanças significativas em relação a lib json anterior prisma
    apenas na identação com opção de escolher os espaços
    inclusive portei as funções de cod_arquivo e decod_arquivo

    USO:
    local json = inclua 'rjson'
    local cod = json.cod({

        a=verdadeiro,
        b=falso,
        c='string',
        d=1,
        [0]={'matriz','de','valores'}

    },nulo,4) //<-- o quanto é a tabulação

    imprima(cod)
    local dec = json.decod(cod)
    imprima(tipo(dec))
    para i,v em pares(dec) inicio
        imprima(i,v)
    fim





saida:

{
    "d": 1,
    "c": "string",
    "b": false,
    "a": true,
    "0": [
        "matriz",
        "de",
        "valores"
    ]
}
tabela
d   1
c   string
b   falso
a   verdadeiro
0   tabela: 0x1ea14a0



*/


//*****************************Constant
local Constant = {
    ESC_MAP = {
        ["\\"] = [[\]],
        ["\""] = [[\"]],
        ["/"] = [[\/]],
        ["\b"] = [[\b]],
        ["\f"] = [[\f]],
        ["\n"] = [[\n]],
        ["\r"] = [[\r]],
        ["\t"] = [[\t]],
        ["\a"] = [[\u0007]],
        ["\v"] = [[\u000b]]
    },

    UN_ESC_MAP = {
        b = "\b",
        f = "\f",
        n = "\n",
        r = "\r",
        t = "\t",
        u0007 = "\a",
        u000b = "\v"
    },

    NULL = defmetatabela({}, {
        __tostring = funcao() retorne "nulo" fim
    })
}






//*****************************Serializer

local NULL = Constant.NULL
local ESC_MAP = Constant.ESC_MAP

local funcao kind_of(obj)
    se tipo(obj) ~= "tabela" entao retorne tipo(obj) fim
    se obj == NULL entao retorne "nulo" fim

    local i = 1
    para _ em pares(obj) inicio
        se obj[i] ~= nulo entao i = i + 1 senao retorne "tabela" fim
    fim

    se i == 1 entao
        retorne "tabela"
    senao
        retorne "matriz"
    fim
fim

local funcao escape_str(s)
    para k, v em pares(ESC_MAP) inicio
        s = s:troque(k, v)
    fim

    retorne s
fim

local Serializer = {
    print_address = falso,
    max_depth = 100
}

defmetatabela(Serializer, {
    __call = funcao(este, opts)
        local serializer = {
            depth = 0,
            max_depth = opts.max_depth,
            print_address = opts.print_address,
            stream = opts.stream
        }

        defmetatabela(serializer, { __index = Serializer })

        retorne serializer
    fim
})

funcao Serializer:space(n)
    local stream = este.stream
    para i = 1, n ou 0 inicio
        stream:write(" ")
    fim

    retorne este
fim

funcao Serializer:key(key)
    local stream = este.stream
    local kind = kind_of(key)

    se kind == "matriz" entao
        erro("Can't encode matriz as key.")
    senaose kind == "tabela" entao
        erro("Can't encode tabela as key.")
    senaose kind == "string" entao
        stream:write("\"", escape_str(key), "\"")
    senaose kind == "numero" entao
        stream:write("\"", convstring(key), "\"")
    senaose kind == 'booleano' entao
        se key entao
            stream:write('true')
        senao
            stream:write('false')
        fim
    senaose kind == 'nulo' entao
        stream:write('null')
    senaose este.print_address entao
        stream:write(convstring(key))
    senao
        erro("Unjsonifiable tipo: " .. kind .. ".")
    fim

    retorne este
fim

funcao Serializer:matriz(arr, replacer, indent, space)
    local stream = este.stream

    stream:write("[")
    para i, v em ipares(arr) inicio
        se replacer entao v = replacer(k, v) fim

        stream:write(i == 1 e "" ou ",")
        stream:write(space > 0 e "\n" ou "")
        este:space(indent)
        este:json(v, replacer, indent + space, space)
    fim
    se #arr > 0 entao
        stream:write(space > 0 e "\n" ou "")
        este:space(indent - space)
    fim
    stream:write("]")

    retorne este
fim

funcao Serializer:tabela(obj, replacer, indent, space)
    local stream = este.stream

    stream:write("{")
    local len = 0
    para k, v em pares(obj) inicio
        se replacer entao v = replacer(k, v) fim

        se v ~= nulo entao
            stream:write(len == 0 e "" ou ",")
            stream:write(space > 0 e "\n" ou "")
            este:space(indent)
            este:key(k)
            stream:write(space > 0 e ": " ou ":")
            este:json(v, replacer, indent + space, space)
            len = len + 1
        fim
    fim
    se len > 0 entao
        stream:write(space > 0 e "\n" ou "")
        este:space(indent - space)
    fim
    stream:write("}")

    retorne este
fim

funcao Serializer:json(obj, replacer, indent, space)
    local stream = este.stream
    local kind = kind_of(obj)

    este.depth = este.depth + 1
    se este.depth > este.max_depth entao erro("Reach max depth: " .. convstring(este.max_depth)) fim

    se kind == "matriz" entao
        este:matriz(obj, replacer, indent, space)
    senaose kind == "tabela" entao
        este:tabela(obj, replacer, indent, space)
    senaose kind == "string" entao
        stream:write("\"", escape_str(obj), "\"")
    senaose kind == "numero" entao
        stream:write(convstring(obj))
    senaose kind == "booleano" entao
        se obj entao
            stream:write('true')
        senao
            stream:write('false')
        fim
    senaose kind == "nulo" entao
        stream:write("null")
    senaose este.print_address entao
        stream:write(convstring(obj))
    senao
        erro("Unjsonifiable tipo: " .. kind)
    fim

    retorne este
fim

funcao Serializer:toString()
    retorne este.stream:toString()
fim



//*****************************Parser


local NULL = Constant.NULL
local UN_ESC_MAP = Constant.UN_ESC_MAP

local funcao next_char(str, pos)
    pos = pos + #str:separe("^%s*", pos)
    retorne str:corte(pos, pos), pos
fim

local funcao syntax_error(str, pos)
    retorne erro("Sintaxe JSON inválida iniciando em " .. pos .. ": " .. str:corte(pos, pos + 10))
fim

local Parser = {}

defmetatabela(Parser, {
    __call = funcao(este, opts)
        local parser = {
            without_null = opts.without_null
        }

        defmetatabela(parser, { __index = Parser })

        retorne parser
    fim
})

funcao Parser:numero(str, pos)
    local num = str:separe("^-?%d+%.?%d*[eE]?[+-]?%d*", pos)
    local val = convnumero(num)
    se nao val entao
        syntax_error(str, pos)
    senao
        retorne val, pos + #num
    fim
fim

funcao Parser:string(str, pos)
    pos = pos + 1
   
    local i = 1
    local chars = {} //tabela.new(#str - pos - 1, 0)
    enquanto(pos <= #str) inicio
        local c = str:corte(pos, pos)

        se c == "\"" entao
            retorne tabela.concat(chars, ""), pos + 1
        senaose c == "\\" entao
            local j = pos + 1

            local next_c = str:corte(j, j)
            para k, v em pares(UN_ESC_MAP) inicio
                se str:corte(j, j + #k - 1) == k entao
                    next_c = v
                    j = j + #k - 1
                fim
            fim

            c = next_c
            pos = j
        fim

        chars[i] = c
        i = i + 1
        pos = pos + 1
    fim

    syntax_error(str, pos)
fim

funcao Parser:matriz(str, pos)
    local arr = {} //tabela.new(10, 0)
    local val
    local i = 1
    local c
   
    pos = pos + 1
    enquanto verdadeiro inicio
        val, pos = este:json(str, pos)
        arr[i] = val
        i = i + 1

        c, pos = next_char(str, pos)
        se (c == ",") entao
            pos = pos + 1
        senaose (c == "]") entao
            retorne arr, pos + 1
        senao
            syntax_error(str, pos)
        fim
    fim

    retorne arr
fim

funcao Parser:tabela(str, pos)
    local obj = {} //tabela.new(0, 10)
    local key
    local val
    local c

    pos = pos + 1
    enquanto verdadeiro inicio
        c, pos = next_char(str, pos)

        se c == "}" entao retorne obj, pos + 1
        senaose c == "\"" entao key, pos = este:string(str, pos)
        senao syntax_error(str, pos) fim

        c, pos = next_char(str, pos)
        se c ~= ":" entao syntax_error(str, pos) fim

        val, pos = este:json(str, pos + 1)
        obj[key] = val

        c, pos = next_char(str, pos)
        se c == "}" entao
            retorne obj, pos + 1
        senaose c == "," entao
            pos = pos + 1
        senao
            syntax_error(str, pos)
        fim
    fim
fim

funcao Parser:json(str, pos)
    local first = falso
    local val
    local c

    se nao pos ou pos == 1 entao first = verdadeiro fim
    pos = pos ou 1

    se tipo(str) ~= "string" entao erro("str should be a string")
    senaose pos > #str entao erro("Reached unexpected fim of input") fim

    c, pos = next_char(str, pos)

    se c == "{" entao
        val, pos =  este:tabela(str, pos)
    senaose c == "[" entao
        val, pos = este:matriz(str, pos)
    senaose c == "\"" entao
        val, pos = este:string(str, pos)
    senaose c == "-" ou c:procure('%d')  entao
        val, pos = este:numero(str, pos)
    senao
        para k, v em pares({ ["true"] = verdadeiro, ["false"] = falso, ["null"] = NULL }) inicio
            se (str:corte(pos, pos + #k - 1) == k) entao
                val, pos = v, pos + #k
                quebre
            fim
        fim

        //se val == nulo entao syntax_error(str, pos) fim
    fim

    se first e pos <= #str entao syntax_error(str, pos) fim
    se este.without_null e val == NULL entao val = nulo fim

    retorne val, pos
fim







//************************JSON





local json = {
    _VERSION = "0.1",
    null = Constant.NULL
}

funcao json.cod(obj, replacer, space, print_address)
    se tipo(space) ~= "numero" entao space = 0 fim

    retorne Serializer({
        print_address = print_address,
        stream = {
            fragments = {},
            write = funcao(este, ...)
                para i = 1, #{...} inicio
                    este.fragments[#este.fragments + 1] = ({...})[i]
                fim
            fim,
            toString = funcao(este)
                retorne tabela.concat(este.fragments)
            fim
        }
    }):json(obj, replacer, space, space):toString()
fim

funcao json.decod(str, without_null)
    retorne Parser({ without_null = without_null }):json(str, 1)
fim


local es_abra = es.abra;

funcao json.decod_arquivo(arq)
  local a, err = es_abra(arq,'leitura');
  se nao a entao retorne falso, err fim
  local str = a:leia'*t'; //lê todo o arquivo.
  a:feche();
 
  retorne json.decod(str); //retorna a tabela. 
fim

funcao json.cod_arquivo(arq, tab)
  se tipo(tab)<> 'tabela' entao
    retorne falso, ('\n\nErro arg #2, espera-se tabela ao invés de ' .. tipo(tab) .. '\n\n');
  fim
  local str = json.cod(tab);
  local a, err = es_abra(arq,'escrita');
  se nao a entao retorne falso, err fim
  a:escreva(str);
  a:feche();
  retorne verdadeiro;
fim


retorne json