Add json module
This commit is contained in:
parent
b00fcc0c0a
commit
5517162a93
174
modules/json.krk
Normal file
174
modules/json.krk
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
"""
|
||||||
|
JSON parser
|
||||||
|
"""
|
||||||
|
|
||||||
|
def loads(s):
|
||||||
|
let i = 0
|
||||||
|
let value # okay dumb scoping thing, but we'll assign this to _value later...
|
||||||
|
def peek():
|
||||||
|
return s[i] if i < len(s) else None
|
||||||
|
def advance():
|
||||||
|
i++
|
||||||
|
def whitespace():
|
||||||
|
while peek() in ' \r\n\t': advance()
|
||||||
|
def string():
|
||||||
|
if peek() != '"': raise ValueError("String should start with \" but got " + peek())
|
||||||
|
advance()
|
||||||
|
let out = ""
|
||||||
|
let c = peek()
|
||||||
|
while c != '"':
|
||||||
|
if c is None: raise ValueError("Unterminated string")
|
||||||
|
if c == '\\':
|
||||||
|
advance()
|
||||||
|
c = peek()
|
||||||
|
if c == '"': out += '"'
|
||||||
|
elif c == '\\': out += '\\'
|
||||||
|
elif c == '/': out += '/'
|
||||||
|
elif c == 'b': out += '\x08' # Backspace
|
||||||
|
elif c == 'f': out += '\x0c' # Formfeed
|
||||||
|
elif c == 'n': out += '\n'
|
||||||
|
elif c == 'r': out += '\r'
|
||||||
|
elif c == 't': out += '\t'
|
||||||
|
elif c == 'u':
|
||||||
|
advance()
|
||||||
|
let sub = [0,0,0,0]
|
||||||
|
sub[0] = peek()
|
||||||
|
advance()
|
||||||
|
sub[1] = peek()
|
||||||
|
advance()
|
||||||
|
sub[2] = peek()
|
||||||
|
advance()
|
||||||
|
sub[3] = peek()
|
||||||
|
if not all([x in '0123456789abcdefABCDEF' for x in sub]):
|
||||||
|
raise ValueError('Invalid codepoint in JSON string')
|
||||||
|
raise NotImplementedError('TODO: unicode escapes')
|
||||||
|
else:
|
||||||
|
raise ValueError('Invalid escape sequence in JSON string')
|
||||||
|
else:
|
||||||
|
out += c
|
||||||
|
advance()
|
||||||
|
c = peek()
|
||||||
|
if peek() != '"':
|
||||||
|
raise ValueError("That was not expected.")
|
||||||
|
advance()
|
||||||
|
return out
|
||||||
|
def obj():
|
||||||
|
if peek() != '{': raise ValueError("Object should start with {?")
|
||||||
|
advance()
|
||||||
|
let out = {}
|
||||||
|
whitespace()
|
||||||
|
if peek() == '}': return out
|
||||||
|
while True:
|
||||||
|
whitespace()
|
||||||
|
let k = string()
|
||||||
|
whitespace()
|
||||||
|
if peek() != ':': raise ValueError("Expected : after key, got " + peek())
|
||||||
|
advance()
|
||||||
|
let v = value()
|
||||||
|
out[k] = v
|
||||||
|
if peek() == '}':
|
||||||
|
advance()
|
||||||
|
return out
|
||||||
|
if peek() != ',':
|
||||||
|
raise ValueError('Expected , after value, or } for end of object.')
|
||||||
|
advance()
|
||||||
|
def array():
|
||||||
|
if peek() != '[': raise ValueError("Array should start with [?")
|
||||||
|
advance()
|
||||||
|
let out = []
|
||||||
|
whitespace()
|
||||||
|
if peek() == ']': return out
|
||||||
|
while True:
|
||||||
|
whitespace()
|
||||||
|
out.append(value())
|
||||||
|
if peek() == ']':
|
||||||
|
advance()
|
||||||
|
return out
|
||||||
|
if peek() != ',':
|
||||||
|
raise ValueError("Expected , after value, or ] for end of array.")
|
||||||
|
advance()
|
||||||
|
def number():
|
||||||
|
let value = 0
|
||||||
|
let sign = 1
|
||||||
|
if peek() == '-':
|
||||||
|
sign = -1
|
||||||
|
advance()
|
||||||
|
if peek() == '0':
|
||||||
|
advance()
|
||||||
|
elif peek() in '0123456789':
|
||||||
|
value = int(peek())
|
||||||
|
advance()
|
||||||
|
while peek() in '0123456789':
|
||||||
|
value = value * 10
|
||||||
|
value = value + int(peek())
|
||||||
|
advance()
|
||||||
|
else:
|
||||||
|
raise ValueError("Expected digit")
|
||||||
|
if peek() == '.':
|
||||||
|
advance()
|
||||||
|
let mult = 0.1
|
||||||
|
if peek() not in '0123456789':
|
||||||
|
raise ValueError("Expected digit")
|
||||||
|
while peek() in '0123456789':
|
||||||
|
value = value + multiplier * int(peek())
|
||||||
|
multiplier = multiplier * 0.1
|
||||||
|
advance()
|
||||||
|
if peek() in 'eE':
|
||||||
|
raise NotImplementedError("Exponent values are not implemented.")
|
||||||
|
return value
|
||||||
|
def boolean():
|
||||||
|
let inv = ValueError("Invalid literal while parsing bool")
|
||||||
|
if peek() == 't':
|
||||||
|
advance()
|
||||||
|
if peek() != 'r': raise inv
|
||||||
|
advance()
|
||||||
|
if peek() != 'u': raise inv
|
||||||
|
advance()
|
||||||
|
if peek() != 'e': raise inv
|
||||||
|
advance()
|
||||||
|
return True
|
||||||
|
elif peek() == 'f':
|
||||||
|
advance()
|
||||||
|
if peek() != 'a': raise inv
|
||||||
|
advance()
|
||||||
|
if peek() != 'l': raise inv
|
||||||
|
advance()
|
||||||
|
if peek() != 's': raise inv
|
||||||
|
advance()
|
||||||
|
if peek() != 'e': raise inv
|
||||||
|
advance()
|
||||||
|
return False
|
||||||
|
raise inv
|
||||||
|
def null():
|
||||||
|
let inv = ValueError("Invalid literal while parsing null")
|
||||||
|
if peek() != 'n': raise inv
|
||||||
|
advance()
|
||||||
|
if peek() != 'u': raise inv
|
||||||
|
advance()
|
||||||
|
if peek() != 'l': raise inv
|
||||||
|
advance()
|
||||||
|
if peek() != 'l': raise inv
|
||||||
|
advance()
|
||||||
|
return None
|
||||||
|
def _value():
|
||||||
|
whitespace()
|
||||||
|
let c = peek()
|
||||||
|
let out = None
|
||||||
|
if c== '"':
|
||||||
|
out = string()
|
||||||
|
elif c == '{':
|
||||||
|
out = obj()
|
||||||
|
elif c == '[':
|
||||||
|
out = array()
|
||||||
|
elif c == '-' or c in '0123456789':
|
||||||
|
out = number()
|
||||||
|
elif c == 't' or c == 'f':
|
||||||
|
out = boolean()
|
||||||
|
elif c == 'n':
|
||||||
|
out = null()
|
||||||
|
else:
|
||||||
|
raise ValueError("Unexpected character")
|
||||||
|
whitespace()
|
||||||
|
return out
|
||||||
|
value = _value
|
||||||
|
return value()
|
121
test/testJsonModule.krk
Normal file
121
test/testJsonModule.krk
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
let data = '''
|
||||||
|
{
|
||||||
|
"glossary": {
|
||||||
|
"title": "example glossary",
|
||||||
|
"GlossDiv": {
|
||||||
|
"title": "S",
|
||||||
|
"GlossList": {
|
||||||
|
"GlossEntry": {
|
||||||
|
"ID": "SGML",
|
||||||
|
"SortAs": "SGML",
|
||||||
|
"GlossTerm": "Standard Generalized Markup Language",
|
||||||
|
"Acronym": "SGML",
|
||||||
|
"Abbrev": "ISO 8879:1986",
|
||||||
|
"GlossDef": {
|
||||||
|
"para": "A meta-markup language, used to create markup languages such as DocBook.",
|
||||||
|
"GlossSeeAlso": ["GML", "XML"]
|
||||||
|
},
|
||||||
|
"GlossSee": "markup"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
print(json.loads(data))
|
||||||
|
|
||||||
|
data = '''
|
||||||
|
{"web-app": {
|
||||||
|
"servlet": [
|
||||||
|
{
|
||||||
|
"servlet-name": "cofaxCDS",
|
||||||
|
"servlet-class": "org.cofax.cds.CDSServlet",
|
||||||
|
"init-param": {
|
||||||
|
"configGlossary:installationAt": "Philadelphia, PA",
|
||||||
|
"configGlossary:adminEmail": "ksm@pobox.com",
|
||||||
|
"configGlossary:poweredBy": "Cofax",
|
||||||
|
"configGlossary:poweredByIcon": "/images/cofax.gif",
|
||||||
|
"configGlossary:staticPath": "/content/static",
|
||||||
|
"templateProcessorClass": "org.cofax.WysiwygTemplate",
|
||||||
|
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
|
||||||
|
"templatePath": "templates",
|
||||||
|
"templateOverridePath": "",
|
||||||
|
"defaultListTemplate": "listTemplate.htm",
|
||||||
|
"defaultFileTemplate": "articleTemplate.htm",
|
||||||
|
"useJSP": false,
|
||||||
|
"jspListTemplate": "listTemplate.jsp",
|
||||||
|
"jspFileTemplate": "articleTemplate.jsp",
|
||||||
|
"cachePackageTagsTrack": 200,
|
||||||
|
"cachePackageTagsStore": 200,
|
||||||
|
"cachePackageTagsRefresh": 60,
|
||||||
|
"cacheTemplatesTrack": 100,
|
||||||
|
"cacheTemplatesStore": 50,
|
||||||
|
"cacheTemplatesRefresh": 15,
|
||||||
|
"cachePagesTrack": 200,
|
||||||
|
"cachePagesStore": 100,
|
||||||
|
"cachePagesRefresh": 10,
|
||||||
|
"cachePagesDirtyRead": 10,
|
||||||
|
"searchEngineListTemplate": "forSearchEnginesList.htm",
|
||||||
|
"searchEngineFileTemplate": "forSearchEngines.htm",
|
||||||
|
"searchEngineRobotsDb": "WEB-INF/robots.db",
|
||||||
|
"useDataStore": true,
|
||||||
|
"dataStoreClass": "org.cofax.SqlDataStore",
|
||||||
|
"redirectionClass": "org.cofax.SqlRedirection",
|
||||||
|
"dataStoreName": "cofax",
|
||||||
|
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
|
||||||
|
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
|
||||||
|
"dataStoreUser": "sa",
|
||||||
|
"dataStorePassword": "dataStoreTestQuery",
|
||||||
|
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
|
||||||
|
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
|
||||||
|
"dataStoreInitConns": 10,
|
||||||
|
"dataStoreMaxConns": 100,
|
||||||
|
"dataStoreConnUsageLimit": 100,
|
||||||
|
"dataStoreLogLevel": "debug",
|
||||||
|
"maxUrlLength": 500}},
|
||||||
|
{
|
||||||
|
"servlet-name": "cofaxEmail",
|
||||||
|
"servlet-class": "org.cofax.cds.EmailServlet",
|
||||||
|
"init-param": {
|
||||||
|
"mailHost": "mail1",
|
||||||
|
"mailHostOverride": "mail2"}},
|
||||||
|
{
|
||||||
|
"servlet-name": "cofaxAdmin",
|
||||||
|
"servlet-class": "org.cofax.cds.AdminServlet"},
|
||||||
|
|
||||||
|
{
|
||||||
|
"servlet-name": "fileServlet",
|
||||||
|
"servlet-class": "org.cofax.cds.FileServlet"},
|
||||||
|
{
|
||||||
|
"servlet-name": "cofaxTools",
|
||||||
|
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
|
||||||
|
"init-param": {
|
||||||
|
"templatePath": "toolstemplates/",
|
||||||
|
"log": 1,
|
||||||
|
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
|
||||||
|
"logMaxSize": "",
|
||||||
|
"dataLog": 1,
|
||||||
|
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
|
||||||
|
"dataLogMaxSize": "",
|
||||||
|
"removePageCache": "/content/admin/remove?cache=pages&id=",
|
||||||
|
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
|
||||||
|
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
|
||||||
|
"lookInContext": 1,
|
||||||
|
"adminGroupID": 4,
|
||||||
|
"betaServer": true}}],
|
||||||
|
"servlet-mapping": {
|
||||||
|
"cofaxCDS": "/",
|
||||||
|
"cofaxEmail": "/cofaxutil/aemail/*",
|
||||||
|
"cofaxAdmin": "/admin/*",
|
||||||
|
"fileServlet": "/static/*",
|
||||||
|
"cofaxTools": "/tools/*"},
|
||||||
|
|
||||||
|
"taglib": {
|
||||||
|
"taglib-uri": "cofax.tld",
|
||||||
|
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
|
||||||
|
'''
|
||||||
|
|
||||||
|
print(json.loads(data))
|
2
test/testJsonModule.krk.expect
Normal file
2
test/testJsonModule.krk.expect
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{'glossary': {'title': 'example glossary', 'GlossDiv': {'title': 'S', 'GlossList': {'GlossEntry': {'SortAs': 'SGML', 'Abbrev': 'ISO 8879:1986', 'Acronym': 'SGML', 'GlossTerm': 'Standard Generalized Markup Language', 'GlossSee': 'markup', 'ID': 'SGML', 'GlossDef': {'para': 'A meta-markup language, used to create markup languages such as DocBook.', 'GlossSeeAlso': ['GML', 'XML']}}}}}}
|
||||||
|
{'web-app': {'taglib': {'taglib-uri': 'cofax.tld', 'taglib-location': '/WEB-INF/tlds/cofax.tld'}, 'servlet': [{'servlet-class': 'org.cofax.cds.CDSServlet', 'init-param': {'templateProcessorClass': 'org.cofax.WysiwygTemplate', 'cachePagesStore': 100, 'searchEngineListTemplate': 'forSearchEnginesList.htm', 'searchEngineFileTemplate': 'forSearchEngines.htm', 'cachePackageTagsStore': 200, 'dataStoreUser': 'sa', 'dataStoreInitConns': 10, 'configGlossary:installationAt': 'Philadelphia, PA', 'cachePagesTrack': 200, 'dataStoreLogFile': '/usr/local/tomcat/logs/datastore.log', 'templateOverridePath': '', 'redirectionClass': 'org.cofax.SqlRedirection', 'cachePackageTagsTrack': 200, 'useDataStore': True, 'maxUrlLength': 500, 'dataStorePassword': 'dataStoreTestQuery', 'defaultFileTemplate': 'articleTemplate.htm', 'defaultListTemplate': 'listTemplate.htm', 'dataStoreConnUsageLimit': 100, 'templatePath': 'templates', 'useJSP': False, 'configGlossary:poweredBy': 'Cofax', 'dataStoreClass': 'org.cofax.SqlDataStore', 'dataStoreName': 'cofax', 'cacheTemplatesRefresh': 15, 'dataStoreDriver': 'com.microsoft.jdbc.sqlserver.SQLServerDriver', 'cachePagesDirtyRead': 10, 'configGlossary:adminEmail': 'ksm@pobox.com', 'dataStoreTestQuery': 'SET NOCOUNT ON;select test=\'test\';', 'cacheTemplatesStore': 50, 'templateLoaderClass': 'org.cofax.FilesTemplateLoader', 'configGlossary:staticPath': '/content/static', 'searchEngineRobotsDb': 'WEB-INF/robots.db', 'cacheTemplatesTrack': 100, 'dataStoreLogLevel': 'debug', 'dataStoreUrl': 'jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon', 'cachePagesRefresh': 10, 'configGlossary:poweredByIcon': '/images/cofax.gif', 'dataStoreMaxConns': 100, 'jspFileTemplate': 'articleTemplate.jsp', 'cachePackageTagsRefresh': 60, 'jspListTemplate': 'listTemplate.jsp'}, 'servlet-name': 'cofaxCDS'}, {'servlet-class': 'org.cofax.cds.EmailServlet', 'init-param': {'mailHostOverride': 'mail2', 'mailHost': 'mail1'}, 'servlet-name': 'cofaxEmail'}, {'servlet-class': 'org.cofax.cds.AdminServlet', 'servlet-name': 'cofaxAdmin'}, {'servlet-class': 'org.cofax.cds.FileServlet', 'servlet-name': 'fileServlet'}, {'servlet-class': 'org.cofax.cms.CofaxToolsServlet', 'init-param': {'logMaxSize': '', 'log': 1, 'removeTemplateCache': '/content/admin/remove?cache=templates&id=', 'dataLogMaxSize': '', 'lookInContext': 1, 'adminGroupID': 4, 'removePageCache': '/content/admin/remove?cache=pages&id=', 'dataLogLocation': '/usr/local/tomcat/logs/dataLog.log', 'betaServer': True, 'fileTransferFolder': '/usr/local/tomcat/webapps/content/fileTransferFolder', 'logLocation': '/usr/local/tomcat/logs/CofaxTools.log', 'dataLog': 1, 'templatePath': 'toolstemplates/'}, 'servlet-name': 'cofaxTools'}], 'servlet-mapping': {'cofaxCDS': '/', 'fileServlet': '/static/*', 'cofaxEmail': '/cofaxutil/aemail/*', 'cofaxTools': '/tools/*', 'cofaxAdmin': '/admin/*'}}}
|
Loading…
Reference in New Issue
Block a user