~repos /plum

#treesitter#compiler#wasm

git clone https://pyrossh.dev/repos/plum.git

A statically typed, imperative programming language inspired by rust, python



libs/std/json.plum



module std/json
import std.{Option, Bool, Float, Str, List, Map, Result}
enum Json =
| None
| Bool
| Float
| Str
| List
| Map
^parse(src: Readable) -> Result(Json, JsonParseError) =
JsonParser.withSrc(src).parse()
class JsonParseError(Error) =
line: Int
pos: Int
token: Char
data JsonParser =
cur: Int = ""
pos: Int = 0
next: Int = ""
src: Readable
^withSrc(src: Readable) =
JsonParser::create()
.src(src)
parse() -> Result(Json, JsonParseError) =
match getNext()
'{' -> parseObject()
'[' -> parseList()
`"` -> parseStr()
'n' -> parseNone()
`t` | `f` -> parseBool()
0..9 -> parseFloat()
_ -> Err(createErr())
createErr() =
JsonParseError::create()
.line(0)
.pos(pos)
.token(cur)
parseNone() -> Result[None, JsonParseError] =
find("null")
check:
assert Json.parse("null") == None
parseBool() -> Result[Bool, JsonParseError] =
if find("true")
True
else if find("false")
False
check:
assert Json.parse("true") == True
assert Json.parse("false") == False
parseStr() -> Result[Str, JsonParseError] =
find("null")
check:
assert Json.parse(`"hello"`) == "hello"
parseFloat() -> Result[Float, JsonParseError] =
find("null")
parseInt() -> Result[Int, JsonParseError] =
find("Int")
check:
assert Json.parse("123") == 123
parseList() -> Result[List, JsonParseError] =
find("null")
check:
assert Json.parse("[]") == List[Json]()
assert Json.parse("[1, 2]") == List[Json](1, 2)
parseMap() -> Result[Map, JsonParseError] =
find("null")
check:
assert Json.parse("{}") == Map[Str, Json]()
assert Json.parse(`{"a": 1, "b": 2}`) == Map[Str, Json]("a" => 1, "b" => 2)
isSpace(c: Int) -> Bool =
{c == ' '} || {{c >= '\t'} && {c <= '\r'}}
isDelim(c: Int) -> Bool =
(c == ',') || (c == '}') || (c == ':') || (c == ']') || (_is_space(c)) || (c == 0)
isDigit(c: Int) -> Bool =
(c >= '0') && (c <= '9')
fn _get_next_raw(): U32 =>
try
_inc_pos()
let chr: (U32, U8) = _buf.utf32(_pos-1)?
cur = _next = chr._1
else
cur = _next = 0
end
fn _get_next(): U32 =>
try
repeat
_inc_pos()
let chr: (U32, U8) = _buf.utf32(_pos-1)?
cur = _next = chr._1
until not _is_space(cur) end
cur
else
cur = _next = 0
end
fn _expect(chr: U32, msg: String): Bool =>
if cur == chr then
_get_next()
true
else
_err(msg)
false
end
fn _inc_pos(i: ISize = 1) => _pos = _pos.add(i)
fn (p JsonParser) parseObject(): Map[str, Json] =
object := Map[str, Json]()
p.getNext() # '{'
while True
if p.cur != '"'
p._err("JSON: Object: expected '\"'\n")
break
val_key := p.parse_string()
if !p._expect(':', "JSON: Object: expected ':'\n")
break
val_val := Json(nil)
match p.cur
'{' ->
val_val = parse_object(p)
p._expect('}', "JSON: Object: expected '}'\n")
'[' ->
val_val = parse_array(p)
p._expect(']', "JSON: Object: expected ']'\n")
'"' ->
val_val = p.parseString()
'n' ->
val_val = try p._get_n()? end // null?
't' ->
val_val = try p._get_t()? end // true?
'f' ->
val_val = try p._get_f()? end // false?
ch ->
if p._is_digit(ch) || ch == '.'
val_val = p.parseNum()
else
p._err("invalid value\n")
break
object.set(val_key, val_val)
if p.cur == '}'
break
if !p._expect(',', "JSON: Object: expected ','\n")
printLn("failed at: %s\n".cstring(), val_key.cstring())
object
fn (p: JsonParser) parseList(): List[Json] =
p._get_next() // '['
while true
when p.cur
'{' -> _values.push(JSONObject.create_with_parser(p)); p._expect('}', "JSON: Array: expected object delimiter\n")
'[' -> _values.push(JSONArray.create_with_parser(p)); p._expect(']', "JSON: Array: expected array delimiter\n")
'"' -> _values.push(p.parseString())
't' ->
t: Bool := p._get_t()?
_values.push(t)
'f' ->
f: Bool := p._get_f()?
_values.push(f)
'n' ->
n: None := p._get_n()?
_values.push(n)
_ ->
if p._is_digit(p.cur)
let n = p.parseNum()
_values.push(n)
if p.cur == ']'
break
if !p._expect(',', "JSON: Array: expected ','\n")
break
fn (p: Parser) parseUtf16(): int =
result := 0
count := 0
while count <= 4
p._get_next_raw()
if _is_digit(cur)
result = ((result << 4) or (cur - '0').i32())
else if {cur >= 'a'} && {cur <= 'f'}
result = ((result << 4) or ((cur - 'a').i32() + 10))
else if (cur >= 'A') && (cur <= 'F')
result = ((result << 4) or ((cur - 'A').i32() + 10))
count = count+1
result
fn (p: JsonParser) parseStr(): str =
result := ""
while true
p._get_next_raw()
match cur
0 -> _err("JSON: parseString: expected '\"'\n"); return ""
'"' -> _get_next(); break
'\\' ->
match _next
'\\' -> result.push('\\'); p._get_next_raw()
'"' -> result.push('"'); p._get_next_raw()
'\'' -> result.push('\''); p._get_next_raw()
'/' -> result.push('/'); p._get_next_raw()
'b' -> result.push('\b'); p._get_next_raw()
'f' -> result.push('\f'); p._get_next_raw()
'n' -> result.push('\n'); p._get_next_raw()
'r' -> result.push('\r'); p._get_next_raw()
't' -> result.push('\t'); p._get_next_raw()
'u' ->
_get_next_raw()
ures := p.parseUtf16()
if ures < 0
_err("invalid utf escape\n")
break
result.push_utf32(ures.u32())
_ ->
result.push_utf32(cur.u32())
result
fn (p: JsonParser) parseNum(): int | float =
result := ""
is_float := false
if cur == '-'
result.push('-')
_get_next_raw()
if cur == '.'
result.push('0')
result.push('.')
is_float = true
_get_next_raw()
else
while _is_digit(cur)
result.push(cur.u8())
_get_next_raw()
if cur == '.'
result.push('.')
_get_next_raw()
while _is_digit(cur)
result.push(cur.u8())
_get_next_raw()
is_exp := when cur
'e' ->
result.push('e'); _get_next_raw()
true
'E' ->
result.push('E'); _get_next_raw()
true
_ -> false
if is_exp
sign := when cur
'+' ->
result.push('+'); _get_next_raw()
true
'-' ->
result.push('-'); _get_next_raw()
true
_ -> false
if sign
while _is_digit(cur)
result.push(cur.u8())
_get_next_raw()
res := is_float ? result.f64() : result.i64()
if _is_space(cur)
_get_next() // skip to the next non-whitespace character
res
fn _get_n(): None? =>
_get_next_raw()
if cur == 'u' then
_get_nu()?
else
error
end
fn _get_nu(): None? =>
_get_next_raw()
if cur == 'l' then
_get_nul()?
else
error
end
fn _get_nul(): None? =>
_get_next_raw()
if cur == 'l' then
_get_next()
None
else
error
end
fn _get_t(): bool? =
_get_next_raw()
if cur == 'r' then
_get_tr()?
else
error
end
fn _get_tr(): bool? =
_get_next_raw()
if cur == 'u' then
_get_tru()?
else
error
end
fn _get_tru(): bool? =
_get_next_raw()
if cur == 'e' then
_get_next()
true
else
error
end
fn _get_f(): bool? =
_get_next_raw()
if cur == 'a' then
_get_fa()?
else
error
end
fn _get_fa(): bool? =
_get_next_raw()
if cur == 'l' then
_get_fal()?
else
error
end
fn _get_fal(): bool? =
_get_next_raw()
if cur == 's' then
_get_fals()?
else
error
end
fn _get_fals(): bool? =
_get_next_raw()
if cur == 'e' then
_get_next()
false
else
error
end