json.oak
← Standard library
See on GitHub ↗
{
default: default
slice: slice
map: map
} := import('std')
{
space?: space?
join: join
} := import('str')
fn esc(c) if c {
'\t' -> '\\t'
'\n' -> '\\n'
'\r' -> '\\r'
'\f' -> '\\f'
'"' -> '\\"'
'\\' -> '\\\\'
_ -> c
}
fn escape(s) {
max := len(s)
fn sub(i, acc) if i {
max -> acc
_ -> sub(i + 1, acc << esc(s.(i)))
}
sub(0, '')
}
fn serialize(c) if type(c) {
:null, :empty, :function -> 'null'
:string -> '"' << escape(c) << '"'
:atom -> '"' << string(c) << '"'
:int, :float, :bool -> string(c)
:list -> '[' << c |> map(serialize) |> join(',') << ']'
:object -> '{' << keys(c) |> map(fn(k) '"' << escape(k) << '":' << serialize(c.(k))) |> join(',') << '}'
}
fn Reader(s) {
index := 0
err? := false
fn next {
index <- index + 1
default(s.(index - 1), '')
}
fn peek default(s.(index), '')
fn nextWord(n) if index + n > len(s) {
true -> {
index <- len(s)
?
}
_ -> {
word := s |> slice(index, index + n)
index <- index + n
word
}
}
fn forward {
fn sub if space?(peek()) -> {
index <- index + 1
sub()
}
sub()
}
{
next: next
peek: peek
forward: forward
nextWord: nextWord
done?: fn() index >= len(s)
err!: fn {
err? <- true
:error
}
err?: fn() err?
}
}
fn parseNull(r) if r.nextWord(4) {
'null' -> ?
_ -> r.err!()
}
fn parseString(r) {
next := r.next
next()
fn sub(acc) if c := next() {
'' -> r.err!()
'\\' -> sub(acc << if c := next() {
't' -> '\t'
'n' -> '\n'
'r' -> '\r'
'f' -> '\f'
'"' -> '"'
_ -> c
})
'"' -> acc
_ -> sub(acc << c)
}
sub('')
}
fn parseNumber(r) {
peek := r.peek
next := r.next
decimal? := false
negate? := if peek() {
'-' -> {
next()
true
}
_ -> false
}
fn sub(acc) if peek() {
'.' -> if decimal? {
true -> r.err!()
_ -> {
decimal? <- true
sub(acc << next())
}
}
'0', '1', '2', '3', '4'
'5', '6', '7', '8', '9' -> sub(acc << next())
_ -> acc
}
result := sub('')
if parsed := if decimal? {
true -> float(result)
_ -> int(result)
} {
? -> :error
_ -> if negate? {
true -> -parsed
_ -> parsed
}
}
}
fn parseTrue(r) if r.nextWord(4) {
'true' -> true
_ -> r.err!()
}
fn parseFalse(r) if r.nextWord(5) {
'false' -> false
_ -> r.err!()
}
fn parseList(r) {
err? := r.err?
peek := r.peek
next := r.next
forward := r.forward
next()
forward()
fn sub(acc) if err?() {
true -> :error
_ -> if peek() {
'' -> r.err!()
']' -> {
next()
acc
}
_ -> {
acc << _parseReader(r)
forward()
if peek() = ',' -> next()
forward()
sub(acc)
}
}
}
sub([])
}
fn parseObject(r) {
err? := r.err?
peek := r.peek
next := r.next
forward := r.forward
next()
forward()
fn sub(acc) if err?() {
true -> :error
_ -> if peek() {
'' -> r.err!()
'}' -> {
next()
acc
}
_ -> {
key := parseString(r)
if !err?() -> {
forward()
if peek() = ':' -> next()
val := _parseReader(r)
if !err?() -> {
forward()
if peek() = ',' -> next()
forward()
sub(acc.(key) := val)
}
}
}
}
}
sub({})
}
fn _parseReader(r) {
r.forward()
result := if r.peek() {
'n' -> parseNull(r)
'"' -> parseString(r)
't' -> parseTrue(r)
'f' -> parseFalse(r)
'[' -> parseList(r)
'{' -> parseObject(r)
_ -> parseNumber(r)
}
if r.err?() {
true -> :error
_ -> result
}
}
fn parse(s) Reader(s) |> _parseReader()