95 lines
1.7 KiB
Plaintext
95 lines
1.7 KiB
Plaintext
|
|
fun is_space(c)
|
|
return c == ' '
|
|
or c == '\t'
|
|
or c == '\n';
|
|
|
|
fun is_digit(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, is_space);
|
|
|
|
if ended(ctx): {
|
|
print('Source only contains whitespace');
|
|
return none;
|
|
}
|
|
|
|
return parse_any_value(ctx);
|
|
}
|
|
|
|
fun parse_any_value(ctx) {
|
|
|
|
if is_digit(current(ctx)): {
|
|
# Number (int or float)
|
|
parsed = 0;
|
|
while not ended(ctx) and is_digit(current(ctx)): {
|
|
parsed = parsed * 10 + unicode(current(ctx)) - unicode('0');
|
|
next(ctx);
|
|
}
|
|
|
|
if current(ctx) == '.': {
|
|
|
|
next(ctx);
|
|
|
|
if ended(ctx):
|
|
# Source string ended unexpectedly after dot.
|
|
return none;
|
|
|
|
if not is_digit(current(ctx)):
|
|
# Got something other than a digit after dot.
|
|
return none;
|
|
|
|
fact = 1;
|
|
while not ended(ctx) and is_digit(current(ctx)): {
|
|
fact = fact / 10.0;
|
|
parsed = parsed + fact * (unicode(current(ctx)) - unicode('0'));
|
|
next(ctx);
|
|
}
|
|
}
|
|
|
|
return parsed;
|
|
}
|
|
|
|
assert(false);
|
|
}
|
|
|
|
|
|
tests = [
|
|
'',
|
|
'1'
|
|
];
|
|
|
|
i = 0;
|
|
while i < count(tests): {
|
|
parse(tests[i]);
|
|
i = i + 1;
|
|
}
|