Compare commits
4 Commits
6487ef65d0
...
06898e4ec1
Author | SHA1 | Date |
---|---|---|
Emery Hemingway | 06898e4ec1 | |
Emery Hemingway | 2aaa588f6a | |
Emery Hemingway | e0b569e465 | |
Emery Hemingway | 13d3995507 |
|
@ -19,7 +19,7 @@ proc `!`*(typ: static typedesc): Pattern {.inline.} =
|
||||||
patterns.dropType(typ)
|
patterns.dropType(typ)
|
||||||
|
|
||||||
proc `?`*[T](val: T): Pattern {.inline.} =
|
proc `?`*[T](val: T): Pattern {.inline.} =
|
||||||
patterns.grab[T](val)
|
patterns.drop[T](val)
|
||||||
|
|
||||||
proc `?:`*(typ: static typedesc): Pattern {.inline.} =
|
proc `?:`*(typ: static typedesc): Pattern {.inline.} =
|
||||||
patterns.grabType(typ)
|
patterns.grabType(typ)
|
||||||
|
@ -30,9 +30,6 @@ proc `?:`*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Patt
|
||||||
proc `?:`*(typ: static typedesc; bindings: sink openArray[(Value, Pattern)]): Pattern {.inline.} =
|
proc `?:`*(typ: static typedesc; bindings: sink openArray[(Value, Pattern)]): Pattern {.inline.} =
|
||||||
patterns.grab(typ, bindings)
|
patterns.grab(typ, bindings)
|
||||||
|
|
||||||
proc `??`*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern {.inline.} =
|
|
||||||
patterns.inject(pat, bindings)
|
|
||||||
|
|
||||||
type
|
type
|
||||||
PublishProc = proc (turn: var Turn; v: Value; h: Handle) {.closure.}
|
PublishProc = proc (turn: var Turn; v: Value; h: Handle) {.closure.}
|
||||||
RetractProc = proc (turn: var Turn; h: Handle) {.closure.}
|
RetractProc = proc (turn: var Turn; h: Handle) {.closure.}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
include_rules
|
include_rules
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/..
|
NIM_FLAGS += --path:$(TUP_CWD)/..
|
||||||
: foreach *.nim |> !nim_check |>
|
: foreach *.nim |> !nim_check |>
|
||||||
|
: patterns.nim |> !nim_bin |>
|
||||||
|
|
|
@ -16,8 +16,6 @@ when defined(posix):
|
||||||
proc echo(args: varargs[string, `$`]) {.used.} =
|
proc echo(args: varargs[string, `$`]) {.used.} =
|
||||||
stderr.writeLine(args)
|
stderr.writeLine(args)
|
||||||
|
|
||||||
proc `$`(b: seq[byte]): string = cast[string](b)
|
|
||||||
|
|
||||||
proc badRequest(conn: Connection; msg: string) =
|
proc badRequest(conn: Connection; msg: string) =
|
||||||
conn.send(SupportedVersion & " 400 " & msg, endOfMessage = true)
|
conn.send(SupportedVersion & " 400 " & msg, endOfMessage = true)
|
||||||
|
|
||||||
|
@ -33,6 +31,9 @@ proc extractQuery(s: var string): Table[Symbol, seq[QueryValue]] =
|
||||||
|
|
||||||
proc parseRequest(conn: Connection; text: string): (int, HttpRequest) =
|
proc parseRequest(conn: Connection; text: string): (int, HttpRequest) =
|
||||||
## Parse an `HttpRequest` request out of a `text` from a `Connection`.
|
## Parse an `HttpRequest` request out of a `text` from a `Connection`.
|
||||||
|
result[1].host = RequestHost(orKind: RequestHostKind.absent)
|
||||||
|
result[1].body = RequestBody(orKind: RequestBodyKind.absent)
|
||||||
|
|
||||||
var
|
var
|
||||||
token: string
|
token: string
|
||||||
off: int
|
off: int
|
||||||
|
@ -91,13 +92,13 @@ proc parseRequest(conn: Connection; text: string): (int, HttpRequest) =
|
||||||
for e in vals.mitems:
|
for e in vals.mitems:
|
||||||
e = e.toLowerAscii
|
e = e.toLowerAscii
|
||||||
if k == Symbol"host":
|
if k == Symbol"host":
|
||||||
result[1].host = e
|
result[1].host = RequestHost(orKind: RequestHostKind.`present`, present: e)
|
||||||
if v == "": v = move e
|
if v == "": v = move e
|
||||||
else:
|
else:
|
||||||
v.add ", "
|
v.add ", "
|
||||||
v.add e
|
v.add e
|
||||||
if k == Symbol"host":
|
if k == Symbol"host":
|
||||||
result[1].host = v
|
result[1].host = RequestHost(orKind: RequestHostKind.`present`, present: v)
|
||||||
result[1].headers[k] = v
|
result[1].headers[k] = v
|
||||||
|
|
||||||
result[0] = off
|
result[0] = off
|
||||||
|
@ -140,6 +141,9 @@ proc send(ses: Session; chunk: Chunk) =
|
||||||
of ChunkKind.bytes:
|
of ChunkKind.bytes:
|
||||||
ses.send(chunk.bytes)
|
ses.send(chunk.bytes)
|
||||||
|
|
||||||
|
func `==`(s: string; rh: RequestHost): bool =
|
||||||
|
rh.orKind == RequestHostKind.present and rh.present == s
|
||||||
|
|
||||||
proc match(b: HttpBinding, r: HttpRequest): bool =
|
proc match(b: HttpBinding, r: HttpRequest): bool =
|
||||||
## Check if `HttpBinding` `b` matches `HttpRequest` `r`.
|
## Check if `HttpBinding` `b` matches `HttpRequest` `r`.
|
||||||
result =
|
result =
|
||||||
|
@ -241,9 +245,8 @@ proc service(turn: var Turn; exch: Exchange) =
|
||||||
if handler.isNone:
|
if handler.isNone:
|
||||||
stop(turn)
|
stop(turn)
|
||||||
else:
|
else:
|
||||||
let
|
let cap = newCap(turn, exch)
|
||||||
cap = newCap(turn, exch)
|
publish(turn, handler.get, HttpContext(
|
||||||
ctx = publish(turn, handler.get, HttpContext(
|
|
||||||
req: exch.req,
|
req: exch.req,
|
||||||
res: embed cap,
|
res: embed cap,
|
||||||
))
|
))
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/times
|
import std/[tables, times]
|
||||||
import preserves
|
import preserves
|
||||||
import ../../syndicate, ../bags, ../protocols/[timer, dataspace]
|
import ../../syndicate, ../protocols/[timer, dataspace]
|
||||||
|
|
||||||
when defined(solo5):
|
when defined(solo5):
|
||||||
import solo5_dispatcher
|
import solo5_dispatcher
|
||||||
|
@ -27,7 +27,7 @@ when defined(solo5):
|
||||||
## Owning facet of driver.
|
## Owning facet of driver.
|
||||||
target: Cap
|
target: Cap
|
||||||
## Destination for LaterThan assertions.
|
## Destination for LaterThan assertions.
|
||||||
deadlines: Bag[float]
|
deadlines: Table[float, Facet]
|
||||||
## Deadlines that other actors are observing.
|
## Deadlines that other actors are observing.
|
||||||
|
|
||||||
proc spawnTimerDriver(facet: Facet; cap: Cap): TimerDriver =
|
proc spawnTimerDriver(facet: Facet; cap: Cap): TimerDriver =
|
||||||
|
@ -35,11 +35,12 @@ when defined(solo5):
|
||||||
|
|
||||||
proc await(driver: TimerDriver; deadline: float) {.solo5dispatch.} =
|
proc await(driver: TimerDriver; deadline: float) {.solo5dispatch.} =
|
||||||
yieldUntil(deadline)
|
yieldUntil(deadline)
|
||||||
if deadline in driver.deadlines:
|
let facet = driver.deadlines.getOrDefault(deadline)
|
||||||
|
if not facet.isNil:
|
||||||
# check if the deadline is still observed
|
# check if the deadline is still observed
|
||||||
proc turnWork(turn: var Turn) =
|
proc turnWork(turn: var Turn) =
|
||||||
discard publish(turn, driver.target, LaterThan(seconds: deadline))
|
discard publish(turn, driver.target, LaterThan(seconds: deadline))
|
||||||
run(driver.facet, turnWork)
|
run(facet, turnWork)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
import std/[oserrors, posix, sets]
|
import std/[oserrors, posix, sets]
|
||||||
|
@ -84,7 +85,7 @@ else:
|
||||||
## Owning facet of driver.
|
## Owning facet of driver.
|
||||||
target: Cap
|
target: Cap
|
||||||
## Destination for LaterThan assertions.
|
## Destination for LaterThan assertions.
|
||||||
deadlines: Bag[float]
|
deadlines: Table[float, Facet]
|
||||||
## Deadlines that other actors are observing.
|
## Deadlines that other actors are observing.
|
||||||
timers: HashSet[cint]
|
timers: HashSet[cint]
|
||||||
# TODO: use a single timer descriptor
|
# TODO: use a single timer descriptor
|
||||||
|
@ -100,7 +101,7 @@ else:
|
||||||
proc earliestFloat(driver: TimerDriver): float =
|
proc earliestFloat(driver: TimerDriver): float =
|
||||||
assert driver.deadlines.len > 0
|
assert driver.deadlines.len > 0
|
||||||
result = high float
|
result = high float
|
||||||
for deadline in driver.deadlines:
|
for deadline in driver.deadlines.keys:
|
||||||
if deadline < result:
|
if deadline < result:
|
||||||
result = deadline
|
result = deadline
|
||||||
|
|
||||||
|
@ -119,11 +120,12 @@ else:
|
||||||
# Check if the timer is expired which
|
# Check if the timer is expired which
|
||||||
# could happen before waiting.
|
# could happen before waiting.
|
||||||
wait(FD fd, Read)
|
wait(FD fd, Read)
|
||||||
if deadline in driver.deadlines:
|
let facet = driver.deadlines.getOrDefault(deadline)
|
||||||
|
if not facet.isNil:
|
||||||
# Check if the deadline is still observed.
|
# Check if the deadline is still observed.
|
||||||
proc turnWork(turn: var Turn) =
|
proc turnWork(turn: var Turn) =
|
||||||
discard publish(turn, driver.target, LaterThan(seconds: deadline))
|
discard publish(turn, driver.target, LaterThan(seconds: deadline))
|
||||||
run(driver.facet, turnWork)
|
run(facet, turnWork)
|
||||||
discard close(fd)
|
discard close(fd)
|
||||||
driver.timers.excl(fd)
|
driver.timers.excl(fd)
|
||||||
|
|
||||||
|
@ -132,16 +134,15 @@ proc spawnTimerDriver*(turn: var Turn; ds: Cap): Actor {.discardable.} =
|
||||||
## dataspace observations of timeouts on `ds`.
|
## dataspace observations of timeouts on `ds`.
|
||||||
linkActor(turn, "timers") do (turn: var Turn):
|
linkActor(turn, "timers") do (turn: var Turn):
|
||||||
let driver = spawnTimerDriver(turn.facet, ds)
|
let driver = spawnTimerDriver(turn.facet, ds)
|
||||||
let pat = inject(grab Observe(pattern: dropType LaterThan), {0: grabLit()})
|
let pat = observePattern(!LaterThan, {@[0.toPreserves]: grabLit()})
|
||||||
during(turn, ds, pat) do (deadline: float):
|
during(turn, ds, pat) do (deadline: float):
|
||||||
if change(driver.deadlines, deadline, +1) == cdAbsentToPresent:
|
driver.deadlines[deadline] = turn.facet
|
||||||
discard trampoline(whelp await(driver, deadline))
|
discard trampoline(whelp await(driver, deadline))
|
||||||
do:
|
do:
|
||||||
discard change(driver.deadlines, deadline, -1, clamp = true)
|
driver.deadlines.del deadline
|
||||||
# TODO: retract assertions that are unobserved.
|
|
||||||
|
|
||||||
proc after*(turn: var Turn; ds: Cap; dur: Duration; act: TurnAction) =
|
proc after*(turn: var Turn; ds: Cap; dur: Duration; act: TurnAction) =
|
||||||
## Execute `act` after some duration of time.
|
## Execute `act` after some duration of time.
|
||||||
var later = wallFloat() + dur.inMilliseconds.float / 1_000.0
|
var later = wallFloat() + dur.inMilliseconds.float / 1_000.0
|
||||||
onPublish(turn, ds, grab LaterThan(seconds: later)):
|
onPublish(turn, ds, ?LaterThan(seconds: later)):
|
||||||
act(turn)
|
act(turn)
|
||||||
|
|
|
@ -1,70 +1,46 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[algorithm, assertions, options, sequtils, tables, typetraits]
|
import std/[assertions, options, tables, typetraits]
|
||||||
|
|
||||||
import preserves
|
import preserves
|
||||||
import ./protocols/dataspacePatterns
|
import ./protocols/[dataspacePatterns, dataspace]
|
||||||
from ./actors import Cap
|
from ./actors import Cap
|
||||||
|
|
||||||
export dataspacePatterns.`$`, PatternKind, DCompoundKind, AnyAtomKind
|
export dataspacePatterns.`$`, AnyAtomKind, GroupTypeKind, PatternKind
|
||||||
|
|
||||||
type
|
type
|
||||||
AnyAtom = dataspacePatterns.AnyAtom
|
|
||||||
DBind = dataspacePatterns.DBind
|
|
||||||
DCompound = dataspacePatterns.DCompound
|
|
||||||
DCompoundArr = dataspacePatterns.DCompoundArr
|
|
||||||
DCompoundDict = dataspacePatterns.DCompoundDict
|
|
||||||
DCompoundRec = dataspacePatterns.DCompoundRec
|
|
||||||
DLit = dataspacePatterns.DLit
|
|
||||||
Pattern* = dataspacePatterns.Pattern
|
Pattern* = dataspacePatterns.Pattern
|
||||||
|
|
||||||
iterator orderedEntries*(dict: DCompoundDict): (Value, Pattern) =
|
proc toPattern(b: sink PatternBind): Pattern =
|
||||||
## Iterate a `DCompoundDict` in Preserves order.
|
Pattern(orKind: PatternKind.`bind`, `bind`: b)
|
||||||
## Values captured from a dictionary are represented as an
|
|
||||||
## array of values ordered by their former key, so using an
|
|
||||||
## ordered iterator is sometimes essential.
|
|
||||||
var keys = dict.entries.keys.toSeq
|
|
||||||
sort(keys, preserves.cmp)
|
|
||||||
for k in keys:
|
|
||||||
yield(k, dict.entries.getOrDefault(k))
|
|
||||||
# getOrDefault doesn't raise and we know the keys will match
|
|
||||||
|
|
||||||
proc toPattern(d: sink DBind): Pattern =
|
proc toPattern(l: sink PatternLit): Pattern =
|
||||||
Pattern(orKind: PatternKind.DBind, dbind: d)
|
Pattern(orKind: PatternKind.`lit`, lit: l)
|
||||||
|
|
||||||
proc toPattern(d: sink DLit): Pattern =
|
proc toPattern(g: sink PatternGroup): Pattern =
|
||||||
Pattern(orKind: PatternKind.DLit, dlit: d)
|
Pattern(orKind: PatternKind.`group`, group: g)
|
||||||
|
|
||||||
proc toPattern(aa: sink AnyAtom): Pattern =
|
proc toPattern(a: sink AnyAtom): Pattern =
|
||||||
DLit(value: aa).toPattern
|
PatternLit(value: a).toPattern
|
||||||
|
|
||||||
proc toPattern(d: sink DCompound): Pattern =
|
proc grab*(p: sink Pattern): Pattern =
|
||||||
Pattern(orKind: PatternKind.DCompound, dcompound: d)
|
PatternBind(pattern: p).toPattern
|
||||||
|
|
||||||
proc toPattern(d: sink DCompoundRec): Pattern =
|
proc drop*(): Pattern = Pattern(orKind: PatternKind.`discard`)
|
||||||
DCompound(orKind: DCompoundKind.rec, rec: d).toPattern
|
|
||||||
|
|
||||||
proc toPattern(d: sink DCompoundArr): Pattern =
|
|
||||||
DCompound(orKind: DCompoundKind.arr, arr: d).toPattern
|
|
||||||
|
|
||||||
proc toPattern(d: sink DCompoundDict): Pattern =
|
|
||||||
DCompound(orKind: DCompoundKind.dict, dict: d).toPattern
|
|
||||||
|
|
||||||
proc drop*(): Pattern {.inline.} = Pattern(orKind: PatternKind.DDiscard)
|
|
||||||
## Create a pattern to match any value without capture.
|
## Create a pattern to match any value without capture.
|
||||||
|
|
||||||
proc grab*(): Pattern {.inline.} = DBind(pattern: drop()).toPattern
|
proc grab*(): Pattern = drop().grab()
|
||||||
## Create a pattern to capture any value.
|
## Create a pattern to capture any value.
|
||||||
|
|
||||||
proc grab*(pr: Value): Pattern =
|
proc drop*(pr: Value): Pattern =
|
||||||
## Convert a `Preserve` value to a `Pattern`.
|
## Convert a `Preserve` value to a `Pattern`.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
import preserves
|
import preserves
|
||||||
check:
|
check:
|
||||||
$(grab parsePreserves"""<foo "bar" #"00" [0 1 2.0] {maybe: #t} <_>>""") ==
|
$("""<foo "bar" #"00" [0 1 2.0] {maybe: #t} <_>>""".parsePreserves.drop) ==
|
||||||
"""<rec foo [<lit "bar"> <lit #"00"> <arr [<lit 0> <lit 1> <lit 2.0>]> <dict {maybe: <lit #t>}> <_>]>"""
|
"""<group <rec foo> {0: <lit "bar"> 1: <lit #"00"> 2: <group <arr> {0: <lit 0> 1: <lit 1> 2: <lit 2.0>}> 3: <group <dict> {maybe: <lit #t>}> 4: <_>}>"""
|
||||||
|
|
||||||
case pr.kind
|
case pr.kind
|
||||||
of pkBoolean:
|
of pkBoolean:
|
||||||
|
@ -73,43 +49,65 @@ proc grab*(pr: Value): Pattern =
|
||||||
AnyAtom(orKind: AnyAtomKind.`double`, double: pr.float).toPattern
|
AnyAtom(orKind: AnyAtomKind.`double`, double: pr.float).toPattern
|
||||||
of pkRegister:
|
of pkRegister:
|
||||||
AnyAtom(orKind: AnyAtomKind.`int`, int: pr.register).toPattern
|
AnyAtom(orKind: AnyAtomKind.`int`, int: pr.register).toPattern
|
||||||
|
of pkBigInt:
|
||||||
|
raiseAssert "cannot make a pattern over a big integer"
|
||||||
of pkString:
|
of pkString:
|
||||||
AnyAtom(orKind: AnyAtomKind.`string`, string: pr.string).toPattern
|
AnyAtom(orKind: AnyAtomKind.`string`, string: pr.string).toPattern
|
||||||
of pkByteString:
|
of pkByteString:
|
||||||
AnyAtom(orKind: AnyAtomKind.`bytes`, bytes: pr.bytes).toPattern
|
AnyAtom(orKind: AnyAtomKind.`bytes`, bytes: pr.bytes).toPattern
|
||||||
of pkSymbol:
|
of pkSymbol:
|
||||||
AnyAtom(orKind: AnyAtomKind.`symbol`, symbol: pr.symbol).toPattern
|
AnyAtom(orKind: AnyAtomKind.`symbol`, symbol: pr.symbol).toPattern
|
||||||
|
|
||||||
of pkRecord:
|
of pkRecord:
|
||||||
if (pr.isRecord("_") and pr.arity == 0) or (pr.isRecord("bind") and pr.arity == 1):
|
if pr.isRecord("_", 0):
|
||||||
drop()
|
drop()
|
||||||
|
elif pr.isRecord("bind", 1):
|
||||||
|
pr.fields[0].drop
|
||||||
else:
|
else:
|
||||||
DCompoundRec(
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.rec))
|
||||||
label: pr.label,
|
group.`type`.rec.label = pr.label
|
||||||
fields: map[Value, Pattern](pr.fields, grab)).toPattern
|
var i: int
|
||||||
|
for v in pr.fields:
|
||||||
|
group.entries[toPreserves i] = drop v
|
||||||
|
inc i
|
||||||
|
group.toPattern
|
||||||
|
|
||||||
of pkSequence:
|
of pkSequence:
|
||||||
DCompoundArr(items: map(pr.sequence, grab)).toPattern
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.arr))
|
||||||
|
for i, v in pr.sequence:
|
||||||
|
group.entries[toPreserves i] = drop v
|
||||||
|
group.toPattern
|
||||||
|
|
||||||
of pkSet:
|
of pkSet:
|
||||||
raiseAssert "cannot construct a pattern over a set literal"
|
raiseAssert "cannot construct a pattern over a set literal"
|
||||||
|
|
||||||
of pkDictionary:
|
of pkDictionary:
|
||||||
var dict = DCompoundDict()
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.dict))
|
||||||
for key, val in pr.pairs: dict.entries[key] = grab val
|
for key, val in pr.pairs:
|
||||||
dict.toPattern
|
group.entries[key] = drop val
|
||||||
|
group.toPattern
|
||||||
|
|
||||||
of pkEmbedded:
|
of pkEmbedded:
|
||||||
if pr.embeddedRef.isNil: drop()
|
if pr.embeddedRef.isNil: drop()
|
||||||
else:
|
else:
|
||||||
AnyAtom(orKind: AnyAtomKind.`embedded`, embedded: pr.embeddedRef).toPattern
|
AnyAtom(orKind: AnyAtomKind.`embedded`, embedded: pr.embeddedRef).toPattern
|
||||||
else:
|
#else:
|
||||||
raise newException(ValueError, "cannot generate a pattern for unhandled Value type")
|
# raise newException(ValueError, "cannot generate a pattern for unhandled Value type")
|
||||||
|
|
||||||
proc grab*[T](x: T): Pattern =
|
proc drop*[T](x: T): Pattern =
|
||||||
## Construct a `Pattern` from value of type `T`.
|
## Construct a `Pattern` from value of type `T`.
|
||||||
|
## This proc is called `drop` because the value `x` is matched but discarded.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
check:
|
check:
|
||||||
$grab(true) == "<lit #t>"
|
$drop(true) == "<lit #t>"
|
||||||
$grab(3.14) == "<lit 3.14>"
|
$drop(3.14) == "<lit 3.14>"
|
||||||
$grab([0, 1, 2, 3]) == "<arr [<lit 0> <lit 1> <lit 2> <lit 3>]>"
|
$drop([0, 1, 2, 3]) == "<group <arr> {0: <lit 0> 1: <lit 1> 2: <lit 2> 3: <lit 3>}>"
|
||||||
grab(x.toPreserves)
|
drop(x.toPreserves)
|
||||||
|
|
||||||
|
proc grab*[T](x: T): Pattern {.
|
||||||
|
deprecated: "use drop unless you wish to capture the provided value".} =
|
||||||
|
PatternBind(pattern: drop x).toPattern
|
||||||
|
|
||||||
proc grabType*(typ: static typedesc): Pattern =
|
proc grabType*(typ: static typedesc): Pattern =
|
||||||
## Derive a `Pattern` from type `typ`.
|
## Derive a `Pattern` from type `typ`.
|
||||||
|
@ -120,40 +118,41 @@ proc grabType*(typ: static typedesc): Pattern =
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
check:
|
check:
|
||||||
$grabType(array[3, int]) ==
|
$grabType(array[3, int]) ==
|
||||||
"""<arr [<bind <_>> <bind <_>> <bind <_>>]>"""
|
"""<group <arr> {0: <bind <_>> 1: <bind <_>> 2: <bind <_>> 3: <bind <_>>}>"""
|
||||||
type
|
type
|
||||||
Point = tuple[x: int; y: int]
|
Point = tuple[x: int; y: int]
|
||||||
Rect {.preservesRecord: "rect".} = tuple[a: Point; B: Point]
|
Rect {.preservesRecord: "rect".} = tuple[a: Point; B: Point]
|
||||||
ColoredRect {.preservesDictionary.} = tuple[color: string; rect: Rect]
|
ColoredRect {.preservesDictionary.} = tuple[color: string; rect: Rect]
|
||||||
check:
|
check:
|
||||||
$(grabType Point) ==
|
$(grabType Point) ==
|
||||||
"<arr [<bind <_>> <bind <_>>]>"
|
"<group <arr> {0: <bind <_>> 1: <bind <_>>}>"
|
||||||
$(grabType Rect) ==
|
$(grabType Rect) ==
|
||||||
"<rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>"
|
"<group <rec rect> {0: <group <arr> {0: <bind <_>> 1: <bind <_>>}> 1: <group <arr> {0: <bind <_>> 1: <bind <_>>}>}>"
|
||||||
$(grabType ColoredRect) ==
|
$(grabType ColoredRect) ==
|
||||||
"<dict {color: <bind <_>> rect: <rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>}>"
|
"<group <dict> {color: <bind <_>> rect: <group <rec rect> {0: <group <arr> {0: <bind <_>> 1: <bind <_>>}> 1: <group <arr> {0: <bind <_>> 1: <bind <_>>}>}>}>"
|
||||||
when typ is ref:
|
when typ is ref:
|
||||||
grabType(pointerBase(typ))
|
grabType(pointerBase(typ))
|
||||||
elif typ.hasPreservesRecordPragma:
|
elif typ.hasPreservesRecordPragma:
|
||||||
var rec = DCompoundRec(label: typ.recordLabel.toSymbol)
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||||
|
group.`type`.rec.label = typ.recordLabel.toSymbol
|
||||||
for _, f in fieldPairs(default typ):
|
for _, f in fieldPairs(default typ):
|
||||||
add(rec.fields, grabType(typeof f))
|
group.entries[group.entries.len.toPreserves] = grabType(typeof f)
|
||||||
result = rec.toPattern
|
group.toPattern
|
||||||
elif typ.hasPreservesDictionaryPragma:
|
elif typ.hasPreservesDictionaryPragma:
|
||||||
var dict = DCompoundDict()
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||||
for key, val in fieldPairs(default typ):
|
for key, val in fieldPairs(default typ):
|
||||||
dict.entries[key.toSymbol] = grabType(typeof val)
|
group.entries[key.toSymbol] = grabType(typeof val)
|
||||||
dict.toPattern
|
group.toPattern
|
||||||
elif typ is tuple:
|
elif typ is tuple:
|
||||||
var arr = DCompoundArr()
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||||
for _, f in fieldPairs(default typ):
|
for _, f in fieldPairs(default typ):
|
||||||
add(arr.items, grabType(typeof f))
|
group.entries[group.entries.len.toPreserves] = grabType(typeof f)
|
||||||
arr.toPattern
|
group.toPattern
|
||||||
elif typ is array:
|
elif typ is array:
|
||||||
var arr = DCompoundArr()
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||||
arr.items.setLen(len(typ))
|
for i in 0..len(typ):
|
||||||
for e in arr.items.mitems: e = grab()
|
group.entries[toPreserves i] = grab()
|
||||||
arr.toPattern
|
group.toPattern
|
||||||
else:
|
else:
|
||||||
grab()
|
grab()
|
||||||
|
|
||||||
|
@ -166,59 +165,47 @@ proc dropType*(typ: static typedesc): Pattern =
|
||||||
when typ is ref:
|
when typ is ref:
|
||||||
dropType(pointerBase(typ))
|
dropType(pointerBase(typ))
|
||||||
elif typ.hasPreservesRecordPragma:
|
elif typ.hasPreservesRecordPragma:
|
||||||
var rec = DCompoundRec(label: typ.recordLabel.toSymbol)
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||||
rec.fields.setLen(fieldCount typ)
|
group.`type`.rec.label = typ.recordLabel.toSymbol
|
||||||
for i, _ in rec.fields:
|
let high = typ.fieldCount.pred
|
||||||
rec.fields[i] = drop()
|
if high >= 0: group.entries[high.toPreserves] = drop()
|
||||||
result = rec.toPattern
|
group.toPattern
|
||||||
elif typ.hasPreservesDictionaryPragma:
|
elif typ.hasPreservesDictionaryPragma:
|
||||||
DCompoundDict().toPattern
|
PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`)).toPattern
|
||||||
elif typ is tuple:
|
elif typ is tuple or typ is array:
|
||||||
var arr = DCompoundArr()
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||||
arr.items.setLen(len typ)
|
let high = typ.fieldCount.pred
|
||||||
for i, _ in arr.items:
|
if high >= 0: group.entries[high.toPreserves] = drop()
|
||||||
arr.items[i] = drop()
|
group.toPattern
|
||||||
arr.toPattern
|
|
||||||
elif typ is array:
|
|
||||||
var arr = DCompoundArr()
|
|
||||||
arr.items.setLen(len(typ))
|
|
||||||
for e in arr.items.mitems: e = drop()
|
|
||||||
arr.toPattern
|
|
||||||
else:
|
else:
|
||||||
drop()
|
drop()
|
||||||
|
|
||||||
proc lookup(bindings: openArray[(int, Pattern)]; i: int): Pattern =
|
proc bindEntries(group: var PatternGroup; bindings: openArray[(int, Pattern)]) =
|
||||||
for (j, b) in bindings:
|
## Set `bindings` for a `group`.
|
||||||
if i == j: return b
|
for (i, pat) in bindings: group.entries[toPreserves i] = pat
|
||||||
return drop()
|
|
||||||
|
|
||||||
proc grab*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern =
|
proc grab*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern =
|
||||||
## Construct a `Pattern` from type `typ` with pattern `bindings` by integer offset.
|
## Construct a `Pattern` from type `typ` with pattern `bindings` by integer offset.
|
||||||
when typ is ptr | ref:
|
when typ is ptr | ref:
|
||||||
grab(pointerBase(typ), bindings)
|
grab(pointerBase(typ), bindings)
|
||||||
elif typ.hasPreservesRecordPragma:
|
elif typ.hasPreservesRecordPragma:
|
||||||
var rec = DCompoundRec(label: typ.recordLabel.toSymbol)
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||||
rec.fields.setLen(fieldCount typ)
|
group.`type`.rec.label = typ.recordLabel.toSymbol
|
||||||
var i: int
|
bindEntries(group, bindings)
|
||||||
for _, f in fieldPairs(default typ):
|
group.toPattern
|
||||||
rec.fields[i] = lookup(bindings, i)
|
|
||||||
inc i
|
|
||||||
result = rec.toPattern
|
|
||||||
elif typ is tuple:
|
elif typ is tuple:
|
||||||
var arr = DCompoundArr()
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||||
arr.items.setLen(fieldCount typ)
|
bindEntries(group, bindings)
|
||||||
var i: int
|
group.toPattern
|
||||||
for _, f in fieldPairs(default typ):
|
|
||||||
arr.items[i] = lookup(bindings, i)
|
|
||||||
inc i
|
|
||||||
result = arr.toPattern
|
|
||||||
else:
|
else:
|
||||||
{.error: "grab with indexed bindings not implemented for " & $typ.}
|
{.error: "grab with indexed bindings not implemented for " & $typ.}
|
||||||
|
|
||||||
proc grab*(typ: static typedesc; bindings: sink openArray[(Value, Pattern)]): Pattern =
|
proc grab*(typ: static typedesc; bindings: sink openArray[(Value, Pattern)]): Pattern =
|
||||||
## Construct a `Pattern` from type `typ` with dictionary field `bindings`.
|
## Construct a `Pattern` from type `typ` with dictionary field `bindings`.
|
||||||
when typ.hasPreservesDictionaryPragma:
|
when typ.hasPreservesDictionaryPragma:
|
||||||
DCompoundDict(entries: bindings.toTable).toPattern
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||||
|
for key, val in bindinds: group.entries[key] = val
|
||||||
|
group.toPattern
|
||||||
else:
|
else:
|
||||||
{.error: "grab with dictionary bindings not implemented for " & $typ.}
|
{.error: "grab with dictionary bindings not implemented for " & $typ.}
|
||||||
|
|
||||||
|
@ -226,11 +213,11 @@ proc grabLit*(): Pattern =
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
check:
|
check:
|
||||||
$grabLit() == """<rec lit [<bind <_>>]>"""
|
$grabLit() == """<group <rec lit> {0: <bind <_>>}>"""
|
||||||
grabType(dataspacePatterns.DLit)
|
grabType(dataspacePatterns.PatternLit)
|
||||||
|
|
||||||
proc grabDict*(): Pattern =
|
proc grabDict*(): Pattern =
|
||||||
grabType(dataspacePatterns.DCompoundDict)
|
grabType(dataspacePatterns.GroupTypeDict)
|
||||||
|
|
||||||
proc unpackLiterals*(pr: Value): Value =
|
proc unpackLiterals*(pr: Value): Value =
|
||||||
result = pr
|
result = pr
|
||||||
|
@ -238,64 +225,42 @@ proc unpackLiterals*(pr: Value): Value =
|
||||||
if pr.isRecord("lit", 1) or pr.isRecord("dict", 1) or pr.isRecord("arr", 1) or pr.isRecord("set", 1):
|
if pr.isRecord("lit", 1) or pr.isRecord("dict", 1) or pr.isRecord("arr", 1) or pr.isRecord("set", 1):
|
||||||
pr = pr.record[0]
|
pr = pr.record[0]
|
||||||
|
|
||||||
proc inject*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern =
|
proc inject*(pattern: sink Pattern; p: Pattern; path: varargs[Value, toPreserves]): Pattern =
|
||||||
## Construct a `Pattern` from `pat` with injected overrides from `bindings`.
|
## Inject `p` inside `pattern` at `path`.
|
||||||
## Injects are made at offsets indexed by the discard (`<_>`) patterns in `pat`.
|
## Injects are made at offsets indexed by the discard (`<_>`) patterns in `pat`.
|
||||||
proc inject(pat: Pattern; bindings: openArray[(int, Pattern)]; offset: var int): Pattern =
|
proc inject(pat: var Pattern; path: openarray[Value]) =
|
||||||
case pat.orKind
|
if len(path) == 0:
|
||||||
of PatternKind.DDiscard:
|
pat = p
|
||||||
result = pat
|
elif pat.orKind != PatternKind.`group`:
|
||||||
for (off, injection) in bindings:
|
raise newException(ValueError, "cannot inject along specified path")
|
||||||
if off == offset:
|
else:
|
||||||
result = injection
|
inject(pat.group.entries[path[0]], path[1..path.high])
|
||||||
break
|
result = pattern
|
||||||
inc offset
|
inject(result, path)
|
||||||
of PatternKind.DBind:
|
|
||||||
let bindOff = offset
|
|
||||||
result = pat
|
|
||||||
result.dbind.pattern = inject(pat.dbind.pattern, bindings, offset)
|
|
||||||
if result.orKind == PatternKind.DBind:
|
|
||||||
for (off, injection) in bindings:
|
|
||||||
if (off == bindOff) and (result.dbind.pattern == injection):
|
|
||||||
result = result.dbind.pattern
|
|
||||||
break # promote the injected pattern over the bind
|
|
||||||
of PatternKind.DLit:
|
|
||||||
result = pat
|
|
||||||
of PatternKind.DCompound:
|
|
||||||
result = pat
|
|
||||||
case pat.dcompound.orKind
|
|
||||||
of DCompoundKind.rec:
|
|
||||||
var fields = mapIt(pat.dcompound.rec.fields):
|
|
||||||
inject(it, bindings, offset)
|
|
||||||
result = DCompoundRec(label: result.dcompound.rec.label, fields: fields).toPattern
|
|
||||||
of DCompoundKind.arr:
|
|
||||||
var items = mapIt(pat.dcompound.arr.items):
|
|
||||||
inject(it, bindings, offset)
|
|
||||||
result = DCompoundArr(items: items).toPattern
|
|
||||||
of DCompoundKind.dict:
|
|
||||||
var dict = DCompoundDict()
|
|
||||||
for key, val in pat.dcompound.dict.entries:
|
|
||||||
dict.entries[key] = inject(val, bindings, offset)
|
|
||||||
result = toPattern(dict)
|
|
||||||
var offset = 0
|
|
||||||
inject(pat, bindings, offset)
|
|
||||||
|
|
||||||
proc inject*(pat: Pattern; bindings: openArray[(Value, Pattern)]): Pattern =
|
|
||||||
## Inject `bindings` into a dictionary pattern.
|
|
||||||
assert pat.orKind == PatternKind.DCompound
|
|
||||||
assert pat.dcompound.orKind == DCompoundKind.dict
|
|
||||||
result = pat
|
|
||||||
for (key, val) in bindings:
|
|
||||||
result.dcompound.dict.entries[key] = val
|
|
||||||
|
|
||||||
proc grabRecord*(label: Value, fields: varargs[Pattern]): Pattern =
|
proc grabRecord*(label: Value, fields: varargs[Pattern]): Pattern =
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
import syndicate/actors, preserves
|
import preserves
|
||||||
check:
|
check:
|
||||||
$grabRecord("Says".toSymbol, grab(), grab()) ==
|
$grabRecord("Says".toSymbol, grab(), grab()) ==
|
||||||
"""<rec Says [<bind <_>> <bind <_>>]>"""
|
"""<group <rec Says> {0: <bind <_>> 1: <bind <_>>}>"""
|
||||||
DCompoundRec(label: label, fields: fields.toSeq).toPattern
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||||
|
group.`type`.rec.label = label
|
||||||
|
for i, f in fields: group.entries[toPreserves i] = f
|
||||||
|
group.toPattern
|
||||||
|
|
||||||
|
proc grabRecord*(label: Value, fields: sink openArray[(int, Pattern)]): Pattern =
|
||||||
|
runnableExamples:
|
||||||
|
from std/unittest import check
|
||||||
|
import preserves
|
||||||
|
check:
|
||||||
|
$grabRecord("Says".toSymbol, {3: grab(), 4: grab()}) ==
|
||||||
|
"""<group <rec Says> {3: <bind <_>> 4: <bind <_>>}>"""
|
||||||
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||||
|
group.`type`.rec.label = label
|
||||||
|
for (i, p) in fields: group.entries[toPreserves i] = p
|
||||||
|
group.toPattern
|
||||||
|
|
||||||
proc grabRecord*(label: string, fields: varargs[Pattern]): Pattern =
|
proc grabRecord*(label: string, fields: varargs[Pattern]): Pattern =
|
||||||
## Sugar for creating record patterns.
|
## Sugar for creating record patterns.
|
||||||
|
@ -304,47 +269,61 @@ proc grabRecord*(label: string, fields: varargs[Pattern]): Pattern =
|
||||||
|
|
||||||
proc grabDictionary*(bindings: sink openArray[(Value, Pattern)]): Pattern =
|
proc grabDictionary*(bindings: sink openArray[(Value, Pattern)]): Pattern =
|
||||||
## Construct a pattern that grabs some dictionary pairs.
|
## Construct a pattern that grabs some dictionary pairs.
|
||||||
DCompoundDict(entries: bindings.toTable).toPattern
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||||
|
for (key, val) in bindings: group.entries[key] = val
|
||||||
|
group.toPattern
|
||||||
|
|
||||||
proc grabDictionary*(bindings: sink openArray[(string, Pattern)]): Pattern =
|
proc grabDictionary*(bindings: sink openArray[(string, Pattern)]): Pattern =
|
||||||
## Construct a pattern that grabs some dictionary pairs.
|
## Construct a pattern that grabs some dictionary pairs.
|
||||||
## Keys are converted from strings to symbols.
|
## Keys are converted from strings to symbols.
|
||||||
result = DCompoundDict().toPattern
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||||
for (key, val) in bindings.items:
|
for (key, val) in bindings: group.entries[toSymbol key] = val
|
||||||
result.dcompound.dict.entries[key.toSymbol] = val
|
group.toPattern
|
||||||
|
|
||||||
proc depattern(comp: DCompound; values: var seq[Value]; index: var int): Value
|
proc depattern(group: PatternGroup; values: var seq[Value]; index: var int): Value
|
||||||
|
|
||||||
proc depattern(pat: Pattern; values: var seq[Value]; index: var int): Value =
|
proc depattern(pat: Pattern; values: var seq[Value]; index: var int): Value =
|
||||||
case pat.orKind
|
case pat.orKind
|
||||||
of PatternKind.DDiscard:
|
of PatternKind.`discard`:
|
||||||
discard
|
discard
|
||||||
of PatternKind.DBind:
|
of PatternKind.`bind`:
|
||||||
if index < values.len:
|
if index < values.len:
|
||||||
result = move values[index]
|
result = move values[index]
|
||||||
inc index
|
inc index
|
||||||
of PatternKind.DLit:
|
of PatternKind.`lit`:
|
||||||
result = pat.dlit.value.toPreserves
|
result = pat.`lit`.value.toPreserves
|
||||||
of PatternKind.DCompound:
|
of PatternKind.`group`:
|
||||||
result = depattern(pat.dcompound, values, index)
|
result = depattern(pat.group, values, index)
|
||||||
|
|
||||||
proc depattern(comp: DCompound; values: var seq[Value]; index: var int): Value =
|
proc depattern(group: PatternGroup; values: var seq[Value]; index: var int): Value =
|
||||||
case comp.orKind
|
case group.`type`.orKind
|
||||||
of DCompoundKind.rec:
|
of GroupTypeKind.rec:
|
||||||
result = initRecord(comp.rec.label, comp.rec.fields.len)
|
result = initRecord(group.`type`.rec.label, group.entries.len)
|
||||||
for i, f in comp.rec.fields:
|
var i: int
|
||||||
result[i] = depattern(f, values, index)
|
for key, val in group.entries:
|
||||||
of DCompoundKind.arr:
|
if i.fromPreserves key:
|
||||||
result = initSequence(comp.arr.items.len)
|
result[i] = depattern(val, values, index)
|
||||||
for i, e in comp.arr.items:
|
of GroupTypeKind.arr:
|
||||||
result[i] = depattern(e, values, index)
|
result = initSequence(group.entries.len)
|
||||||
of DCompoundKind.dict:
|
var i: int
|
||||||
|
for key, val in group.entries:
|
||||||
|
if i.fromPreserves key:
|
||||||
|
result[i] = depattern(val, values, index)
|
||||||
|
of GroupTypeKind.dict:
|
||||||
result = initDictionary(Cap)
|
result = initDictionary(Cap)
|
||||||
for key, val in comp.dict.entries:
|
for key, val in group.entries:
|
||||||
result[key] = depattern(val, values, index)
|
result[key] = depattern(val, values, index)
|
||||||
|
|
||||||
proc depattern*(pat: Pattern; values: sink seq[Value]): Value =
|
proc depattern*(pat: Pattern; values: sink seq[Value]): Value =
|
||||||
## Convert a `Pattern` to a `Value` while replacing binds with `values`.
|
## Convert a `Pattern` to a `Value` while replacing binds with `values`.
|
||||||
|
runnableExamples:
|
||||||
|
from std/unittest import check
|
||||||
|
import preserves
|
||||||
|
type Foo {.preservesRecord: "foo".} = object
|
||||||
|
a, b: int
|
||||||
|
let pat = grabType Foo
|
||||||
|
let val = depattern(pat, @[1.toPreserves, 5.toPreserves])
|
||||||
|
check $val == "<foo 1 5>"
|
||||||
var index: int
|
var index: int
|
||||||
depattern(pat, values, index)
|
depattern(pat, values, index)
|
||||||
|
|
||||||
|
@ -359,44 +338,73 @@ proc fromPreservesHook*[T](lit: var Literal[T]; pr: Value): bool =
|
||||||
proc toPreservesHook*[T](lit: Literal[T]): Value =
|
proc toPreservesHook*[T](lit: Literal[T]): Value =
|
||||||
lit.value.grab.toPreserves
|
lit.value.grab.toPreserves
|
||||||
|
|
||||||
|
func isGroup(pat: Pattern): bool =
|
||||||
|
pat.orKind == PatternKind.`group`
|
||||||
|
|
||||||
|
func isMetaDict(pat: Pattern): bool =
|
||||||
|
pat.orKind == PatternKind.`group` and
|
||||||
|
pat.group.type.orKind == GroupTypeKind.dict
|
||||||
|
|
||||||
|
proc metaApply(result: var Pattern; pat: Pattern; path: openarray[Value], offset: int) =
|
||||||
|
if offset == path.len:
|
||||||
|
result = pat
|
||||||
|
elif result.isGroup and result.group.entries[1.toPreserves].isMetaDict:
|
||||||
|
if offset == path.high:
|
||||||
|
result.group.entries[1.toPreserves].group.entries[path[offset]] = pat
|
||||||
|
else:
|
||||||
|
metaApply(result.group.entries[1.toPreserves].group.entries[path[offset]], pat, path, succ offset)
|
||||||
|
else:
|
||||||
|
assert result.isGroup, "non-group: " & $result
|
||||||
|
assert result.group.entries[1.toPreserves].isMetaDict, "non-meta-dict: " & $result.group.entries[1.toPreserves]
|
||||||
|
raise newException(ValueError, "cannot inject into non-group pattern " & $result)
|
||||||
|
|
||||||
|
proc observePattern*(pat: Pattern; injects: openarray[(seq[Value], Pattern)]): Pattern =
|
||||||
|
result = dropType Observe
|
||||||
|
var meta = pat.toPreserves.drop
|
||||||
|
for (path, pat) in injects:
|
||||||
|
metaApply(meta, pat, path, 0)
|
||||||
|
result.group.entries[0.toPreserves] = meta
|
||||||
|
|
||||||
type
|
type
|
||||||
Path* = seq[Value]
|
Path* = seq[Value]
|
||||||
Paths* = seq[Path]
|
Paths* = seq[Path]
|
||||||
Captures* = seq[Value]
|
Captures* = seq[Value]
|
||||||
Analysis* = tuple
|
Analysis* = tuple
|
||||||
|
presentPaths: Paths
|
||||||
constPaths: Paths
|
constPaths: Paths
|
||||||
constValues: seq[Value]
|
constValues: seq[Value]
|
||||||
capturePaths: Paths
|
capturePaths: Paths
|
||||||
|
|
||||||
func walk(result: var Analysis; path: var Path; p: Pattern)
|
func walk(result: var Analysis; path: var Path; p: Pattern)
|
||||||
|
|
||||||
func walk(result: var Analysis; path: var Path; key: int|Value; pat: Pattern) =
|
func walk(result: var Analysis; path: var Path; key: Value; pat: Pattern) =
|
||||||
path.add(key.toPreserves)
|
path.add(key)
|
||||||
walk(result, path, pat)
|
walk(result, path, pat)
|
||||||
discard path.pop
|
discard path.pop
|
||||||
|
|
||||||
func walk(result: var Analysis; path: var Path; p: Pattern) =
|
func walk(result: var Analysis; path: var Path; p: Pattern) =
|
||||||
case p.orKind
|
case p.orKind
|
||||||
of PatternKind.DCompound:
|
of PatternKind.group:
|
||||||
case p.dcompound.orKind
|
for k, v in p.group.entries: walk(result, path, k, v)
|
||||||
of DCompoundKind.rec:
|
of PatternKind.`bind`:
|
||||||
for k, e in p.dcompound.rec.fields: walk(result, path, k, e)
|
|
||||||
of DCompoundKind.arr:
|
|
||||||
for k, e in p.dcompound.arr.items: walk(result, path, k, e)
|
|
||||||
of DCompoundKind.dict:
|
|
||||||
for k, e in p.dcompound.dict.orderedEntries: walk(result, path, k, e)
|
|
||||||
of PatternKind.DBind:
|
|
||||||
result.capturePaths.add(path)
|
result.capturePaths.add(path)
|
||||||
walk(result, path, p.dbind.pattern)
|
walk(result, path, p.`bind`.pattern)
|
||||||
of PatternKind.DDiscard: discard
|
of PatternKind.`discard`:
|
||||||
of PatternKind.DLit:
|
result.presentPaths.add(path)
|
||||||
|
of PatternKind.`lit`:
|
||||||
result.constPaths.add(path)
|
result.constPaths.add(path)
|
||||||
result.constValues.add(p.dlit.value.toPreserves)
|
result.constValues.add(p.`lit`.value.toPreserves)
|
||||||
|
|
||||||
func analyse*(p: Pattern): Analysis =
|
func analyse*(p: Pattern): Analysis =
|
||||||
var path: Path
|
var path: Path
|
||||||
walk(result, path, p)
|
walk(result, path, p)
|
||||||
|
|
||||||
|
func checkPresence*(v: Value; present: Paths): bool =
|
||||||
|
result = true
|
||||||
|
for path in present:
|
||||||
|
if not result: break
|
||||||
|
result = step(v, path).isSome
|
||||||
|
|
||||||
func projectPaths*(v: Value; paths: Paths): Option[Captures] =
|
func projectPaths*(v: Value; paths: Paths): Option[Captures] =
|
||||||
var res = newSeq[Value](paths.len)
|
var res = newSeq[Value](paths.len)
|
||||||
for i, path in paths:
|
for i, path in paths:
|
||||||
|
@ -408,30 +416,27 @@ func projectPaths*(v: Value; paths: Paths): Option[Captures] =
|
||||||
proc matches*(pat: Pattern; pr: Value): bool =
|
proc matches*(pat: Pattern; pr: Value): bool =
|
||||||
let analysis = analyse(pat)
|
let analysis = analyse(pat)
|
||||||
assert analysis.constPaths.len == analysis.constValues.len
|
assert analysis.constPaths.len == analysis.constValues.len
|
||||||
for i, path in analysis.constPaths:
|
result = checkPresence(pr, analysis.presentPaths)
|
||||||
let v = step(pr, path)
|
if result:
|
||||||
if v.isNone: return false
|
for i, path in analysis.constPaths:
|
||||||
if analysis.constValues[i] != v.get: return false
|
let v = step(pr, path)
|
||||||
for path in analysis.capturePaths:
|
if v.isNone: return false
|
||||||
if isNone step(pr, path): return false
|
if analysis.constValues[i] != v.get: return false
|
||||||
true
|
for path in analysis.capturePaths:
|
||||||
|
if step(pr, path).isNone: return false
|
||||||
|
|
||||||
func capture*(pat: Pattern; pr: Value): seq[Value] =
|
proc capture*(pat: Pattern; pr: Value): seq[Value] =
|
||||||
let analysis = analyse(pat)
|
let analysis = analyse(pat)
|
||||||
assert analysis.constPaths.len == analysis.constValues.len
|
assert analysis.constPaths.len == analysis.constValues.len
|
||||||
for i, path in analysis.constPaths:
|
if checkPresence(pr, analysis.presentPaths):
|
||||||
let v = step(pr, path)
|
for i, path in analysis.constPaths:
|
||||||
if v.isNone : return @[]
|
let v = step(pr, path)
|
||||||
if analysis.constValues[i] != v.get: return @[]
|
if v.isNone : return @[]
|
||||||
for path in analysis.capturePaths:
|
if analysis.constValues[i] != v.get: return @[]
|
||||||
let v = step(pr, path)
|
for path in analysis.capturePaths:
|
||||||
if v.isNone: return @[]
|
let v = step(pr, path)
|
||||||
result.add(get v)
|
if v.isNone: return @[]
|
||||||
|
result.add(get v)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
let txt = readAll stdin
|
stdout.writeLine stdin.readAll.parsePreserves.grab
|
||||||
if txt != "":
|
|
||||||
let
|
|
||||||
v = parsePreserves(txt)
|
|
||||||
pat = grab v
|
|
||||||
stdout.writeLine(pat)
|
|
||||||
|
|
|
@ -29,58 +29,58 @@ type
|
||||||
`embedded`* {.preservesEmbedded.}: EmbeddedRef
|
`embedded`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
|
|
||||||
DLit* {.preservesRecord: "lit".} = object
|
GroupTypeKind* {.pure.} = enum
|
||||||
`value`*: AnyAtom
|
|
||||||
|
|
||||||
DBind* {.preservesRecord: "bind".} = object
|
|
||||||
`pattern`*: Pattern
|
|
||||||
|
|
||||||
DDiscard* {.preservesRecord: "_".} = object
|
|
||||||
|
|
||||||
DCompoundKind* {.pure.} = enum
|
|
||||||
`rec`, `arr`, `dict`
|
`rec`, `arr`, `dict`
|
||||||
DCompoundRec* {.preservesRecord: "rec".} = object
|
GroupTypeRec* {.preservesRecord: "rec".} = object
|
||||||
`label`*: Value
|
`label`*: Value
|
||||||
`fields`*: seq[Pattern]
|
|
||||||
|
|
||||||
DCompoundArr* {.preservesRecord: "arr".} = object
|
GroupTypeArr* {.preservesRecord: "arr".} = object
|
||||||
`items`*: seq[Pattern]
|
|
||||||
|
GroupTypeDict* {.preservesRecord: "dict".} = object
|
||||||
|
|
||||||
|
`GroupType`* {.preservesOr.} = object
|
||||||
|
case orKind*: GroupTypeKind
|
||||||
|
of GroupTypeKind.`rec`:
|
||||||
|
`rec`*: GroupTypeRec
|
||||||
|
|
||||||
DCompoundDict* {.preservesRecord: "dict".} = object
|
of GroupTypeKind.`arr`:
|
||||||
`entries`*: Table[Value, Pattern]
|
`arr`*: GroupTypeArr
|
||||||
|
|
||||||
`DCompound`* {.preservesOr.} = object
|
of GroupTypeKind.`dict`:
|
||||||
case orKind*: DCompoundKind
|
`dict`*: GroupTypeDict
|
||||||
of DCompoundKind.`rec`:
|
|
||||||
`rec`* {.preservesEmbedded.}: DCompoundRec
|
|
||||||
|
|
||||||
of DCompoundKind.`arr`:
|
|
||||||
`arr`* {.preservesEmbedded.}: DCompoundArr
|
|
||||||
|
|
||||||
of DCompoundKind.`dict`:
|
|
||||||
`dict`* {.preservesEmbedded.}: DCompoundDict
|
|
||||||
|
|
||||||
|
|
||||||
PatternKind* {.pure.} = enum
|
PatternKind* {.pure.} = enum
|
||||||
`DDiscard`, `DBind`, `DLit`, `DCompound`
|
`discard`, `bind`, `lit`, `group`
|
||||||
|
PatternDiscard* {.preservesRecord: "_".} = object
|
||||||
|
|
||||||
|
PatternBind* {.preservesRecord: "bind".} = object
|
||||||
|
`pattern`*: Pattern
|
||||||
|
|
||||||
|
PatternLit* {.preservesRecord: "lit".} = object
|
||||||
|
`value`*: AnyAtom
|
||||||
|
|
||||||
|
PatternGroup* {.preservesRecord: "group".} = object
|
||||||
|
`type`*: GroupType
|
||||||
|
`entries`*: Table[Value, Pattern]
|
||||||
|
|
||||||
`Pattern`* {.acyclic, preservesOr.} = ref object
|
`Pattern`* {.acyclic, preservesOr.} = ref object
|
||||||
case orKind*: PatternKind
|
case orKind*: PatternKind
|
||||||
of PatternKind.`DDiscard`:
|
of PatternKind.`discard`:
|
||||||
`ddiscard`*: DDiscard
|
`discard`*: PatternDiscard
|
||||||
|
|
||||||
of PatternKind.`DBind`:
|
of PatternKind.`bind`:
|
||||||
`dbind`* {.preservesEmbedded.}: DBind
|
`bind`* {.preservesEmbedded.}: PatternBind
|
||||||
|
|
||||||
of PatternKind.`DLit`:
|
of PatternKind.`lit`:
|
||||||
`dlit`* {.preservesEmbedded.}: DLit
|
`lit`* {.preservesEmbedded.}: PatternLit
|
||||||
|
|
||||||
of PatternKind.`DCompound`:
|
of PatternKind.`group`:
|
||||||
`dcompound`* {.preservesEmbedded.}: DCompound
|
`group`* {.preservesEmbedded.}: PatternGroup
|
||||||
|
|
||||||
|
|
||||||
proc `$`*(x: AnyAtom | DLit | DBind | DDiscard | DCompound | Pattern): string =
|
proc `$`*(x: AnyAtom | GroupType | Pattern): string =
|
||||||
`$`(toPreserves(x))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*(x: AnyAtom | DLit | DBind | DDiscard | DCompound | Pattern): seq[
|
proc encode*(x: AnyAtom | GroupType | Pattern): seq[byte] =
|
||||||
byte] =
|
|
||||||
encode(toPreserves(x))
|
encode(toPreserves(x))
|
||||||
|
|
|
@ -47,7 +47,7 @@ type
|
||||||
|
|
||||||
HttpRequest* {.preservesRecord: "http-request".} = object
|
HttpRequest* {.preservesRecord: "http-request".} = object
|
||||||
`sequenceNumber`*: BiggestInt
|
`sequenceNumber`*: BiggestInt
|
||||||
`host`*: string
|
`host`*: RequestHost
|
||||||
`port`*: BiggestInt
|
`port`*: BiggestInt
|
||||||
`method`*: Symbol
|
`method`*: Symbol
|
||||||
`path`*: seq[string]
|
`path`*: seq[string]
|
||||||
|
@ -115,6 +115,17 @@ type
|
||||||
`req`*: HttpRequest
|
`req`*: HttpRequest
|
||||||
`res`* {.preservesEmbedded.}: Value
|
`res`* {.preservesEmbedded.}: Value
|
||||||
|
|
||||||
|
RequestHostKind* {.pure.} = enum
|
||||||
|
`present`, `absent`
|
||||||
|
`RequestHost`* {.preservesOr.} = object
|
||||||
|
case orKind*: RequestHostKind
|
||||||
|
of RequestHostKind.`present`:
|
||||||
|
`present`*: string
|
||||||
|
|
||||||
|
of RequestHostKind.`absent`:
|
||||||
|
`absent`* {.preservesLiteral: "#f".}: bool
|
||||||
|
|
||||||
|
|
||||||
PathPatternElementKind* {.pure.} = enum
|
PathPatternElementKind* {.pure.} = enum
|
||||||
`label`, `wildcard`, `rest`
|
`label`, `wildcard`, `rest`
|
||||||
`PathPatternElement`* {.preservesOr.} = object
|
`PathPatternElement`* {.preservesOr.} = object
|
||||||
|
@ -149,6 +160,7 @@ proc `$`*(x: HostPattern | HttpListener | MethodPattern | MimeType | QueryValue
|
||||||
HttpService |
|
HttpService |
|
||||||
HttpBinding |
|
HttpBinding |
|
||||||
HttpContext |
|
HttpContext |
|
||||||
|
RequestHost |
|
||||||
PathPatternElement |
|
PathPatternElement |
|
||||||
Chunk |
|
Chunk |
|
||||||
PathPattern): string =
|
PathPattern): string =
|
||||||
|
@ -163,6 +175,7 @@ proc encode*(x: HostPattern | HttpListener | MethodPattern | MimeType |
|
||||||
HttpService |
|
HttpService |
|
||||||
HttpBinding |
|
HttpBinding |
|
||||||
HttpContext |
|
HttpContext |
|
||||||
|
RequestHost |
|
||||||
PathPatternElement |
|
PathPatternElement |
|
||||||
Chunk |
|
Chunk |
|
||||||
PathPattern): seq[byte] =
|
PathPattern): seq[byte] =
|
||||||
|
|
|
@ -48,6 +48,17 @@ type
|
||||||
`absent`*: SecretKeyFieldAbsent
|
`absent`*: SecretKeyFieldAbsent
|
||||||
|
|
||||||
|
|
||||||
|
SessionItemKind* {.pure.} = enum
|
||||||
|
`Initiator`, `Packet`
|
||||||
|
`SessionItem`* {.preservesOr.} = object
|
||||||
|
case orKind*: SessionItemKind
|
||||||
|
of SessionItemKind.`Initiator`:
|
||||||
|
`initiator`* {.preservesEmbedded.}: Initiator
|
||||||
|
|
||||||
|
of SessionItemKind.`Packet`:
|
||||||
|
`packet`*: Packet
|
||||||
|
|
||||||
|
|
||||||
NoiseProtocolKind* {.pure.} = enum
|
NoiseProtocolKind* {.pure.} = enum
|
||||||
`present`, `invalid`, `absent`
|
`present`, `invalid`, `absent`
|
||||||
NoiseProtocolPresent* {.preservesDictionary.} = object
|
NoiseProtocolPresent* {.preservesDictionary.} = object
|
||||||
|
@ -83,6 +94,9 @@ type
|
||||||
`service`*: ServiceSelector
|
`service`*: ServiceSelector
|
||||||
|
|
||||||
ServiceSelector* = Value
|
ServiceSelector* = Value
|
||||||
|
Initiator* {.preservesRecord: "initiator".} = object
|
||||||
|
`initiatorSession`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
NoiseStepDetail* = ServiceSelector
|
NoiseStepDetail* = ServiceSelector
|
||||||
NoiseSpecKey* = seq[byte]
|
NoiseSpecKey* = seq[byte]
|
||||||
NoiseSpecPreSharedKeys* = Option[Value]
|
NoiseSpecPreSharedKeys* = Option[Value]
|
||||||
|
@ -105,17 +119,21 @@ type
|
||||||
|
|
||||||
|
|
||||||
proc `$`*(x: NoiseDescriptionDetail | NoisePreSharedKeys | SecretKeyField |
|
proc `$`*(x: NoiseDescriptionDetail | NoisePreSharedKeys | SecretKeyField |
|
||||||
|
SessionItem |
|
||||||
NoiseProtocol |
|
NoiseProtocol |
|
||||||
NoisePathStepDetail |
|
NoisePathStepDetail |
|
||||||
NoiseServiceSpec |
|
NoiseServiceSpec |
|
||||||
|
Initiator |
|
||||||
NoiseSpec |
|
NoiseSpec |
|
||||||
Packet): string =
|
Packet): string =
|
||||||
`$`(toPreserves(x))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*(x: NoiseDescriptionDetail | NoisePreSharedKeys | SecretKeyField |
|
proc encode*(x: NoiseDescriptionDetail | NoisePreSharedKeys | SecretKeyField |
|
||||||
|
SessionItem |
|
||||||
NoiseProtocol |
|
NoiseProtocol |
|
||||||
NoisePathStepDetail |
|
NoisePathStepDetail |
|
||||||
NoiseServiceSpec |
|
NoiseServiceSpec |
|
||||||
|
Initiator |
|
||||||
NoiseSpec |
|
NoiseSpec |
|
||||||
Packet): seq[byte] =
|
Packet): seq[byte] =
|
||||||
encode(toPreserves(x))
|
encode(toPreserves(x))
|
||||||
|
|
|
@ -251,7 +251,7 @@ proc recv(relay: Relay; buf: openarray[byte]; slice: Slice[int]) =
|
||||||
var pr = decode(relay.wireBuf)
|
var pr = decode(relay.wireBuf)
|
||||||
if pr.isSome: dispatch(relay, pr.get)
|
if pr.isSome: dispatch(relay, pr.get)
|
||||||
|
|
||||||
proc recv(relay: Relay; buf: openarray[byte]) =
|
proc recv(relay: Relay; buf: openarray[byte]) {.used.} =
|
||||||
feed(relay.wireBuf, buf)
|
feed(relay.wireBuf, buf)
|
||||||
var pr = decode(relay.wireBuf)
|
var pr = decode(relay.wireBuf)
|
||||||
if pr.isSome: dispatch(relay, pr.get)
|
if pr.isSome: dispatch(relay, pr.get)
|
||||||
|
@ -393,11 +393,12 @@ when defined(posix):
|
||||||
entity.alive = false
|
entity.alive = false
|
||||||
close(entity.sock)
|
close(entity.sock)
|
||||||
onStop(turn, kill)
|
onStop(turn, kill)
|
||||||
publish(turn, ds, TransportConnection(
|
var ass = TransportConnection(
|
||||||
`addr`: ta.toPreserves,
|
`addr`: ta.toPreserves,
|
||||||
control: newCap(entity, turn),
|
control: newCap(entity, turn),
|
||||||
resolved: entity.relay.peer.accepted,
|
resolved: entity.relay.peer.accepted,
|
||||||
))
|
)
|
||||||
|
publish(turn, ds, ass)
|
||||||
run(entity.relay.facet, setup)
|
run(entity.relay.facet, setup)
|
||||||
let buf = new seq[byte]
|
let buf = new seq[byte]
|
||||||
entity.alive = true
|
entity.alive = true
|
||||||
|
@ -549,31 +550,28 @@ proc connectRoute(turn: var Turn; ds: Cap; route: Route; transOff: int) =
|
||||||
onPublish(turn, ds, acceptPat) do (origin: Cap):
|
onPublish(turn, ds, acceptPat) do (origin: Cap):
|
||||||
walk(turn, ds, origin, route, transOff, 0)
|
walk(turn, ds, origin, route, transOff, 0)
|
||||||
|
|
||||||
type StepCallback = proc (turn: var Turn; step: Value; origin, next: Cap) {.closure.}
|
type StepCallback = proc (turn: var Turn; step: Value; origin: Cap; res: Resolved) {.closure.}
|
||||||
|
|
||||||
proc spawnStepResolver(turn: var Turn; ds: Cap; stepType: Value; cb: StepCallback) =
|
proc spawnStepResolver(turn: var Turn; ds: Cap; stepType: Value; cb: StepCallback) =
|
||||||
let stepPat = grabRecord(stepType, grab())
|
let pat = observePattern(
|
||||||
let pat = ?Observe(pattern: ResolvedPathStep?:{1: stepPat}) ?? {0: grabLit(), 1: grab()}
|
ResolvedPathStep?:{1: grabRecord(stepType)},
|
||||||
|
{ @[0.toPreserve]: grabLit(), @[1.toPreserve]: grab() },
|
||||||
|
)
|
||||||
during(turn, ds, pat) do (origin: Cap; stepDetail: Literal[Value]):
|
during(turn, ds, pat) do (origin: Cap; stepDetail: Literal[Value]):
|
||||||
let step = toRecord(stepType, stepDetail.value)
|
|
||||||
proc duringCallback(turn: var Turn; ass: Value; h: Handle): TurnAction =
|
proc duringCallback(turn: var Turn; ass: Value; h: Handle): TurnAction =
|
||||||
var res = ass.preservesTo Resolved
|
var res: Resolved
|
||||||
if res.isSome:
|
if res.fromPreserves ass:
|
||||||
if res.get.orKind == ResolvedKind.accepted and
|
cb(turn, stepDetail.value, origin, res)
|
||||||
res.get.accepted.responderSession of Cap:
|
|
||||||
cb(turn, step, origin, res.get.accepted.responderSession.Cap)
|
|
||||||
else:
|
|
||||||
publish(turn, ds, ResolvedPathStep(
|
|
||||||
origin: origin, pathStep: step, resolved: res.get))
|
|
||||||
proc action(turn: var Turn) =
|
proc action(turn: var Turn) =
|
||||||
stop(turn)
|
stop(turn)
|
||||||
result = action
|
result = action
|
||||||
publish(turn, origin, Resolve(
|
publish(turn, origin, Resolve(
|
||||||
step: step, observer: newCap(turn, during(duringCallback))))
|
step: stepDetail.value, observer: newCap(turn, during(duringCallback))))
|
||||||
|
|
||||||
proc spawnRelays*(turn: var Turn; ds: Cap) =
|
proc spawnRelays*(turn: var Turn; ds: Cap) =
|
||||||
## Spawn actors that manage routes and appeasing gatekeepers.
|
## Spawn actors that manage routes and appease gatekeepers.
|
||||||
let transPat = ?Observe(pattern: !TransportConnection) ?? { 0: grab() }
|
|
||||||
|
let transPat = observePattern(!TransportConnection, { @[0.toPreserves]: grab() })
|
||||||
# Use a generic pattern and type matching
|
# Use a generic pattern and type matching
|
||||||
# in the during handler because it is easy.
|
# in the during handler because it is easy.
|
||||||
|
|
||||||
|
@ -600,20 +598,21 @@ proc spawnRelays*(turn: var Turn; ds: Cap) =
|
||||||
resolved: rejected(embed e),
|
resolved: rejected(embed e),
|
||||||
))
|
))
|
||||||
|
|
||||||
let resolvePat = ?Observe(pattern: !ResolvePath) ?? {0: grab()}
|
let resolvePat = observePattern(!ResolvePath, {@[0.toPreserves]: grab()})
|
||||||
during(turn, ds, resolvePat) do (route: Literal[Route]):
|
during(turn, ds, resolvePat) do (route: Literal[Route]):
|
||||||
for i, transAddr in route.value.transports:
|
for i, transAddr in route.value.transports:
|
||||||
connectRoute(turn, ds, route.value, i)
|
connectRoute(turn, ds, route.value, i)
|
||||||
|
|
||||||
spawnStepResolver(turn, ds, "ref".toSymbol) do (
|
spawnStepResolver(turn, ds, "ref".toSymbol) do (
|
||||||
turn: var Turn, step: Value, origin: Cap, next: Cap):
|
turn: var Turn, step: Value, origin: Cap, res: Resolved):
|
||||||
publish(turn, ds, ResolvedPathStep(
|
publish(turn, ds, ResolvedPathStep(
|
||||||
origin: origin, pathStep: step, resolved: next.accepted))
|
origin: origin, pathStep: step, resolved: res))
|
||||||
|
|
||||||
type BootProc* = proc (turn: var Turn; ds: Cap) {.closure.}
|
type BootProc* = proc (turn: var Turn; ds: Cap) {.closure.}
|
||||||
|
|
||||||
proc resolve*(turn: var Turn; ds: Cap; route: Route; bootProc: BootProc) =
|
proc resolve*(turn: var Turn; ds: Cap; route: Route; bootProc: BootProc) =
|
||||||
## Resolve `route` within `ds` and call `bootProc` with resolved capabilities.
|
## Resolve `route` within `ds` and call `bootProc` with resolved capabilities.
|
||||||
|
let pat = ResolvePath ?: {0: ?route, 3: ?:ResolvedAccepted}
|
||||||
during(turn, ds, ResolvePath ?: {0: ?route, 3: ?:ResolvedAccepted}) do (dst: Cap):
|
during(turn, ds, ResolvePath ?: {0: ?route, 3: ?:ResolvedAccepted}) do (dst: Cap):
|
||||||
bootProc(turn, dst)
|
bootProc(turn, dst)
|
||||||
|
|
||||||
|
|
|
@ -9,32 +9,28 @@ import ./actors, ./bags, ./patterns
|
||||||
import ./protocols/dataspacePatterns
|
import ./protocols/dataspacePatterns
|
||||||
|
|
||||||
type
|
type
|
||||||
DCompound = dataspacePatterns.DCompound
|
|
||||||
Pattern = dataspacePatterns.Pattern
|
Pattern = dataspacePatterns.Pattern
|
||||||
Path = seq[Value]
|
Path = seq[Value]
|
||||||
ClassKind = enum classNone, classRecord, classSequence, classDictionary
|
ClassKind = enum classNone, classRecord, classSequence, classDictionary
|
||||||
Class = object
|
Class = object
|
||||||
kind: ClassKind
|
kind: ClassKind
|
||||||
label: Value
|
label: Value
|
||||||
arity: int
|
|
||||||
|
|
||||||
func classOf(v: Value): Class =
|
func classOf(v: Value): Class =
|
||||||
case v.kind
|
case v.kind
|
||||||
of pkRecord: Class(kind: classRecord, label: v.label, arity: v.arity)
|
of pkRecord: Class(kind: classRecord, label: v.label)
|
||||||
of pkSequence: Class(kind: classSequence, arity: v.len)
|
of pkSequence: Class(kind: classSequence)
|
||||||
of pkDictionary: Class(kind: classDictionary)
|
of pkDictionary: Class(kind: classDictionary)
|
||||||
else: Class(kind: classNone)
|
else: Class(kind: classNone)
|
||||||
|
|
||||||
proc classOf(p: Pattern): Class =
|
proc classOf(p: Pattern): Class =
|
||||||
if p.orKind == PatternKind.DCompound:
|
if p.orKind == PatternKind.group:
|
||||||
case p.dcompound.orKind
|
case p.group.type.orKind
|
||||||
of DCompoundKind.rec:
|
of GroupTypeKind.rec:
|
||||||
Class(kind: classRecord,
|
Class(kind: classRecord, label: p.group.`type`.rec.label)
|
||||||
label: p.dcompound.rec.label,
|
of GroupTypeKind.arr:
|
||||||
arity: p.dcompound.rec.fields.len)
|
Class(kind: classSequence)
|
||||||
of DCompoundKind.arr:
|
of GroupTypeKind.dict:
|
||||||
Class(kind: classSequence, arity: p.dcompound.arr.items.len)
|
|
||||||
of DCompoundKind.dict:
|
|
||||||
Class(kind: classDictionary)
|
Class(kind: classDictionary)
|
||||||
else:
|
else:
|
||||||
Class(kind: classNone)
|
Class(kind: classNone)
|
||||||
|
@ -69,13 +65,14 @@ type
|
||||||
LeafProc = proc (l: Leaf; v: Value) {.closure.}
|
LeafProc = proc (l: Leaf; v: Value) {.closure.}
|
||||||
ObserverProc = proc (turn: var Turn; group: ObserverGroup; vs: seq[Value]) {.closure.}
|
ObserverProc = proc (turn: var Turn; group: ObserverGroup; vs: seq[Value]) {.closure.}
|
||||||
|
|
||||||
proc getLeaves(cont: Continuation; constPaths: Paths): LeafMap =
|
proc getLeaves(cont: Continuation; presentPaths, constPaths: Paths): LeafMap =
|
||||||
result = cont.leafMap.getOrDefault(constPaths)
|
result = cont.leafMap.getOrDefault(constPaths)
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
new result
|
new result
|
||||||
cont.leafMap[constPaths] = result
|
cont.leafMap[constPaths] = result
|
||||||
assert not cont.isEmpty
|
assert not cont.isEmpty
|
||||||
for ass in cont.cache:
|
for ass in cont.cache:
|
||||||
|
# TODO: check presence
|
||||||
let key = projectPaths(ass, constPaths)
|
let key = projectPaths(ass, constPaths)
|
||||||
if key.isSome:
|
if key.isSome:
|
||||||
var leaf = result.getOrDefault(get key)
|
var leaf = result.getOrDefault(get key)
|
||||||
|
@ -165,25 +162,13 @@ proc getOrNew[A, B, C](t: var Table[A, TableRef[B, C]], k: A): TableRef[B, C] =
|
||||||
result = newTable[B, C]()
|
result = newTable[B, C]()
|
||||||
t[k] = result
|
t[k] = result
|
||||||
|
|
||||||
iterator pairs(dc: DCompound): (Value, Pattern) =
|
|
||||||
case dc.orKind
|
|
||||||
of DCompoundKind.rec:
|
|
||||||
for i, p in dc.rec.fields:
|
|
||||||
yield (i.toPreserves, p,)
|
|
||||||
of DCompoundKind.arr:
|
|
||||||
for i, p in dc.arr.items:
|
|
||||||
yield (i.toPreserves, p,)
|
|
||||||
of DCompoundKind.dict:
|
|
||||||
for pair in dc.dict.entries.pairs:
|
|
||||||
yield pair
|
|
||||||
|
|
||||||
proc extendWalk(node: Node; popCount: Natural; stepIndex: Value; pat: Pattern; path: var Path): tuple[popCount: Natural, nextNode: Node] =
|
proc extendWalk(node: Node; popCount: Natural; stepIndex: Value; pat: Pattern; path: var Path): tuple[popCount: Natural, nextNode: Node] =
|
||||||
case pat.orKind
|
case pat.orKind
|
||||||
of PatternKind.DDiscard, PatternKind.DLit:
|
of PatternKind.`discard`, PatternKind.lit:
|
||||||
result = (popCount, node)
|
result = (popCount, node)
|
||||||
of PatternKind.DBind:
|
of PatternKind.`bind`:
|
||||||
result = extendWalk(node, popCount, stepIndex, pat.dbind.pattern, path)
|
result = extendWalk(node, popCount, stepIndex, pat.`bind`.pattern, path)
|
||||||
of PatternKind.DCompound:
|
of PatternKind.`group`:
|
||||||
let
|
let
|
||||||
selector: Selector = (popCount, stepIndex,)
|
selector: Selector = (popCount, stepIndex,)
|
||||||
table = node.edges.getOrNew(selector)
|
table = node.edges.getOrNew(selector)
|
||||||
|
@ -198,7 +183,7 @@ proc extendWalk(node: Node; popCount: Natural; stepIndex: Value; pat: Pattern; p
|
||||||
if v.isSome and class == classOf(get v):
|
if v.isSome and class == classOf(get v):
|
||||||
result.nextNode.continuation.cache.incl a
|
result.nextNode.continuation.cache.incl a
|
||||||
result.popCount = 0
|
result.popCount = 0
|
||||||
for step, p in pat.dcompound.pairs:
|
for step, p in pat.group.entries:
|
||||||
add(path, step)
|
add(path, step)
|
||||||
result = extendWalk(result.nextNode, result.popCount, step, p, path)
|
result = extendWalk(result.nextNode, result.popCount, step, p, path)
|
||||||
discard pop(path)
|
discard pop(path)
|
||||||
|
@ -231,7 +216,7 @@ proc add*(index: var Index; turn: var Turn; pattern: Pattern; observer: Cap) =
|
||||||
let
|
let
|
||||||
cont = index.root.extend(pattern)
|
cont = index.root.extend(pattern)
|
||||||
analysis = analyse pattern
|
analysis = analyse pattern
|
||||||
constValMap = cont.getLeaves(analysis.constPaths)
|
constValMap = cont.getLeaves(analysis.presentPaths, analysis.constPaths)
|
||||||
leaf = constValMap.getLeaf(analysis.constValues)
|
leaf = constValMap.getLeaf(analysis.constValues)
|
||||||
endpoints = leaf.getEndpoints(analysis.capturePaths)
|
endpoints = leaf.getEndpoints(analysis.capturePaths)
|
||||||
# TODO if endpoints.cachedCaptures.len > 0:
|
# TODO if endpoints.cachedCaptures.len > 0:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "20240408"
|
version = "20240421"
|
||||||
author = "Emery Hemingway"
|
author = "Emery Hemingway"
|
||||||
description = "Syndicated actors for conversational concurrency"
|
description = "Syndicated actors for conversational concurrency"
|
||||||
license = "Unlicense"
|
license = "Unlicense"
|
||||||
|
|
|
@ -1,103 +1,87 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[options, tables, unittest]
|
import std/[options, sequtils, tables, unittest]
|
||||||
|
|
||||||
import preserves, syndicate, syndicate/protocols/gatekeeper
|
import preserves, syndicate, syndicate/protocols/[gatekeeper, timer]
|
||||||
|
|
||||||
import ./test_schema
|
import ./test_schema
|
||||||
|
|
||||||
test "patterns":
|
suite "example":
|
||||||
let
|
var pat: Pattern
|
||||||
pat = ?Observe(pattern: !Foo) ?? {0: grab()}
|
check pat.fromPreserves parsePreserves"""
|
||||||
text = """<rec Observe [<rec rec [<lit foo> <arr [<bind <_>> <_> <_>]>]> <_>]>"""
|
<group <arr> {
|
||||||
check($pat == text)
|
0: <lit 1>
|
||||||
|
1: <bind <group <arr> {
|
||||||
|
0: <bind <_>>
|
||||||
|
1: <_>
|
||||||
|
}>>
|
||||||
|
2: <_>
|
||||||
|
}>
|
||||||
|
"""
|
||||||
|
|
||||||
let
|
const A = "[1 2 3]"
|
||||||
worte = @["alles", "in", "ordnung"]
|
test A:
|
||||||
observer = Observe(pattern: inject(?:Foo, { 0: ?worte })).toPreserves
|
let v = parsePreserves A
|
||||||
have = capture(pat, observer).toPreserves.unpackLiterals
|
check:
|
||||||
want = [worte.toPreserves].toPreserves
|
not pat.matches(v)
|
||||||
check(have == want)
|
|
||||||
|
|
||||||
type Obj {.preservesDictionary.} = object
|
const B = "[1 [2 3] 4]"
|
||||||
a, b, c: int
|
test B:
|
||||||
|
|
||||||
test "dictionaries":
|
|
||||||
let pat = ?:Obj
|
|
||||||
var source = initDictionary(Cap)
|
|
||||||
source["b".toSymbol] = 2.toPreserves
|
|
||||||
source["c".toSymbol] = 3.toPreserves
|
|
||||||
source["a".toSymbol] = 1.toPreserves
|
|
||||||
|
|
||||||
let values = capture(pat, source)
|
|
||||||
check values.len == 3
|
|
||||||
check values[0] == 1.toPreserves
|
|
||||||
check values[1] == 2.toPreserves
|
|
||||||
check values[2] == 3.toPreserves
|
|
||||||
|
|
||||||
type
|
|
||||||
File {.preservesDictionary.} = object
|
|
||||||
name: string
|
|
||||||
path: string
|
|
||||||
size: BiggestInt
|
|
||||||
`type`: string
|
|
||||||
Files = Table[Symbol, File]
|
|
||||||
Fields = Table[Symbol, string]
|
|
||||||
|
|
||||||
Request {.preservesRecord: "request".} = object
|
|
||||||
seq: BiggestInt
|
|
||||||
fields: Fields
|
|
||||||
files: Files
|
|
||||||
|
|
||||||
test "literals":
|
|
||||||
const txt = """<rec request [<lit 3> <dict {artists: <lit "kyyyyym"> date: <lit "2023-10-14"> notes: <lit "Lots of stuff"> title: <lit "Domes show">}> <dict {front-cover: <dict {name: <lit "ADULT_TIME_Glielmi.jpg"> path: <lit "/tmp/652adad1b3d2b666dcc8d857.jpg"> size: <lit 255614> type: <lit "image/jpeg">}>}>]>"""
|
|
||||||
var pr = parsePreserves(txt)
|
|
||||||
|
|
||||||
var capture: Literal[Request]
|
|
||||||
check capture.fromPreserves(pr)
|
|
||||||
|
|
||||||
suite "captures":
|
|
||||||
for txt in [
|
|
||||||
"#f",
|
|
||||||
"#t",
|
|
||||||
"0",
|
|
||||||
"-1",
|
|
||||||
"foo",
|
|
||||||
"<foo>",
|
|
||||||
"[0, 1, 2]",
|
|
||||||
]:
|
|
||||||
test txt:
|
|
||||||
let
|
|
||||||
pr = parsePreserves txt
|
|
||||||
pat = grab pr
|
|
||||||
checkpoint $pat
|
|
||||||
check pat.matches pr
|
|
||||||
|
|
||||||
suite "protocol":
|
|
||||||
test "Observe":
|
|
||||||
let pat = ?:Observe
|
|
||||||
const text = """<rec Observe [<bind <_>> <bind <_>>]>"""
|
|
||||||
check $pat == text
|
|
||||||
|
|
||||||
test "later-than":
|
|
||||||
let
|
let
|
||||||
obsA = parsePreserves"""<Observe <rec later-than [<lit 1704113731.419243>]> #f>"""
|
v = parsePreserves B
|
||||||
obsB = parsePreserves"""<Observe <rec Observe [<rec rec [<lit later-than> <arr [<rec lit [<bind <_>>]>]>]> <_>]> #f>"""
|
c = parsePreserves "[[2 3] 2]"
|
||||||
patA = """<rec later-than [<lit 1704113731.419243>]>""".parsePreserves.preservesTo(Pattern).get
|
check pat.matches(v)
|
||||||
patB = """<rec Observe [<rec rec [<lit later-than> <arr [<rec lit [<bind <_>>]>]>]> <_>]>""".parsePreserves.preservesTo(Pattern).get
|
check pat.capture(v).toPreserves == c
|
||||||
|
|
||||||
patC = grab obsA
|
const C = "[1 [2] 5]"
|
||||||
|
test C:
|
||||||
|
let v = parsePreserves C
|
||||||
|
check:
|
||||||
|
not pat.matches(v)
|
||||||
|
|
||||||
test $patC:
|
const D = "[1 [2 3 4] 5]"
|
||||||
check patC.matches obsA
|
test D:
|
||||||
|
|
||||||
test $patB:
|
|
||||||
checkpoint $obsA
|
|
||||||
check patB.matches obsA
|
|
||||||
|
|
||||||
test "TransportConnection":
|
|
||||||
let
|
let
|
||||||
pat = TransportConnection ?: { 2: ?:Rejected}
|
v = parsePreserves D
|
||||||
text = """<rec connect-transport [<_> <_> <rec rejected [<bind <_>>]>]>"""
|
c = parsePreserves "[[2 3 4] 2]"
|
||||||
check $pat == text
|
check pat.matches(v)
|
||||||
|
check pat.capture(v).toPreserves == c
|
||||||
|
|
||||||
|
const E = "[1 [<x> <y>] []]"
|
||||||
|
test E:
|
||||||
|
let
|
||||||
|
v = parsePreserves E
|
||||||
|
c = parsePreserves "[[<x> <y>] <x>]"
|
||||||
|
check pat.matches(v)
|
||||||
|
check pat.capture(v).toPreserves == c
|
||||||
|
|
||||||
|
suite "meta":
|
||||||
|
|
||||||
|
test "pattern-of-pattern":
|
||||||
|
let
|
||||||
|
pat = grabRecord("foo".toSymbol, {666: drop()})
|
||||||
|
meta = pat.toPreserves.drop()
|
||||||
|
check $meta == "<group <rec group> {0: <group <rec rec> {0: <lit foo>}> 1: <group <dict> {666: <_>}>}>"
|
||||||
|
|
||||||
|
test "observe":
|
||||||
|
let
|
||||||
|
val = Observe(pattern: LaterThan ?: {0: drop 12.24}).toPreserves
|
||||||
|
pat = grab(val)
|
||||||
|
check pat.matches(val)
|
||||||
|
check pat.capture(val) == @[val]
|
||||||
|
let
|
||||||
|
meta = observePattern(!LaterThan, {@[0.toPreserves]: grabLit()})
|
||||||
|
res = parsePreserves "[12.24]"
|
||||||
|
check meta.matches(val)
|
||||||
|
check meta.capture(val).toPreserves == res
|
||||||
|
|
||||||
|
test "connect-transport":
|
||||||
|
let pat = parsePreserves"""
|
||||||
|
<group <rec connect-transport> {0: <group <rec unix> {0: <lit "/run/user/1000/dataspace">}> 2: <group <rec accepted> {0: <bind <_>>}>}>
|
||||||
|
""".preservesTo(Pattern).get
|
||||||
|
let val = parsePreserves"""
|
||||||
|
<connect-transport <unix "/run/user/1000/dataspace"> #:#f <accepted #:#f>>
|
||||||
|
"""
|
||||||
|
check pat.matches(val)
|
||||||
|
check pat.capture(val).toPreserves == parsePreserves "[#:#f]"
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/times
|
import std/times
|
||||||
import syndicate, syndicate/drivers/timers
|
import syndicate, syndicate/drivers/timers, preserves
|
||||||
|
|
||||||
|
var passCount = 0
|
||||||
|
|
||||||
runActor("timer-test") do (turn: var Turn):
|
runActor("timer-test") do (turn: var Turn):
|
||||||
let timers = newDataspace(turn)
|
let timers = newDataspace(turn)
|
||||||
|
@ -10,13 +12,21 @@ runActor("timer-test") do (turn: var Turn):
|
||||||
|
|
||||||
onPublish(turn, timers, ?LaterThan(seconds: 1356100000)):
|
onPublish(turn, timers, ?LaterThan(seconds: 1356100000)):
|
||||||
echo "now in 13th bʼakʼtun"
|
echo "now in 13th bʼakʼtun"
|
||||||
|
inc passCount
|
||||||
|
|
||||||
after(turn, timers, initDuration(seconds = 3)) do (turn: var Turn):
|
after(turn, timers, initDuration(seconds = 3)) do (turn: var Turn):
|
||||||
echo "third timer expired"
|
echo "third timer expired"
|
||||||
stopActor(turn)
|
assert passCount == 3
|
||||||
|
inc passCount
|
||||||
|
|
||||||
after(turn, timers, initDuration(seconds = 1)) do (turn: var Turn):
|
after(turn, timers, initDuration(seconds = 1)) do (turn: var Turn):
|
||||||
echo "first timer expired"
|
echo "first timer expired"
|
||||||
|
assert passCount == 1
|
||||||
|
inc passCount
|
||||||
|
|
||||||
after(turn, timers, initDuration(seconds = 2)) do (turn: var Turn):
|
after(turn, timers, initDuration(seconds = 2)) do (turn: var Turn):
|
||||||
echo "second timer expired"
|
echo "second timer expired"
|
||||||
|
assert passCount == 2
|
||||||
|
inc passCount
|
||||||
|
|
||||||
|
doAssert passCount == 4, $passCount
|
||||||
|
|
Loading…
Reference in New Issue