preserves-nim/src/preserves/parse.nim

99 lines
3.2 KiB
Nim
Raw Normal View History

2021-07-16 17:11:19 +00:00
# SPDX-License-Identifier: ISC
import std/[base64, parseutils, sets, strutils, tables]
import npeg
import ../preserves, ./pegs
type
Frame = tuple[value: Preserve, pos: int]
Stack = seq[Frame]
proc shrink(stack: var Stack; n: int) = stack.setLen(stack.len - n)
template pushStack(v: Preserve) = stack.add((v, capture[0].si))
const pegParser = peg("Document", stack: Stack):
# Override rules from pegs.nim
Document <- Preserves.Document
Preserves.Record <- Preserves.Record:
var
record: seq[Preserve]
labelOff: int
while stack[labelOff].pos < capture[0].si:
inc labelOff
for i in labelOff.succ..stack.high:
record.add(move stack[i].value)
record.add(move stack[labelOff].value)
stack.shrink record.len
pushStack Preserve(kind: pkRecord, record: move record)
Preserves.Sequence <- Preserves.Sequence:
var sequence: seq[Preserve]
for frame in stack.mitems:
if frame.pos > capture[0].si:
sequence.add(move frame.value)
stack.shrink sequence.len
pushStack Preserve(kind: pkSequence, sequence: move sequence)
Preserves.Dictionary <- Preserves.Dictionary:
var dict: Table[Preserve, Preserve]
for i in countDown(stack.high.pred, 0, 2):
if stack[i].pos < capture[0].si: break
dict[move stack[i].value] = move stack[i.succ].value
stack.shrink 2*dict.len
pushStack Preserve(kind: pkDictionary, dict: move dict)
Preserves.Set <- Preserves.Set:
var set: HashSet[Preserve]
for frame in stack.mitems:
if frame.pos > capture[0].si:
set.incl(move frame.value)
stack.shrink set.len
pushStack Preserve(kind: pkSet, set: move set)
Preserves.Boolean <- Preserves.Boolean:
case $0
of "#f": pushStack Preserve(kind: pkBoolean)
of "#t": pushStack Preserve(kind: pkBoolean, bool: true)
else: discard
Preserves.Float <- Preserves.Float:
pushStack Preserve(kind: pkFloat, float: parseFloat($1))
Preserves.Double <- Preserves.Double:
pushStack Preserve(kind: pkDouble)
let i = stack.high
discard parseBiggestFloat($0, stack[i].value.double)
Preserves.SignedInteger <- Preserves.SignedInteger:
pushStack Preserve(kind: pkSignedInteger, int: parseInt($0))
Preserves.String <- Preserves.String:
pushStack Preserve(kind: pkString, string: unescape($0))
Preserves.charByteString <- Preserves.charByteString:
let s = unescape($1)
pushStack Preserve(kind: pkByteString, bytes: cast[seq[byte]](s))
Preserves.hexByteString <- Preserves.hexByteString:
pushStack Preserve(kind: pkByteString, bytes: cast[seq[byte]](parseHexStr($1)))
Preserves.b64ByteString <- Preserves.b64ByteString:
pushStack Preserve(kind: pkByteString, bytes: cast[seq[byte]](base64.decode($1)))
Preserves.Symbol <- Preserves.Symbol:
pushStack Preserve(kind: pkSymbol, symbol: $0)
Preserves.Compact <- Preserves.Compact:
pushStack decodePreserves(stack.pop.value.bytes)
proc parsePreserves*(text: string): Preserve {.gcsafe.} =
var stack: Stack
let match = pegParser.match(text, stack)
if not match.ok:
raise newException(ValueError, "failed to parse Preserves:\n" & text[match.matchMax..text.high])
assert(stack.len == 1)
stack.pop.value