86 lines
2.1 KiB
Python
86 lines
2.1 KiB
Python
from os.path import dirname
|
|
import sys
|
|
|
|
sys.path.append(f"{dirname(__file__)}/../src")
|
|
from party import regex, Parser, just, FwDeclaration
|
|
|
|
from typing import TypeVar, Any
|
|
|
|
# Utilities
|
|
# whitespace = regex(r"\s*")
|
|
whitespace = just(" ").or_else(just("\n")).repeat()
|
|
|
|
T = TypeVar("T")
|
|
|
|
|
|
def lexeme(p: Parser[T]) -> Parser[T]:
|
|
return p << whitespace
|
|
|
|
|
|
# Punctuation
|
|
lbrace = lexeme(just("{"))
|
|
rbrace = lexeme(just("}"))
|
|
lbrack = lexeme(just("["))
|
|
rbrack = lexeme(just("]"))
|
|
colon = lexeme(just(":"))
|
|
comma = lexeme(just(","))
|
|
|
|
# Primitives
|
|
true = lexeme(just("true")).value(True)
|
|
false = lexeme(just("false")).value(False)
|
|
null = lexeme(just("null")).value(None)
|
|
number = lexeme(regex(r"-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?")).map(float)
|
|
just_part = regex(r'[^"\\]+')
|
|
just_esc = just("\\") >> (
|
|
just("\\")
|
|
| just("/")
|
|
| just('"')
|
|
| just("b").value("\b")
|
|
| just("f").value("\f")
|
|
| just("n").value("\n")
|
|
| just("r").value("\r")
|
|
| just("t").value("\t")
|
|
| regex(r"u[0-9a-fA-F]{4}").map(lambda s: chr(int(s[1:], 16)))
|
|
)
|
|
quoted = lexeme(just('"') >> (just_part | just_esc).repeat().map(lambda l: "".join(l)) << just('"'))
|
|
|
|
# Data structures
|
|
json_value = FwDeclaration[Any]()
|
|
object_pair = (quoted << colon) & json_value.p()
|
|
json_object = lbrace >> object_pair.sep_by(comma).map(dict) << rbrace
|
|
array = lbrack >> json_value.p().sep_by(comma) << rbrack
|
|
|
|
# Everything
|
|
json_value.define(quoted | number | json_object | array | true | false | null)
|
|
json_doc = whitespace >> json_value.p()
|
|
|
|
|
|
def test():
|
|
assert (
|
|
json_doc.parse(
|
|
r"""
|
|
{
|
|
"int": 1,
|
|
"just": "hello",
|
|
"a list": [1, 2, 3],
|
|
"escapes": "\n \u24D2",
|
|
"nested": {"x": "y"},
|
|
"other": [true, false, null]
|
|
}
|
|
"""
|
|
)
|
|
== {
|
|
"int": 1,
|
|
"just": "hello",
|
|
"a list": [1, 2, 3],
|
|
"escapes": "\n ⓒ",
|
|
"nested": {"x": "y"},
|
|
"other": [True, False, None],
|
|
}
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
from sys import stdin
|
|
test()
|
|
# print(repr(json_doc.parse(stdin.read())))
|