You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
109 lines
2.4 KiB
109 lines
2.4 KiB
from sly import Lexer, Parser |
|
import pprint |
|
|
|
""" |
|
Implementation with SLY of a very simple JSON parser. |
|
Little reminder: here's the grammar |
|
|
|
json := object | array |
|
object := '{' members '}' |
|
members := pair | pair ',' members |
|
array := '[' elements ']' |
|
elements := value | value "," elements |
|
pair := STRING ':' value |
|
value := STRING | INTEGER | FLOAT | object | array |
|
|
|
""" |
|
|
|
|
|
class JsonLexer(Lexer): |
|
tokens = {STRING, NUMBER, TRUE, FALSE, NULL} |
|
ignore = ' \t\n\r' |
|
literals = {'{', '}', '[', ']', ':', ','} |
|
|
|
@_(r'-?(0|[1-9][0-9]*)(\.[0-9]+)?([Ee][+-]?[0-9]+)?') |
|
def NUMBER(self, t): |
|
t.value = float(t.value) |
|
return t |
|
|
|
@_(r'"([ !#-\[\]-\U0010ffff]+|\\(["\/\\bfnrt]|u[0-9A-Fa-f]{4}))*"') |
|
def STRING(self, t): |
|
t.value = json_unescape(t.value) |
|
return t |
|
|
|
@_(r'true') |
|
def TRUE(self, t): |
|
t.value = True |
|
return t |
|
|
|
@_(r'false') |
|
def FALSE(self, t): |
|
t.value = False |
|
return t |
|
|
|
@_(r'null') |
|
def NULL(self, t): |
|
t.value = None |
|
return t |
|
|
|
|
|
class JsonParser(Parser): |
|
tokens = JsonLexer.tokens |
|
start = 'value' |
|
|
|
@_(r'"{" [ pairs ] "}"') |
|
def value(self, p): |
|
if p.pairs: |
|
return dict(p.pairs) |
|
else: |
|
return {} |
|
|
|
@_(r'pair { "," pair }') |
|
def pairs(self, p): |
|
return [p.pair0] + p.pair1 |
|
|
|
@_(r'STRING ":" value') |
|
def pair(self, p): |
|
return (p.STRING, p.value) |
|
|
|
@_(r'"[" [ items ] "]"') |
|
def value(self, p): |
|
if p.items: |
|
return p.items |
|
else: |
|
return [] |
|
|
|
@_(r'value { "," value }') |
|
def items(self, p): |
|
return [p.value0] + p.value1 |
|
|
|
@_('STRING', |
|
'NUMBER', |
|
'TRUE', |
|
'FALSE', |
|
'NULL') |
|
def value(self, p): |
|
return p[0] |
|
|
|
def error(self, p): |
|
raise ValueError("Parsing error at token %s" % str(p)) |
|
|
|
|
|
if __name__ == "__main__": |
|
lexer = JSONLexer() |
|
parser = JSONParser() |
|
json_text = """{"menu": { |
|
"id": "file", |
|
"value": "File", |
|
"popup": { |
|
"menuitem": [ |
|
{"value": "New", "onclick": "CreateNewDoc()"}, |
|
{"value": "Open", "onclick": "OpenDoc()"}, |
|
{"value": "Close", "onclick": "CloseDoc()"} |
|
], |
|
"delay" : 1.5 |
|
} |
|
}} |
|
""" |
|
result = parser.parse(lexer.tokenize(json_text)) |
|
pprint.pprint(result)
|
|
|