fun isSpace(c) return c == " " or c == "\t" or c == "\n"; fun isDigit(c) { u = unicode(c); d = u - unicode("0"); return d >= 0 and d <= 9; } fun skipSpaces(context) { # Skip zero or more whitespace characters. while context.cur < count(context.str) and isSpace(context.str[context.cur]): context.cur = context.cur + 1; } fun parseNull(context) { assert(context.cur < count(context) and context.str[context.cur] == 'n'); if context.cur+3 > count(context) or context.str[context.cur+1] != 'u' or context.str[context.cur+2] != 'l' or context.str[context.cur+3] != 'l': return none, "Unexpected token starting with \"n\""; context.cur = context.cur + count("null"); return none; } fun parseTrue(context) { assert(context.cur < count(context) and context.str[context.cur] == 't'); if context.cur+3 > count(context) or context.str[context.cur+1] != 'r' or context.str[context.cur+2] != 'u' or context.str[context.cur+3] != 'e': return none, "Unexpected token starting with \"t\""; context.cur = context.cur + count("true"); return true; } fun parseFalse(context) { assert(context.cur < count(context) and context.str[context.cur] == 'f'); if context.cur+3 > count(context) or context.str[context.cur+1] != 'a' or context.str[context.cur+2] != 'l' or context.str[context.cur+3] != 's' or context.str[context.cur+4] != 'e': return none, "Unexpected token starting with \"f\""; context.cur = context.cur + count("false"); return false; } fun skip(context, test_callback) { k = context.cur; while k < count(context) and test_callback(context.str[k]): k = k + 1; return k; } fun parseInteger(context) { assert(context.cur < count(context) and isDigit(context.str[context.cur])); buffer = 0; do { c = context.str[context.cur]; d = unicode(c) - unicode('0'); buffer = buffer * 10 + d; context.cur = context.cur + 1; } while context.cur < count(context) and isDigit(context.str[context.cur]); return buffer; } fun parseFloating(context) { assert(context.cur < count(context) and isDigit(context.str[context.cur])); # It's ensured by the caller (parseIntegerOrFloating) # that now the strings contains two sequences of # digits with a dot in the middle. buffer = 0.0; # Scan the integer part. do { c = context.str[context.cur]; d = unicode(c) - unicode('0'); buffer = buffer * 10 + d; context.cur = context.cur + 1; } while context.str[context.cur] != "."; assert(isDigit(context.str[context.cur+1])); # Scan the decimal part. q = 1; do { c = context.str[context.cur]; d = unicode(c) - unicode('0'); q = q / 10; buffer = buffer + q * d; context.cur = context.cur + 1; } while context.cur < count(context) and isDigit(context); return buffer; } fun parseIntegerOrFloating(context) { assert(context.cur < count(context) and isDigit(context.str[context.cur])); # Is the first digit sequence followed by a dot # and then one or more digits? k = skip(context, isDigit); if k+1 < count(context) and context.str[k] == "." and isDigit(context.str[k+1]): # It's a floating point value, then! return parseFloating(context); else # It's good old integer! return parseInteger(context); } fun parseValue(context) { if context.cur == count(context): return none, "Source ended where a value was expected"; c = context.str[context.cur]; if c == "{": val, err = parseObject(context); else if c == "[": val, err = parseArray(context); else if c == '"': val, err = parseString(context); else if isDigit(c): val, err = parseIntegerOrFloating(context); else if c == 'n': val, err = parseNull(context); else if c == 't': val, err = parseTrue(context); else if c == 'f': val, err = parseFalse(context); else { val = none; err = strcat("Unexepected character \"", c, "\" where a value was expected"); } return val, err; } fun parse(str) { context = {str: str, cur: 0}; skipSpaces(context); val, err = parseValue(context); return val, err; } val, err = parse('3.14'); print("val=[", val, "]\n"); print("err=[", err, "]\n");