~ehmry/dhall-nim

de0ba50c34be9f7eca6f38248db3d400450df48a — Emery Hemingway 9 months ago d7dabd0 master
Add cbor_to_dhall
4 files changed, 143 insertions(+), 1 deletions(-)

M dhall.nimble
A src/cbor_to_dhall.nim
A src/dhall/cbor_translation.nim
M src/dhall/terms.nim
M dhall.nimble => dhall.nimble +1 -1
@@ 6,7 6,7 @@ description   = "Dhall language evaluator"
license       = "ISC"
srcDir        = "src"
installExt    = @["nim"]
bin           = @["xml_to_dhall"]
bin           = @["cbor_to_dhall", "xml_to_dhall"]


# Dependencies

A src/cbor_to_dhall.nim => src/cbor_to_dhall.nim +50 -0
@@ 0,0 1,50 @@
# SPDX-FileCopyrightText: 2020 Emery Hemingway
#
# SPDX-License-Identifier: ISC

import dhall/binary, dhall/render, dhall/cbor_translation
import cbor
import std/parseopt

proc usage() =
  stderr.write "cbor_to_dhall [--binary|-b] < input.cbor > output.dhall"
  quit 1

proc main() =
  type Format = enum unicode, binary, error
  var format: Format
  for kind, key, _ in getopt():
    if format != error:
      case kind:
      of cmdLongOption:
        case key
        of "binary", "cbor", "encode":
          format = binary
        of "help":
          usage()
        else:
          format = error
      of cmdShortOption:
        case key
        of "b", "c", "e":
          format = binary
        of "h":
          usage()
        else:
          format = error
      else:
        format = error

  if format == error:
    echo "unhandled command flags"
    quit -1

  let buf = stdin.readAll
  if buf != "":
    let expr = buf.parseCbor.toDhall
    if format == binary:
      stdout.write expr.encode
    else:
      stdout.write $expr

main()

A src/dhall/cbor_translation.nim => src/dhall/cbor_translation.nim +83 -0
@@ 0,0 1,83 @@
import ./terms

import cbor

import std/sequtils, std/tables

# std/options

proc toDhall*(cbor: CborNode): Term =
  ## Convert a CBOR item to a Dhall Term.
  ## https://git.sr.ht/~ehmry/dhall-cbor/

  proc toApp(cbor: CborNode): Term =
    let CBOR = newVar"cbor"
    case cbor.kind
    of cborUnsigned, cborNegative:
      result = newApp(CBOR.newField("integer"), cbor.getInt.newInteger)
    of cborBytes:
      # TODO: base64 for compatibility with JSON?
      const alphabet = "0123456789abcdef"
      var hexString = newString(3 + cbor.bytes.len * 2)
      hexString[0] = 'h'
      hexString[1] = '\''
      hexString[hexString.high] = '\''
      for i, b in cbor.bytes:
        hexString[i*2+2] = alphabet[cbor.bytes[i] shl 4]
        hexString[i*2+3] = alphabet[cbor.bytes[i] and 0xf]
      result = newApp(CBOR.newField("bytes"), hexString.newTerm)
    of cborText:
      result = newApp(CBOR.newField("text"), cbor.text.newTerm)
    of cborArray:
      result = newApp(CBOR.newField("array"), cbor.seq.map(toApp).newTerm)
    of cborMap:
      var list = newSeqOfCap[Term](cbor.map.len)
      for k, v in cbor.map:
        list.add newRecordLiteral([("mapKey", k.toApp), ("mapValue", v.toApp)])
      result = newApp(CBOR.newField("map"), list.newTerm)
    of cborTag: discard
    of cborSimple:
      if cbor.isBool:
        result = newApp(CBOR.newField("bool"), cbor.getBool.newTerm)
      else:
        result = newApp(CBOR.newField("simple"), cbor.simple.int.newTerm)
    of cborFloat:
      result = newApp(CBOR.newField("double"), cbor.float.newTerm)
    of cborRaw:
      raiseAssert "unhandled raw cbor item"

  let
    CBOR = newVar"CBOR"
    arrayPi = newPi(newListType(CBOR), CBOR)
    boolPi = newPi(bBool.newTerm, CBOR)
    stringPi = newPi(bText.newTerm, CBOR)
    doublePi = newPi(bDouble.newTerm, CBOR)
    integerPi = newPi(bInteger.newTerm, CBOR)
    mapPi = newPi(
        newListType(newRecordType(
            [("mapKey", CBOR), ("mapValue", CBOR)])),
        CBOR)
    simplePi = newPi(bNatural.newTerm, CBOR)
    tagPi = newPi(bNatural.newTerm, newPi(CBOR, CBOR))

  result = Term(kind: tLambda,
    funcLabel: "CBOR",
    funcType: bType.newTerm,
    funcBody: Term(kind: tLambda,
        funcLabel: "cbor",
        funcType: newRecordType([
          ("array", arrayPi),
          ("bool", boolPi),
          ("bytes", stringPi),
          ("double", doublePi),
          ("integer", integerPi),
          ("map", mapPi),
          ("null", CBOR),
          ("simple", simplePi),
          ("tag", tagPi),
          ("text", stringPi),
          ("undefined", CBOR)]),
        funcBody: cbor.toApp))

proc toCbor*(expr: Value): CborNode =
  raiseAssert "not implemented"

M src/dhall/terms.nim => src/dhall/terms.nim +9 -0
@@ 524,12 524,21 @@ func newValue*(s: string): Value =
func newTerm*(i: Natural): Term =
  Term(kind: tNaturalLiteral, natural: initBigInt i)

func newTerm*(f: float64): Term =
  Term(kind: tDoubleLiteral, double: f)

func newTerm*(l: seq[Term]): Term =
  Term(kind: tList, list: l)

func newTerm*(b: bool): Term =
  Term(kind: tBoolLiteral, bool: b)

func newValue*(b: bool): Value =
  Value(kind: tBoolLiteral, bool: b)

func newInteger*(i: Natural): Term =
  Term(kind: tIntegerLiteral, integer: initBigInt(i))

func newInteger*(i: BigInt): Value =
  Value(kind: tIntegerLiteral, integer: i)