301 lines
5.3 KiB
Plaintext
301 lines
5.3 KiB
Plaintext
|
|
fun isSpace(c)
|
|
return c == ' '
|
|
or c == '\t'
|
|
or c == '\n';
|
|
|
|
fun isDigit(c)
|
|
return unicode(c) <= unicode('9')
|
|
and unicode(c) >= unicode('0');
|
|
|
|
fun skip(ctx, test)
|
|
while not ended(ctx): {
|
|
if not test(current(ctx)):
|
|
break;
|
|
next(ctx);
|
|
}
|
|
|
|
fun ended(ctx)
|
|
return ctx.i == count(ctx.str);
|
|
|
|
fun next(ctx) {
|
|
assert(ctx.i < count(ctx.str));
|
|
ctx.i = ctx.i + 1;
|
|
}
|
|
|
|
fun current(ctx) {
|
|
assert(ctx.i < count(ctx.str));
|
|
return ctx.str[ctx.i];
|
|
}
|
|
|
|
fun parse(str) {
|
|
|
|
if str == none:
|
|
str = '';
|
|
|
|
ctx = {str: str, i: 0};
|
|
|
|
skip(ctx, isSpace);
|
|
|
|
if ended(ctx): {
|
|
print('Source only contains whitespace');
|
|
return none;
|
|
}
|
|
|
|
return parseAny(ctx);
|
|
}
|
|
|
|
fun parseArray(ctx) {
|
|
|
|
assert(current(ctx) == '[');
|
|
|
|
next(ctx); # Skip '['.
|
|
|
|
skip(ctx, isSpace);
|
|
|
|
if ended(ctx): {
|
|
error("Source ended inside an array");
|
|
return none;
|
|
}
|
|
|
|
arr = [];
|
|
|
|
if current(ctx) == ']': {
|
|
next(ctx);
|
|
return arr;
|
|
}
|
|
|
|
while true: {
|
|
|
|
child = parseAny(ctx);
|
|
|
|
if child == none:
|
|
return none;
|
|
|
|
arr[count(arr)] = child;
|
|
|
|
skip(ctx, isSpace);
|
|
|
|
if ended(ctx): {
|
|
error("Source ended inside an array");
|
|
return none;
|
|
}
|
|
|
|
if current(ctx) == ']':
|
|
break;
|
|
|
|
if current(ctx) != ',': {
|
|
error("Bad character inside array");
|
|
return none;
|
|
}
|
|
|
|
next(ctx); # Skip ','.
|
|
|
|
skip(ctx, isSpace);
|
|
|
|
if ended(ctx): {
|
|
error("Source ended inside an array");
|
|
return none;
|
|
}
|
|
}
|
|
|
|
next(ctx);
|
|
return arr;
|
|
}
|
|
|
|
fun parseObject(ctx) {
|
|
|
|
assert(current(ctx) == '{');
|
|
|
|
next(ctx); # Skip '{'.
|
|
|
|
skip(ctx, isSpace);
|
|
|
|
if ended(ctx): {
|
|
error("Source ended inside an object");
|
|
return none;
|
|
}
|
|
|
|
obj = {};
|
|
|
|
if current(ctx) == '}': {
|
|
next(ctx);
|
|
return {};
|
|
}
|
|
|
|
while true: {
|
|
|
|
if current(ctx) != '"': {
|
|
error("Bad character where a string was expected");
|
|
return none;
|
|
}
|
|
|
|
key = parseString(ctx);
|
|
|
|
if key == none:
|
|
return none;
|
|
|
|
skip(ctx, isSpace);
|
|
|
|
if current(ctx) != ':': {
|
|
error("Bad character where ':' was expected");
|
|
return none;
|
|
}
|
|
|
|
next(ctx); # Skip ':'.
|
|
|
|
skip(ctx, isSpace);
|
|
|
|
child = parseAny(ctx);
|
|
|
|
if child == none:
|
|
return none;
|
|
|
|
obj[key] = child;
|
|
|
|
skip(ctx, isSpace);
|
|
|
|
if ended(ctx): {
|
|
error("Source ended inside an arrays");
|
|
return none;
|
|
}
|
|
|
|
if current(ctx) == '}':
|
|
break;
|
|
|
|
if current(ctx) != ',': {
|
|
error("Bad character inside array");
|
|
return none;
|
|
}
|
|
|
|
next(ctx); # Skip ','.
|
|
|
|
skip(ctx, isSpace);
|
|
|
|
if ended(ctx): {
|
|
error("Source ended inside an array");
|
|
return none;
|
|
}
|
|
}
|
|
|
|
next(ctx);
|
|
return obj;
|
|
}
|
|
|
|
fun parseString(ctx) {
|
|
|
|
next(ctx); # Skip the '"'.
|
|
|
|
buff = '';
|
|
|
|
while not ended(ctx): {
|
|
|
|
if current(ctx) == '"':
|
|
break;
|
|
|
|
buff = strcat(buff, current(ctx));
|
|
|
|
next(ctx);
|
|
}
|
|
|
|
if ended(ctx): {
|
|
error("Source ended inside a string");
|
|
return none;
|
|
}
|
|
|
|
next(ctx); # Skip the '"'.
|
|
return buff;
|
|
}
|
|
|
|
fun parseNumber(ctx) {
|
|
|
|
assert(isDigit(current(ctx)));
|
|
|
|
# Number (int or float)
|
|
|
|
parsed = 0;
|
|
while not ended(ctx): {
|
|
if not isDigit(current(ctx)):
|
|
break;
|
|
parsed = parsed * 10 + unicode(current(ctx)) - unicode('0');
|
|
next(ctx);
|
|
}
|
|
|
|
if ended(ctx):
|
|
return parsed;
|
|
|
|
if current(ctx) == '.': {
|
|
|
|
next(ctx);
|
|
|
|
if ended(ctx): {
|
|
error("Source string ended unexpectedly after dot");
|
|
return none;
|
|
}
|
|
|
|
if not isDigit(current(ctx)): {
|
|
error("Got something other than a digit after dot");
|
|
return none;
|
|
}
|
|
|
|
fact = 1;
|
|
while not ended(ctx): {
|
|
if not isDigit(current(ctx)):
|
|
break;
|
|
fact = fact / 10.0;
|
|
parsed = parsed + fact * (unicode(current(ctx)) - unicode('0'));
|
|
next(ctx);
|
|
}
|
|
}
|
|
|
|
return parsed;
|
|
}
|
|
|
|
fun parseAny(ctx) {
|
|
|
|
print('Parsing value\n');
|
|
|
|
assert(not ended(ctx));
|
|
|
|
if current(ctx) == '[':
|
|
return parseArray(ctx);
|
|
|
|
if current(ctx) == '{':
|
|
return parseObject(ctx);
|
|
|
|
if current(ctx) == '"':
|
|
return parseString(ctx);
|
|
|
|
if isDigit(current(ctx)):
|
|
return parseNumber(ctx);
|
|
|
|
error("Not implemented yet");
|
|
}
|
|
|
|
|
|
#tests = ['', '1', '10', '1.10', '"jeje"', '[]', '[1,2,3]', ' [ ] ', ' [ 1 , 2 , 3 ]', '{}', ' { } ', '{"hoy":4}', ' { "hoy" : 4 } '];
|
|
|
|
#i = 0;
|
|
#while i < count(tests): {
|
|
# r = parse(tests[i]);
|
|
# if r == none:
|
|
# print('\nTest ', i, ' failed\n');
|
|
# else
|
|
# print(tests[i], ' -> ', r, '\n');
|
|
# i = i + 1;
|
|
#}
|
|
|
|
file = files.openFile('examples/large-file.json', files.READ);
|
|
if file == none:
|
|
error("Failed to open file");
|
|
|
|
buff = newBuffer(30000000);
|
|
if buff == none:
|
|
error("Failed to create buffer");
|
|
|
|
n = files.read(file, buff);
|
|
|
|
text = bufferToString(sliceBuffer(buff, 0, n));
|
|
|
|
print(parse(text));
|