# 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..') 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