debug.oak
← Standard library
See on GitHub ↗
{
println: stdPrintln
default: default
range: range
toHex: toHex
map: map
each: each
some: some
every: every
values: values
reduce: reduce
entries: entries
} := import('std')
{
letter?: letter?
digit?: digit?
join: join
padStart: padStart
} := import('str')
math := import('math')
{
sort!: sort!
} := import('sort')
{
format: format
} := import('fmt')
fn _validIdent?(s) s |> every(fn(c, i) if i {
0 -> letter?(c) | c = '_' | c = '?' | c = '!'
_ -> letter?(c) | digit?(c) | c = '_' | c = '?' | c = '!'
})
fn _numeral?(s) s |> every(digit?)
fn _primitive?(x) if type(x) {
:null, :empty, :bool, :int, :float, :string, :atom
:function -> true
_ -> false
}
fn inspect(x, options) {
{
indent: indentUnit
depth: depth
maxLine: maxLine
maxList: maxList
maxObject: maxObject
} := options |> default({})
indentUnit := indentUnit |> default(' ')
depth := depth |> default(-1)
maxLine := maxLine |> default(80)
maxList := maxList |> default(16)
maxObject := maxObject |> default(3)
fn inspectObjectKey(key) if {
_validIdent?(key), _numeral?(key) -> key
_ -> inspectLine(key, -1)
}
fn inspectAbbreviated(x) if type(x) {
:list -> '[ {{0}} items... ]' |> format(len(x))
:object -> '{ {{0}} entries... }' |> format(len(x))
}
fn inspectLine(x, depth) if type(x) {
:null, :empty, :bool, :int, :float -> string(x)
:string -> '\'' + (x |> map(fn(c) if c {
'\\' -> '\\\\'
'\'' -> '\\\''
'\n' -> '\\n'
'\r' -> '\\r'
'\f' -> '\\f'
'\t' -> '\\t'
_ -> if {
codepoint(c) < 32
codepoint(c) > 126 -> '\\x' << toHex(codepoint(c)) |> padStart(2, '0')
_ -> c
}
})) + '\''
:atom -> if _validIdent?(payload := string(x)) {
true -> ':' + string(payload)
_ -> 'atom({{0}})' |> format(inspectLine(payload))
}
:function -> 'fn { ... }'
:list -> '[' + (x |> map(fn(y) inspectLine(y, depth)) |> join(', ')) + ']'
:object -> if len(x) {
0 -> '{}'
_ -> '{ ' + {
entries(x) |>
sort!(0) |>
map(fn(entry) inspectObjectKey(entry.0) + ': ' + inspectLine(entry.1, depth)) |>
join(', ')
} + ' }'
}
}
fn inspectMulti(x, indent, depth) {
innerIndent := indent + indentUnit
if type(x) {
:list -> x |> reduce('[', fn(lines, item) {
lines << '\n' + innerIndent + inspectAny(item, innerIndent, depth)
}) << '\n' + indent + ']'
:object -> entries(x) |> sort!(0) |> reduce('{', fn(lines, entry) {
lines << '\n' + innerIndent + inspectObjectKey(entry.0) + ': ' +
inspectAny(entry.1, innerIndent, depth)
}) << '\n' + indent + '}'
}
}
fn inspectAny(x, indent, depth) {
line := inspectLine(x, depth - 1)
overflows? := len(line) + len(indent) > maxLine
if {
_primitive?(x) -> line
depth = 0 -> inspectAbbreviated(x)
overflows? -> inspectMulti(x, indent, depth - 1)
type(x) = :list -> if {
len(x) > maxList
x |> some(fn(y) !_primitive?(y)) -> inspectMulti(x, indent, depth - 1)
_ -> line
}
type(x) = :object -> if {
len(x) > maxObject
x |> values() |> some(fn(y) !_primitive?(y)) -> inspectMulti(x, indent, depth - 1)
_ -> line
}
}
}
inspectAny(x, '', depth)
}
fn println(x, options) stdPrintln(inspect(x, options))
fn bar(n) {
n := math.max(n * 8, 0)
whole := int(n / 8)
rem := n % 8
graph := range(whole) |> map(fn '█') |> join() + if math.round(rem) {
0 -> ''
1 -> '▏'
2 -> '▎'
3 -> '▍'
4 -> '▌'
5 -> '▋'
6 -> '▊'
7 -> '▉'
8 -> '█'
}
if graph = '' & n > 0 {
true -> '▏'
_ -> graph
}
}
fn histo(xs, opts) if len(xs) {
0 -> ''
_ -> {
opts := opts |> default({})
min := opts.min |> default(math.min(xs...))
max := opts.max |> default(math.max(xs...))
bars := opts.bars |> default(10) |> math.min(len(xs)) |> math.max(1)
label := opts.label |> default(?)
cols := opts.cols |> default(80)
unit := (max - min) / bars
buckets := range(bars) |> map(fn 0)
xs |> each(fn(x) if x >= min & x < max -> {
i := int((x - min) / unit)
buckets.(i) := buckets.(i) + 1
})
maxcount := math.max(buckets...)
labels := buckets |> map(string)
maxlen := math.max(labels |> map(len)...)
if label = :start -> labels := labels |> map(fn(l) l |> padStart(maxlen, ' '))
buckets |> map(fn(n, i) {
b := n |> math.scale(0, maxcount, 0, cols) |> bar()
if label {
:start -> labels.(i) + ' ' + b
:end -> b + ' ' + labels.(i)
_ -> b
}
}) |> join('\n')
}
}