Add json module

This commit is contained in:
K. Lange 2021-01-11 19:02:51 +09:00
parent b00fcc0c0a
commit 5517162a93
3 changed files with 297 additions and 0 deletions

174
modules/json.krk Normal file
View 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
View 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))

View 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/*'}}}