Files
Noja/examples/json.noja
T

285 lines
5.1 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):
return none, 'Source only contains whitespace';
res, err = parseAny(ctx);
return res, err;
}
fun parseArray(ctx) {
assert(current(ctx) == '[');
next(ctx); # Skip '['.
skip(ctx, isSpace);
if ended(ctx):
return none, "Source ended inside an array";
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):
return none, "Source ended inside an array";
if current(ctx) == ']':
break;
if current(ctx) != ',':
return none, "Bad character inside array";
next(ctx); # Skip ','.
skip(ctx, isSpace);
if ended(ctx):
return none, "Source ended inside an array";
}
next(ctx);
return arr;
}
fun parseObject(ctx) {
assert(current(ctx) == '{');
next(ctx); # Skip '{'.
skip(ctx, isSpace);
if ended(ctx):
return none, "Source ended inside an object";
obj = {};
if current(ctx) == '}': {
next(ctx);
return {};
}
while true: {
if current(ctx) != '"':
return none, "Bad character where a string was expected";
key = parseString(ctx);
if key == none:
return none;
skip(ctx, isSpace);
if current(ctx) != ':':
return none, "Bad character where ':' was expected";
next(ctx); # Skip ':'.
skip(ctx, isSpace);
child = parseAny(ctx);
if child == none:
return none;
obj[key] = child;
skip(ctx, isSpace);
if ended(ctx):
return none, "Source ended inside an arrays";
if current(ctx) == '}':
break;
if current(ctx) != ',':
return none, "Bad character inside array";
next(ctx); # Skip ','.
skip(ctx, isSpace);
if ended(ctx):
return none, "Source ended inside an array";
}
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):
return none, "Source ended inside a string";
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):
return none, "Source string ended unexpectedly after dot";
if not isDigit(current(ctx)):
return none, "Got something other than a digit after dot";
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) {
assert(not ended(ctx));
if current(ctx) == '[':
res, err = parseArray(ctx);
else if current(ctx) == '{':
res, err = parseObject(ctx);
else if current(ctx) == '"':
res, err = parseString(ctx);
else if isDigit(current(ctx)):
res, err = parseNumber(ctx);
else
error("Not implemented yet");
return res, err;
}
tests = [
'',
'1',
'10',
'1.@10',
'1.10',
'"jeje"',
'[]',
'[1,2,3]',
' [ ] ',
' [ 1 , 2 , 3 ]',
'{}',
' { } ',
'{"hoy":4}',
' { "hoy" : 4 } '
];
i = 0;
while i < count(tests): {
res, err = parse(tests[i]);
if res == none:
print('Test ', i, ' failed (', err, ')\n');
else
print('Test ', i, ' passed (', tests[i], ' -> ', res, ')\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));