Files
Viper_VRTE/json/__parser.vp
T

300 lines
8.2 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import memhub
import string
from . import (
JsonValue, JSON_NULL, JSON_BOOL, JSON_INT, JSON_FLOAT,
JSON_STRING, JSON_ARRAY, JSON_OBJECT,
null, bool_val, int_val, float_val, string_val, array, object,
)
class ParseState:
src: str
pos: sizet = 0
len: sizet = 0
pool: rawptr[memhub.MemManager]
def __init__(self, src: str, pool: rawptr[memhub.MemManager]):
self.src = src
self.len = string.strlen(src) if src != None else 0
self.pool = pool
def _peek(s: rawptr[ParseState]) -> char:
if s.pos >= s.len: return '\0'
return s.src[s.pos]
def _advance(s: rawptr[ParseState]) -> char:
if s.pos >= s.len: return '\0'
ch: char = s.src[s.pos]
s.pos += 1
return ch
def _skip_whitespace(s: rawptr[ParseState]):
while s.pos < s.len:
ch: char = s.src[s.pos]
if ch == ' ' or ch == '\t' or ch == '\n' or ch == '\r':
s.pos += 1
else:
break
def _expect(s: rawptr[ParseState], ch: char) -> bool:
_skip_whitespace(s)
if s.pos >= s.len: return False
if s.src[s.pos] != ch: return False
s.pos += 1
return True
def _parse_string(s: rawptr[ParseState]) -> str:
if _peek(s) != '"': return None
_advance(s)
start: sizet = s.pos
slen: sizet = 0
while s.pos < s.len:
ch: char = s.src[s.pos]
if ch == '"':
break
if ch == '\\':
s.pos += 1
slen += 1
slen += 1
s.pos += 1
buf: str = s.pool.alloc(slen + 1)
if buf == None:
return None
s.pos = start
di: sizet = 0
while s.pos < s.len:
ch: char = s.src[s.pos]
if ch == '"':
s.pos += 1
break
if ch == '\\':
s.pos += 1
if s.pos < s.len:
esc: char = s.src[s.pos]
if esc == 'n':
buf[di] = '\n'
elif esc == 't':
buf[di] = '\t'
elif esc == 'r':
buf[di] = '\r'
elif esc == '\\':
buf[di] = '\\'
elif esc == '"':
buf[di] = '"'
elif esc == '/':
buf[di] = '/'
else:
buf[di] = esc
di += 1
s.pos += 1
continue
buf[di] = ch
di += 1
s.pos += 1
buf[di] = '\0'
return buf
def _parse_number(s: rawptr[ParseState]) -> rawptr[JsonValue]:
_skip_whitespace(s)
is_float: bool = False
start: sizet = s.pos
if s.pos < s.len and s.src[s.pos] == '-':
s.pos += 1
while s.pos < s.len:
ch: char = s.src[s.pos]
if ch >= '0' and ch <= '9':
s.pos += 1
else:
break
if s.pos < s.len and s.src[s.pos] == '.':
is_float = True
s.pos += 1
while s.pos < s.len:
ch: char = s.src[s.pos]
if ch >= '0' and ch <= '9':
s.pos += 1
else:
break
if s.pos < s.len and (s.src[s.pos] == 'e' or s.src[s.pos] == 'E'):
is_float = True
s.pos += 1
if s.pos < s.len and (s.src[s.pos] == '+' or s.src[s.pos] == '-'):
s.pos += 1
while s.pos < s.len:
ch: char = s.src[s.pos]
if ch >= '0' and ch <= '9':
s.pos += 1
else:
break
num_len: sizet = s.pos - start
buf: str = s.pool.alloc(num_len + 1)
if buf == None:
return null(s.pool)
i: sizet = 0
while i < num_len:
buf[i] = s.src[start + i]
i += 1
buf[num_len] = '\0'
if is_float:
val: double = string.atof(buf)
return float_val(s.pool, val)
else:
ival: int64 = string.atoll(buf)
return int_val(s.pool, ival)
def _parse_value(s: rawptr[ParseState]) -> rawptr[JsonValue]:
_skip_whitespace(s)
if s.pos >= s.len:
return null(s.pool)
ch: char = _peek(s)
if ch == '"':
sv: str = _parse_string(s)
return string_val(s.pool, sv)
if ch == '{':
return _parse_object(s)
if ch == '[':
return _parse_array(s)
if ch == '-' or (ch >= '0' and ch <= '9'):
return _parse_number(s)
if ch == 't':
if s.pos + 3 < s.len:
if s.src[s.pos + 1] == 'r' and s.src[s.pos + 2] == 'u' and s.src[s.pos + 3] == 'e':
s.pos += 4
return bool_val(s.pool, True)
return null(s.pool)
if ch == 'f':
if s.pos + 4 < s.len:
if s.src[s.pos + 1] == 'a' and s.src[s.pos + 2] == 'l' and s.src[s.pos + 3] == 's' and s.src[s.pos + 4] == 'e':
s.pos += 5
return bool_val(s.pool, False)
return null(s.pool)
if ch == 'n':
if s.pos + 3 < s.len:
if s.src[s.pos + 1] == 'u' and s.src[s.pos + 2] == 'l' and s.src[s.pos + 3] == 'l':
s.pos += 4
return null(s.pool)
return null(s.pool)
return null(s.pool)
def _parse_array(s: rawptr[ParseState]) -> rawptr[JsonValue]:
if not _expect(s, '['):
return null(s.pool)
arr: rawptr[JsonValue] = array(s.pool)
# 池耗尽 ⇒ array() 返回 None(JsonValue.__new__/null 的既有约定:分配失败返回 None)。
# 若继续走到 arr._append_child(...) 就是空指针解引用 ⇒ 这里向上返回 None
# (与 json.parse 可能返回 None 的约定一致,调用方本就必须判 None)。
if arr == None:
return null(s.pool)
_skip_whitespace(s)
if s.pos < s.len and _peek(s) == ']':
_advance(s)
return arr
item: rawptr[JsonValue] = _parse_value(s)
# item == None ⟺ _parse_value 内部某处分配失败(_parse_value 对语法错误返回的是
# 合法的 JSON_NULL 节点,只有池耗尽才是 None)⇒ 向上返回 None,不追加半条。
if item == None:
return null(s.pool)
arr._append_child(item)
while True:
_skip_whitespace(s)
if s.pos >= s.len:
break
if _peek(s) != ',':
break
_advance(s)
item = _parse_value(s)
if item == None:
return null(s.pool)
arr._append_child(item)
_expect(s, ']')
return arr
def _parse_object(s: rawptr[ParseState]) -> rawptr[JsonValue]:
if not _expect(s, '{'):
return null(s.pool)
obj: rawptr[JsonValue] = object(s.pool)
# 同 _parse_array:池耗尽时 obj 为 None,不能对 None 调 _append_child。
if obj == None:
return null(s.pool)
_skip_whitespace(s)
if s.pos < s.len and _peek(s) == '}':
_advance(s)
return obj
_skip_whitespace(s)
key: str = _parse_string(s)
_expect(s, ':')
val: rawptr[JsonValue] = _parse_value(s)
if key != None and val != None:
klen: sizet = string.strlen(key)
kbuf: str = s.pool.alloc(klen + 1)
if kbuf != None:
string.memcpy(kbuf, key, klen + 1)
val.key = kbuf
obj._append_child(val)
while True:
_skip_whitespace(s)
if s.pos >= s.len:
break
if _peek(s) != ',':
break
_advance(s)
_skip_whitespace(s)
key = _parse_string(s)
_expect(s, ':')
val = _parse_value(s)
if key != None and val != None:
klen2: sizet = string.strlen(key)
kbuf2: str = s.pool.alloc(klen2 + 1)
if kbuf2 != None:
string.memcpy(kbuf2, key, klen2 + 1)
val.key = kbuf2
obj._append_child(val)
_expect(s, '}')
return obj
def parse(pool: rawptr[memhub.MemManager], src: str) -> rawptr[JsonValue]:
"""解析 JSON 字符串,返回 JsonValue 树"""
if src == None:
return null(pool)
s: rawptr[ParseState] = ParseState(src, pool)
return _parse_value(s)