Add P-Expressions reader

This commit is contained in:
Emery Hemingway 2024-04-26 13:13:42 +02:00
parent ca0cebcefd
commit 3b9c164737
2 changed files with 175 additions and 0 deletions

View File

@ -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

83
tests/test_p_exprs.nim Normal file
View File

@ -0,0 +1,83 @@
# 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 |,|>
>
"""