From b099475d259607ac494c56ef8d92470e1dc88e4f Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Tue, 8 Jun 2021 12:14:56 +0200 Subject: [PATCH] Record utilities --- src/preserves.nim | 205 +++++++++++++++++++++++++++------------------- 1 file changed, 122 insertions(+), 83 deletions(-) diff --git a/src/preserves.nim b/src/preserves.nim index 568561e..3f84e90 100644 --- a/src/preserves.nim +++ b/src/preserves.nim @@ -4,11 +4,13 @@ import base64, endians, json, hashes, tables, streams import bigints type - PreserveKind = enum + PreserveKind* = enum pkBoolean, pkFloat, pkDouble, pkSignedInteger, pkBigInteger, pkString, pkByteString, pkSymbol, pkRecord, pkSequence, pkSet, pkDictionary, pkEmbedded - Preserve*[T] = object + Preserve*[T] {.acyclic.} = object + ## Type that stores a Preserves value. + ## ``T`` is the domain-specific type of "embedded" values, otherwise ``void``. case kind*: PreserveKind of pkBoolean: bool*: bool @@ -37,17 +39,111 @@ type of pkEmbedded: embedded*: T +proc `$`*[T](prs: Preserve[T]): string = + case prs.kind: + of pkBoolean: + case prs.bool + of false: result = "#f" + of true: result = "#t" + of pkFloat: + result = $prs.float & "f" + of pkDouble: + result = $prs.double + of pkSignedInteger: + result = $prs.int + of pkBigInteger: + result = $prs.bigint + of pkString: + result = escapeJson(prs.string) + of pkByteString: + result.add("#[") + result.add(base64.encode(prs.bytes)) + result.add(']') + of pkSymbol: + result.add(escapeJsonUnquoted(prs.symbol)) + of pkRecord: + result.add('<') + for val in prs.record: + result.add(' ') + result.add($val) + result.add('>') + of pkSequence: + result.add('[') + for i, val in prs.seq: + if i > 0: + result.add(' ') + result.add($val) + result.add(']') + of pkSet: + result.add("#{") + for val in prs.set.keys: + result.add($val) + result.add(' ') + if result.len > 1: + result.setLen(result.high) + result.add('}') + of pkDictionary: + result.add('{') + for (key, value) in prs.dict.pairs: + result.add($key) + result.add(" :") + result.add($value) + result.add(' ') + if result.len > 1: + result.setLen(result.high) + result.add('}') + of pkEmbedded: + when not T is void: + $prs.embedded + +proc toPreserve*(b: bool): Preserve[void] = + Preserve[void](kind: pkBoolean, bool: b) + +proc toPreserve*(n: SomeInteger): Preserve[void] = + Preserve[void](kind: pkSignedInteger, int: n.BiggestInt) + +proc toPreserve*(n: BigInt): Preserve[void] = + if initBigInt(low(BiggestInt)) < n and n < initBigInt(high(BiggestInt)): + var tmp: BiggestUint + for limb in n.limbs: + tmp = (tmp shl 32) or limb + if Negative in n.flags: + tmp = (not tmp) + 1 + result = Preserve[void](kind: pkSignedInteger, int: cast[BiggestInt](tmp)) + else: + result = Preserve[void](kind: pkBigInteger, bigint: n) + +proc toPreserve*(s: string): Preserve[void] = + Preserve[void](kind: pkString, string: s) + proc symbol*[T](s: string): Preserve[void] {.inline.} = ## Symbol constructor. Preserve[T](kind: pkSymbol, symbol: s) +proc record*[T](label: Preserve[T], args: varargs[Preserve[T]]): Preserve[T] = + ## Record constructor. + result = Preserve[T](kind: pkRecord, record: newSeqOfCap(1+args.len)) + result.record.add(label) + for arg in args: result.record.add(arg) + +proc record*[T](label: string, args: varargs[Preserve[T]]): Preserve[T] {.inline.} = + ## Record constructor that converts ``label`` to a symbol. + record(symbol[T](label), args) + proc label*[T](prs: Preserve[T]): Preserve[T] {.inline.} = + ## Return the label of a record value. prs.record[0] +proc arity*[T](prs: Preserve[T]): int {.inline.} = + ## Return the number of fields in a record value. + pred(prs.record.len) + proc fields*[T](prs: Preserve[T]): seq[Preserve[T]] {.inline.} = + ## Return the fields of a record value. prs.record[1..prs.record.high] -iterator fields*[T](prs: Preserve[T]): seq[Preserve[T]] = +iterator fields*[T](prs: Preserve[T]): Preserve[T] = + ## Iterate the fields of a record value. for i in 1..prs.record.high: yield prs.record[i] @@ -160,66 +256,6 @@ proc hash*[T](prs: Preserve[T]): Hash = h = h !& hash(prs.embedded) !$h -proc `$`*[T](prs: Preserve[T]): string = - case prs.kind: - of pkBoolean: - case prs.bool - of false: result = "#f" - of true: result = "#t" - of pkFloat: - result = $prs.float & "f" - of pkDouble: - result = $prs.double - of pkSignedInteger: - result = $prs.int - of pkBigInteger: - result = $prs.bigint - of pkString: - result = escapeJson(prs.string) - of pkByteString: - result.add("#[") - result.add(base64.encode(prs.bytes)) - result.add(']') - of pkSymbol: - result.add('|') - result.add(escapeJsonUnquoted(prs.symbol)) - result.add('|') - of pkRecord: - result.add('<') - result.add(prs.label) - for val in prs.fields: - result.add(' ') - result.add($val) - result.add('>') - of pkSequence: - result.add('[') - for i, val in prs.seq: - if i > 0: - result.add(' ') - result.add($val) - result.add(']') - of pkSet: - result.add("#{") - for val in prs.set.keys: - result.add($val) - result.add(' ') - if result.len > 1: - result.setLen(result.high) - result.add('}') - of pkDictionary: - result.add('{') - for (key, value) in prs.dict.pairs: - result.add($key) - result.add(" :") - result.add($value) - result.add(' ') - if result.len > 1: - result.setLen(result.high) - result.add('}') - of pkEmbedded: - when not T is void: - $prs.embedded - proc writeVarint(s: Stream; n: int) = var n = n while true: @@ -420,26 +456,6 @@ proc parsePreserve*(s: Stream): Preserve[void] = else: assertStream(false) -proc toPreserve*(b: bool): Preserve[void] = - Preserve[void](kind: pkBoolean, bool: b) - -proc toPreserve*(n: SomeInteger): Preserve[void] = - Preserve[void](kind: pkSignedInteger, int: n.BiggestInt) - -proc toPreserve*(n: BigInt): Preserve[void] = - if initBigInt(low(BiggestInt)) < n and n < initBigInt(high(BiggestInt)): - var tmp: BiggestUint - for limb in n.limbs: - tmp = (tmp shl 32) or limb - if Negative in n.flags: - tmp = (not tmp) + 1 - result = Preserve[void](kind: pkSignedInteger, int: cast[BiggestInt](tmp)) - else: - result = Preserve[void](kind: pkBigInteger, bigint: n) - -proc toPreserve*(s: string): Preserve[void] = - Preserve[void](kind: pkString, string: s) - proc toPreserve*(js: JsonNode): Preserve[void] = case js.kind of JString: @@ -506,3 +522,26 @@ proc toJson*[T](prs: Preserve[T]): JsonNode = result[key.string] = val.toJson of pkEmbedded: raise newException(ValueError, "cannot convert embedded value to JSON") + +type Record* = object + ## Type of a preserves record type. + label*: string + arity*: Natural + +proc init*[T](rec: Record, fields: varargs[Preserve[T]]): Preserve[T] = + ## Initialize a new record value. + assert(fields.len == rec.arity) + record(rec.label, fields) + +proc isClassOf*[T](rec: Record, val: Preserve[T]): bool = + ## Compare the label and arity of ``val`` to the record type ``rec``. + if val.kind == pkRecord: + let label = val.label + if label.kind == pkSymbol: + result = label.symbol == rec.label and rec.arity == val.arity + +proc classOf*[T](val: Preserve[T]): Record = + ## Derive the ``Record`` type of ``val``. + if val.kind != pkRecord or val.label.kind == pkSymbol: + raise newException(ValueError, "cannot derive class of non-record value") + Record(label: val.label.symbol, arity: val.arity)