163 lines
4.5 KiB
Nim
163 lines
4.5 KiB
Nim
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
# SPDX-License-Identifier: Unlicense
|
|
|
|
import std/[base64, json, options, sets, sequtils, streams, strutils, tables, typetraits]
|
|
import ./values
|
|
|
|
proc `$`*(s: Symbol): string =
|
|
let sym = string s
|
|
if sym.len > 0 and sym[0] in {'A'..'z'} and not sym.anyIt(char(it) in { '\x00'..'\x19', '"', '\\', '|' }):
|
|
result = sym
|
|
else:
|
|
result = newStringOfCap(sym.len shl 1)
|
|
result.add('|')
|
|
for c in sym:
|
|
case c
|
|
of '\\':
|
|
result.add("\\\\")
|
|
of '/':
|
|
result.add("\\/")
|
|
of '\x08':
|
|
result.add("\\b")
|
|
of '\x0c':
|
|
result.add("\\f")
|
|
of '\x0a':
|
|
result.add("\\n")
|
|
of '\x0d':
|
|
result.add("\\r")
|
|
of '\x09':
|
|
result.add("\\t")
|
|
of '|':
|
|
result.add("\\|")
|
|
else:
|
|
result.add(c)
|
|
result.add('|')
|
|
|
|
type TextMode* = enum textPreserves, textJson
|
|
|
|
proc writeText*[E](stream: Stream; pr: Preserve[E]; mode = textPreserves) =
|
|
## Encode Preserves to a `Stream` as text.
|
|
if pr.embedded: write(stream, "#!")
|
|
case pr.kind:
|
|
of pkBoolean:
|
|
case pr.bool
|
|
of false: write(stream, "#f")
|
|
of true: write(stream, "#t")
|
|
of pkFloat:
|
|
write(stream, $pr.float)
|
|
write(stream, 'f')
|
|
of pkDouble:
|
|
write(stream, $pr.double)
|
|
of pkRegister:
|
|
write(stream, $pr.register)
|
|
of pkBigInt:
|
|
write(stream, $pr.bigint)
|
|
of pkString:
|
|
write(stream, escapeJson(pr.string))
|
|
of pkByteString:
|
|
if pr.bytes.allIt(char(it) in {' '..'!', '#'..'~'}):
|
|
write(stream, "#\"")
|
|
write(stream, cast[string](pr.bytes))
|
|
write(stream, '"')
|
|
else:
|
|
if pr.bytes.len > 64:
|
|
write(stream, "#[") #]#
|
|
write(stream, base64.encode(pr.bytes))
|
|
write(stream, ']')
|
|
else:
|
|
const alphabet = "0123456789abcdef"
|
|
write(stream, "#x\"")
|
|
for b in pr.bytes:
|
|
write(stream, alphabet[int(b shr 4)])
|
|
write(stream, alphabet[int(b and 0xf)])
|
|
write(stream, '"')
|
|
of pkSymbol:
|
|
let sym = pr.symbol.string
|
|
if sym.len > 0 and sym[0] in {'A'..'z'} and not sym.anyIt(char(it) in { '\x00'..'\x19', '"', '\\', '|' }):
|
|
write(stream, sym)
|
|
else:
|
|
write(stream, '|')
|
|
for c in sym:
|
|
case c
|
|
of '\\':
|
|
write(stream, "\\\\")
|
|
of '/':
|
|
write(stream, "\\/")
|
|
of '\x08':
|
|
write(stream, "\\b")
|
|
of '\x0c':
|
|
write(stream, "\\f")
|
|
of '\x0a':
|
|
write(stream, "\\n")
|
|
of '\x0d':
|
|
write(stream, "\\r")
|
|
of '\x09':
|
|
write(stream, "\\t")
|
|
of '|':
|
|
write(stream, "\\|")
|
|
else:
|
|
write(stream, c)
|
|
write(stream, '|')
|
|
of pkRecord:
|
|
assert(pr.record.len > 0)
|
|
write(stream, '<')
|
|
writeText(stream, pr.record[pr.record.high], mode)
|
|
for i in 0..<pr.record.high:
|
|
write(stream, ' ')
|
|
writeText(stream, pr.record[i], mode)
|
|
write(stream, '>')
|
|
of pkSequence:
|
|
write(stream, '[')
|
|
var insertSeperator: bool
|
|
case mode
|
|
of textPreserves:
|
|
for val in pr.sequence:
|
|
if insertSeperator: write(stream, ' ')
|
|
else: insertSeperator = true
|
|
writeText(stream, val, mode)
|
|
of textJson:
|
|
for val in pr.sequence:
|
|
if insertSeperator: write(stream, ',')
|
|
else: insertSeperator = true
|
|
writeText(stream, val, mode)
|
|
write(stream, ']')
|
|
of pkSet:
|
|
write(stream, "#{")
|
|
var insertSeperator: bool
|
|
for val in pr.set.items:
|
|
if insertSeperator: write(stream, ' ')
|
|
else: insertSeperator = true
|
|
writeText(stream, val, mode)
|
|
write(stream, '}')
|
|
of pkDictionary:
|
|
write(stream, '{')
|
|
var insertSeperator: bool
|
|
case mode
|
|
of textPreserves:
|
|
for (key, value) in pr.dict.items:
|
|
if insertSeperator: write(stream, ' ')
|
|
else: insertSeperator = true
|
|
writeText(stream, key, mode)
|
|
write(stream, ": ")
|
|
writeText(stream, value, mode)
|
|
of textJson:
|
|
for (key, value) in pr.dict.items:
|
|
if insertSeperator: write(stream, ',')
|
|
else: insertSeperator = true
|
|
writeText(stream, key, mode)
|
|
write(stream, ':')
|
|
writeText(stream, value, mode)
|
|
write(stream, '}')
|
|
of pkEmbedded:
|
|
write(stream, "#!")
|
|
when compiles($pr.embed) and not E is void:
|
|
write(stream, $pr.embed)
|
|
else:
|
|
write(stream, "…")
|
|
|
|
proc `$`*[E](pr: Preserve[E]): string =
|
|
## Generate the textual representation of ``pr``.
|
|
var stream = newStringStream()
|
|
writeText(stream, pr, textPreserves)
|
|
result = move stream.data
|