273 lines
6.7 KiB
Plaintext
273 lines
6.7 KiB
Plaintext
|
|
fun isSpace(c: String)
|
|
return c == " "
|
|
or c == "\t"
|
|
or c == "\n";
|
|
|
|
fun isDigit(c: String) {
|
|
u = unicode(c);
|
|
d = u - unicode("0");
|
|
return d >= 0 and d <= 9;
|
|
}
|
|
|
|
fun skipSpaces(context: Map) {
|
|
# 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: Map) {
|
|
|
|
assert(context.cur < count(context.str)
|
|
and context.str[context.cur] == 'n');
|
|
|
|
if context.cur+3 > count(context.str)
|
|
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: Map) {
|
|
|
|
assert(context.cur < count(context.str)
|
|
and context.str[context.cur] == 't');
|
|
|
|
if context.cur+3 > count(context.str)
|
|
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: Map) {
|
|
|
|
assert(context.cur < count(context.str)
|
|
and context.str[context.cur] == 'f');
|
|
|
|
if context.cur+3 > count(context.str)
|
|
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 dummy() {}
|
|
Func = type(dummy);
|
|
|
|
fun skip(context: Map, test_callback: Func) {
|
|
k = context.cur;
|
|
while k < count(context.str) and test_callback(context.str[k]):
|
|
k = k + 1;
|
|
return k;
|
|
}
|
|
|
|
fun parseInteger(context: Map) {
|
|
assert(context.cur < count(context.str) 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.str)
|
|
and isDigit(context.str[context.cur]);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
fun parseFloating(context: Map) {
|
|
assert(context.cur < count(context.str) 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.str)
|
|
and isDigit(context);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
fun parseIntegerOrFloating(context: Map) {
|
|
assert(context.cur < count(context.str) 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.str) 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 parseString(context: Map) {
|
|
|
|
assert(context.cur < count(context.str)
|
|
and context.str[context.cur] == '"');
|
|
|
|
context.cur = context.cur + 1;
|
|
|
|
buffer = "";
|
|
|
|
while context.cur < count(context.str)
|
|
and context.str[context.cur] != '"': {
|
|
# TODO: Support special characters such as \n, \t ecc
|
|
buffer = string.cat(buffer, context.str[context.cur]);
|
|
context.cur = context.cur + 1;
|
|
}
|
|
|
|
if context.cur == count(context.str):
|
|
return none, "Source string ended inside a string literal";
|
|
|
|
context.cur = context.cur + 1;
|
|
return buffer;
|
|
}
|
|
|
|
fun parseArray(context: Map) {
|
|
assert(context.cur < count(context.str)
|
|
and context.str[context.cur] == '[');
|
|
|
|
context.cur = context.cur + 1;
|
|
|
|
skipSpaces(context);
|
|
|
|
if context.cur == count(context.str):
|
|
return none, "Source string ended inside an array";
|
|
|
|
if context.str[context.cur] == ']':
|
|
return [];
|
|
|
|
list = [];
|
|
|
|
while true: {
|
|
|
|
item, err = parseValue(context);
|
|
if err != none:
|
|
return none, err;
|
|
|
|
list[count(list)] = item;
|
|
|
|
skipSpaces(context);
|
|
|
|
if context.cur == count(context.str):
|
|
return none, "Source string ended inside an array";
|
|
|
|
if context.str[context.cur] == ']':
|
|
break;
|
|
|
|
if context.str[context.cur] != ',':
|
|
return none, "Unexpected token inside an array, where either ',' or ']' were expected";
|
|
|
|
context.cur = context.cur + 1;
|
|
|
|
skipSpaces(context);
|
|
}
|
|
assert(context.cur < count(context.str)
|
|
and context.str[context.cur] == ']');
|
|
|
|
context.cur = context.cur + 1;
|
|
return list;
|
|
}
|
|
|
|
fun parseValue(context: Map) {
|
|
|
|
if context.cur == count(context.str):
|
|
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 = string.cat("Unexepected character \"", c, "\" where a value was expected");
|
|
}
|
|
return val, err;
|
|
}
|
|
|
|
fun parse(str: String) {
|
|
|
|
context = {str: str, cur: 0};
|
|
skipSpaces(context);
|
|
val, err = parseValue(context);
|
|
return val, err;
|
|
}
|
|
|
|
fun compareAny(A, B) {
|
|
|
|
fun compareLists(A: List, B: List) {
|
|
n = count(A);
|
|
if n != count(B):
|
|
return flase;
|
|
|
|
i = 0;
|
|
while i < n: {
|
|
if not compareAny(A[i], B[i]):
|
|
return false;
|
|
i = i + 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
T = type(A);
|
|
|
|
if T != type(B):
|
|
return false;
|
|
|
|
if T == List:
|
|
return compareLists(A, B);
|
|
|
|
if T == Map:
|
|
error("Maps aren't supported yet!");
|
|
|
|
return A == B;
|
|
}
|