preserves-nim/src/preserves/private/texts.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