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) and test(current(ctx)): 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) and current(ctx) != '"': { 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) and isDigit(current(ctx)): { 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) and isDigit(current(ctx)): { 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));