Add P-Expressions reader
This commit is contained in:
parent
ca0cebcefd
commit
beedc07510
|
@ -0,0 +1,92 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import
|
||||||
|
npeg,
|
||||||
|
../preserves, ./pegs
|
||||||
|
|
||||||
|
type
|
||||||
|
Frame = tuple[value: Value, pos: int]
|
||||||
|
Stack = seq[Frame]
|
||||||
|
|
||||||
|
proc shrink(stack: var Stack; n: int) = stack.setLen(stack.len - n)
|
||||||
|
|
||||||
|
template pushStack(v: Value) = stack.add((v, capture[0].si))
|
||||||
|
|
||||||
|
template collectEntries(result: var seq[Value]; stack: var Stack) =
|
||||||
|
for frame in stack.mitems:
|
||||||
|
if frame.pos > capture[0].si:
|
||||||
|
result.add frame.value.move
|
||||||
|
stack.shrink result.len
|
||||||
|
|
||||||
|
proc parseExpressions*(text: string): seq[Value] =
|
||||||
|
let parser = peg("Document", stack: Stack):
|
||||||
|
|
||||||
|
ws <- *{ ' ', '\t', '\r', '\n' }
|
||||||
|
|
||||||
|
Document <- *Expr * ws * !1
|
||||||
|
|
||||||
|
Annotation <-
|
||||||
|
('@' * SimpleExpr) |
|
||||||
|
('#' * {'\x20', '\x09', '\x21'} * @{'\r','\n'})
|
||||||
|
|
||||||
|
Trailer <- *(ws * Annotation)
|
||||||
|
|
||||||
|
Expr <- ws * (Punct | SimpleExpr) * Trailer
|
||||||
|
|
||||||
|
Punct <- {',', ';'} | +':':
|
||||||
|
pushStack initRecord("p", toSymbol $0)
|
||||||
|
|
||||||
|
SimpleExpr <-
|
||||||
|
Atom |
|
||||||
|
Compound |
|
||||||
|
Embedded |
|
||||||
|
Annotated
|
||||||
|
|
||||||
|
Embedded <- "#:" * SimpleExpr:
|
||||||
|
pushstack stack.pop.value.embed
|
||||||
|
|
||||||
|
Annotated <- Annotation * SimpleExpr
|
||||||
|
|
||||||
|
Compound <- Sequence | Record | Block | Group | Set
|
||||||
|
|
||||||
|
Sequence <- '[' * *Expr * ws * ']':
|
||||||
|
var pr = Value(kind: pkSequence)
|
||||||
|
collectEntries(pr.sequence, stack)
|
||||||
|
pushStack pr
|
||||||
|
|
||||||
|
Record <- '<' * *Expr * ws * '>':
|
||||||
|
var pr = Value(kind: pkRecord)
|
||||||
|
collectEntries(pr.record, stack)
|
||||||
|
pr.record.add toSymbol"r"
|
||||||
|
pushStack pr
|
||||||
|
|
||||||
|
Block <- '{' * *Expr * ws * '}':
|
||||||
|
var pr = Value(kind: pkRecord)
|
||||||
|
collectEntries(pr.record, stack)
|
||||||
|
pr.record.add toSymbol"b"
|
||||||
|
pushStack pr
|
||||||
|
|
||||||
|
Group <- '(' * *Expr * ws * ')':
|
||||||
|
var pr = Value(kind: pkRecord)
|
||||||
|
collectEntries(pr.record, stack)
|
||||||
|
pr.record.add toSymbol"g"
|
||||||
|
pushStack pr
|
||||||
|
|
||||||
|
Set <- "#{" * *Expr * ws * '}':
|
||||||
|
var pr = Value(kind: pkRecord)
|
||||||
|
collectEntries(pr.record, stack)
|
||||||
|
pr.record.add toSymbol"s"
|
||||||
|
pushStack pr
|
||||||
|
|
||||||
|
Atom <- Preserves.Atom:
|
||||||
|
pushStack parsePreserves($0)
|
||||||
|
|
||||||
|
var stack: Stack
|
||||||
|
let match = parser.match(text, stack)
|
||||||
|
if not match.ok:
|
||||||
|
raise newException(ValueError, "failed to parse Preserves Expressions:\n" & text[match.matchMax..text.high])
|
||||||
|
|
||||||
|
result.setLen stack.len
|
||||||
|
for i, _ in result:
|
||||||
|
result[i] = move stack[i].value
|
|
@ -0,0 +1,80 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import
|
||||||
|
std/unittest,
|
||||||
|
preserves, preserves/expressions
|
||||||
|
|
||||||
|
template testExpr(name, code, cntrl: string) {.dirty.} =
|
||||||
|
test name:
|
||||||
|
checkpoint code
|
||||||
|
let
|
||||||
|
pr = parsePreserves cntrl
|
||||||
|
exprs = parseExpressions code
|
||||||
|
checkpoint $(exprs.toPreserves)
|
||||||
|
check exprs.len == 1
|
||||||
|
let px = exprs[0]
|
||||||
|
check px == pr
|
||||||
|
|
||||||
|
suite "expression":
|
||||||
|
|
||||||
|
testExpr "date", """
|
||||||
|
<date 1821 (lookup-month "February") 3>
|
||||||
|
""", """
|
||||||
|
<r date 1821 <g lookup-month "February"> 3>
|
||||||
|
"""
|
||||||
|
|
||||||
|
testExpr "r", "<>", "<r>"
|
||||||
|
|
||||||
|
testExpr "begin",
|
||||||
|
"""(begin (println! (+ 1 2)) (+ 3 4))""",
|
||||||
|
"""<g begin <g println! <g + 1 2>> <g + 3 4>>"""
|
||||||
|
|
||||||
|
testExpr "g",
|
||||||
|
"""()""", """<g>"""
|
||||||
|
|
||||||
|
testExpr "groups",
|
||||||
|
"""[() () ()]""", """[<g>, <g>, <g>]"""
|
||||||
|
|
||||||
|
testExpr "loop", """{
|
||||||
|
setUp();
|
||||||
|
# Now enter the loop
|
||||||
|
loop: {
|
||||||
|
greet("World");
|
||||||
|
}
|
||||||
|
tearDown();
|
||||||
|
}
|
||||||
|
""", """
|
||||||
|
<b
|
||||||
|
setUp <g> <p |;|>
|
||||||
|
# Now enter the loop
|
||||||
|
loop <p |:|> <b
|
||||||
|
greet <g "World"> <p |;|>
|
||||||
|
>
|
||||||
|
tearDown <g> <p |;|>
|
||||||
|
>"""
|
||||||
|
|
||||||
|
testExpr "+", """
|
||||||
|
[1 + 2.0, print "Hello", predicate: #t, foo, #:remote, bar]
|
||||||
|
""", """
|
||||||
|
[1 + 2.0 <p |,|> print "Hello" <p |,|> predicate <p |:|> #t <p |,|>
|
||||||
|
foo <p |,|> #:remote <p |,|> bar]"""
|
||||||
|
|
||||||
|
testExpr "set",
|
||||||
|
"""#{1 2 3}""", """<s 1 2 3>"""
|
||||||
|
|
||||||
|
testExpr "group-set",
|
||||||
|
"""#{(read) (read) (read)}""",
|
||||||
|
"""<s <g read> <g read> <g read>>"""
|
||||||
|
|
||||||
|
testExpr "block", """
|
||||||
|
{
|
||||||
|
optional name: string,
|
||||||
|
address: Address,
|
||||||
|
}
|
||||||
|
""", """
|
||||||
|
<b
|
||||||
|
optional name <p |:|> string <p |,|>
|
||||||
|
address <p |:|> Address <p |,|>
|
||||||
|
>
|
||||||
|
"""
|
Loading…
Reference in New Issue