300 lines
8.2 KiB
Plaintext
300 lines
8.2 KiB
Plaintext
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)
|