Update dataspace patterns protocol
Skeletons do not check for the presence of ignored entries in pattern matches, this will cause bugs!
This commit is contained in:
parent
2aaa588f6a
commit
06898e4ec1
|
@ -19,7 +19,7 @@ proc `!`*(typ: static typedesc): Pattern {.inline.} =
|
|||
patterns.dropType(typ)
|
||||
|
||||
proc `?`*[T](val: T): Pattern {.inline.} =
|
||||
patterns.grab[T](val)
|
||||
patterns.drop[T](val)
|
||||
|
||||
proc `?:`*(typ: static typedesc): Pattern {.inline.} =
|
||||
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.} =
|
||||
patterns.grab(typ, bindings)
|
||||
|
||||
proc `??`*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern {.inline.} =
|
||||
patterns.inject(pat, bindings)
|
||||
|
||||
type
|
||||
PublishProc = proc (turn: var Turn; v: Value; h: Handle) {.closure.}
|
||||
RetractProc = proc (turn: var Turn; h: Handle) {.closure.}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
include_rules
|
||||
NIM_FLAGS += --path:$(TUP_CWD)/..
|
||||
: foreach *.nim |> !nim_check |>
|
||||
: patterns.nim |> !nim_bin |>
|
||||
|
|
|
@ -134,7 +134,7 @@ proc spawnTimerDriver*(turn: var Turn; ds: Cap): Actor {.discardable.} =
|
|||
## dataspace observations of timeouts on `ds`.
|
||||
linkActor(turn, "timers") do (turn: var Turn):
|
||||
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):
|
||||
driver.deadlines[deadline] = turn.facet
|
||||
discard trampoline(whelp await(driver, deadline))
|
||||
|
@ -144,5 +144,5 @@ proc spawnTimerDriver*(turn: var Turn; ds: Cap): Actor {.discardable.} =
|
|||
proc after*(turn: var Turn; ds: Cap; dur: Duration; act: TurnAction) =
|
||||
## Execute `act` after some duration of time.
|
||||
var later = wallFloat() + dur.inMilliseconds.float / 1_000.0
|
||||
onPublish(turn, ds, grab LaterThan(seconds: later)):
|
||||
onPublish(turn, ds, ?LaterThan(seconds: later)):
|
||||
act(turn)
|
||||
|
|
|
@ -1,70 +1,46 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[algorithm, assertions, options, sequtils, tables, typetraits]
|
||||
import std/[assertions, options, tables, typetraits]
|
||||
|
||||
import preserves
|
||||
import ./protocols/dataspacePatterns
|
||||
import ./protocols/[dataspacePatterns, dataspace]
|
||||
from ./actors import Cap
|
||||
|
||||
export dataspacePatterns.`$`, PatternKind, DCompoundKind, AnyAtomKind
|
||||
export dataspacePatterns.`$`, AnyAtomKind, GroupTypeKind, PatternKind
|
||||
|
||||
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
|
||||
|
||||
iterator orderedEntries*(dict: DCompoundDict): (Value, Pattern) =
|
||||
## Iterate a `DCompoundDict` in Preserves order.
|
||||
## 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(b: sink PatternBind): Pattern =
|
||||
Pattern(orKind: PatternKind.`bind`, `bind`: b)
|
||||
|
||||
proc toPattern(d: sink DBind): Pattern =
|
||||
Pattern(orKind: PatternKind.DBind, dbind: d)
|
||||
proc toPattern(l: sink PatternLit): Pattern =
|
||||
Pattern(orKind: PatternKind.`lit`, lit: l)
|
||||
|
||||
proc toPattern(d: sink DLit): Pattern =
|
||||
Pattern(orKind: PatternKind.DLit, dlit: d)
|
||||
proc toPattern(g: sink PatternGroup): Pattern =
|
||||
Pattern(orKind: PatternKind.`group`, group: g)
|
||||
|
||||
proc toPattern(aa: sink AnyAtom): Pattern =
|
||||
DLit(value: aa).toPattern
|
||||
proc toPattern(a: sink AnyAtom): Pattern =
|
||||
PatternLit(value: a).toPattern
|
||||
|
||||
proc toPattern(d: sink DCompound): Pattern =
|
||||
Pattern(orKind: PatternKind.DCompound, dcompound: d)
|
||||
proc grab*(p: sink Pattern): Pattern =
|
||||
PatternBind(pattern: p).toPattern
|
||||
|
||||
proc toPattern(d: sink DCompoundRec): Pattern =
|
||||
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)
|
||||
proc drop*(): Pattern = Pattern(orKind: PatternKind.`discard`)
|
||||
## 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.
|
||||
|
||||
proc grab*(pr: Value): Pattern =
|
||||
proc drop*(pr: Value): Pattern =
|
||||
## Convert a `Preserve` value to a `Pattern`.
|
||||
runnableExamples:
|
||||
from std/unittest import check
|
||||
import preserves
|
||||
check:
|
||||
$(grab parsePreserves"""<foo "bar" #"00" [0 1 2.0] {maybe: #t} <_>>""") ==
|
||||
"""<rec foo [<lit "bar"> <lit #"00"> <arr [<lit 0> <lit 1> <lit 2.0>]> <dict {maybe: <lit #t>}> <_>]>"""
|
||||
$("""<foo "bar" #"00" [0 1 2.0] {maybe: #t} <_>>""".parsePreserves.drop) ==
|
||||
"""<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
|
||||
of pkBoolean:
|
||||
|
@ -73,43 +49,65 @@ proc grab*(pr: Value): Pattern =
|
|||
AnyAtom(orKind: AnyAtomKind.`double`, double: pr.float).toPattern
|
||||
of pkRegister:
|
||||
AnyAtom(orKind: AnyAtomKind.`int`, int: pr.register).toPattern
|
||||
of pkBigInt:
|
||||
raiseAssert "cannot make a pattern over a big integer"
|
||||
of pkString:
|
||||
AnyAtom(orKind: AnyAtomKind.`string`, string: pr.string).toPattern
|
||||
of pkByteString:
|
||||
AnyAtom(orKind: AnyAtomKind.`bytes`, bytes: pr.bytes).toPattern
|
||||
of pkSymbol:
|
||||
AnyAtom(orKind: AnyAtomKind.`symbol`, symbol: pr.symbol).toPattern
|
||||
|
||||
of pkRecord:
|
||||
if (pr.isRecord("_") and pr.arity == 0) or (pr.isRecord("bind") and pr.arity == 1):
|
||||
if pr.isRecord("_", 0):
|
||||
drop()
|
||||
elif pr.isRecord("bind", 1):
|
||||
pr.fields[0].drop
|
||||
else:
|
||||
DCompoundRec(
|
||||
label: pr.label,
|
||||
fields: map[Value, Pattern](pr.fields, grab)).toPattern
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.rec))
|
||||
group.`type`.rec.label = pr.label
|
||||
var i: int
|
||||
for v in pr.fields:
|
||||
group.entries[toPreserves i] = drop v
|
||||
inc i
|
||||
group.toPattern
|
||||
|
||||
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:
|
||||
raiseAssert "cannot construct a pattern over a set literal"
|
||||
|
||||
of pkDictionary:
|
||||
var dict = DCompoundDict()
|
||||
for key, val in pr.pairs: dict.entries[key] = grab val
|
||||
dict.toPattern
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.dict))
|
||||
for key, val in pr.pairs:
|
||||
group.entries[key] = drop val
|
||||
group.toPattern
|
||||
|
||||
of pkEmbedded:
|
||||
if pr.embeddedRef.isNil: drop()
|
||||
else:
|
||||
AnyAtom(orKind: AnyAtomKind.`embedded`, embedded: pr.embeddedRef).toPattern
|
||||
else:
|
||||
raise newException(ValueError, "cannot generate a pattern for unhandled Value type")
|
||||
#else:
|
||||
# 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`.
|
||||
## This proc is called `drop` because the value `x` is matched but discarded.
|
||||
runnableExamples:
|
||||
from std/unittest import check
|
||||
check:
|
||||
$grab(true) == "<lit #t>"
|
||||
$grab(3.14) == "<lit 3.14>"
|
||||
$grab([0, 1, 2, 3]) == "<arr [<lit 0> <lit 1> <lit 2> <lit 3>]>"
|
||||
grab(x.toPreserves)
|
||||
$drop(true) == "<lit #t>"
|
||||
$drop(3.14) == "<lit 3.14>"
|
||||
$drop([0, 1, 2, 3]) == "<group <arr> {0: <lit 0> 1: <lit 1> 2: <lit 2> 3: <lit 3>}>"
|
||||
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 =
|
||||
## Derive a `Pattern` from type `typ`.
|
||||
|
@ -120,40 +118,41 @@ proc grabType*(typ: static typedesc): Pattern =
|
|||
from std/unittest import check
|
||||
check:
|
||||
$grabType(array[3, int]) ==
|
||||
"""<arr [<bind <_>> <bind <_>> <bind <_>>]>"""
|
||||
"""<group <arr> {0: <bind <_>> 1: <bind <_>> 2: <bind <_>> 3: <bind <_>>}>"""
|
||||
type
|
||||
Point = tuple[x: int; y: int]
|
||||
Rect {.preservesRecord: "rect".} = tuple[a: Point; B: Point]
|
||||
ColoredRect {.preservesDictionary.} = tuple[color: string; rect: Rect]
|
||||
check:
|
||||
$(grabType Point) ==
|
||||
"<arr [<bind <_>> <bind <_>>]>"
|
||||
"<group <arr> {0: <bind <_>> 1: <bind <_>>}>"
|
||||
$(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) ==
|
||||
"<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:
|
||||
grabType(pointerBase(typ))
|
||||
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):
|
||||
add(rec.fields, grabType(typeof f))
|
||||
result = rec.toPattern
|
||||
group.entries[group.entries.len.toPreserves] = grabType(typeof f)
|
||||
group.toPattern
|
||||
elif typ.hasPreservesDictionaryPragma:
|
||||
var dict = DCompoundDict()
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||
for key, val in fieldPairs(default typ):
|
||||
dict.entries[key.toSymbol] = grabType(typeof val)
|
||||
dict.toPattern
|
||||
group.entries[key.toSymbol] = grabType(typeof val)
|
||||
group.toPattern
|
||||
elif typ is tuple:
|
||||
var arr = DCompoundArr()
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||
for _, f in fieldPairs(default typ):
|
||||
add(arr.items, grabType(typeof f))
|
||||
arr.toPattern
|
||||
group.entries[group.entries.len.toPreserves] = grabType(typeof f)
|
||||
group.toPattern
|
||||
elif typ is array:
|
||||
var arr = DCompoundArr()
|
||||
arr.items.setLen(len(typ))
|
||||
for e in arr.items.mitems: e = grab()
|
||||
arr.toPattern
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||
for i in 0..len(typ):
|
||||
group.entries[toPreserves i] = grab()
|
||||
group.toPattern
|
||||
else:
|
||||
grab()
|
||||
|
||||
|
@ -166,59 +165,47 @@ proc dropType*(typ: static typedesc): Pattern =
|
|||
when typ is ref:
|
||||
dropType(pointerBase(typ))
|
||||
elif typ.hasPreservesRecordPragma:
|
||||
var rec = DCompoundRec(label: typ.recordLabel.toSymbol)
|
||||
rec.fields.setLen(fieldCount typ)
|
||||
for i, _ in rec.fields:
|
||||
rec.fields[i] = drop()
|
||||
result = rec.toPattern
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||
group.`type`.rec.label = typ.recordLabel.toSymbol
|
||||
let high = typ.fieldCount.pred
|
||||
if high >= 0: group.entries[high.toPreserves] = drop()
|
||||
group.toPattern
|
||||
elif typ.hasPreservesDictionaryPragma:
|
||||
DCompoundDict().toPattern
|
||||
elif typ is tuple:
|
||||
var arr = DCompoundArr()
|
||||
arr.items.setLen(len typ)
|
||||
for i, _ in arr.items:
|
||||
arr.items[i] = drop()
|
||||
arr.toPattern
|
||||
elif typ is array:
|
||||
var arr = DCompoundArr()
|
||||
arr.items.setLen(len(typ))
|
||||
for e in arr.items.mitems: e = drop()
|
||||
arr.toPattern
|
||||
PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`)).toPattern
|
||||
elif typ is tuple or typ is array:
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||
let high = typ.fieldCount.pred
|
||||
if high >= 0: group.entries[high.toPreserves] = drop()
|
||||
group.toPattern
|
||||
else:
|
||||
drop()
|
||||
|
||||
proc lookup(bindings: openArray[(int, Pattern)]; i: int): Pattern =
|
||||
for (j, b) in bindings:
|
||||
if i == j: return b
|
||||
return drop()
|
||||
proc bindEntries(group: var PatternGroup; bindings: openArray[(int, Pattern)]) =
|
||||
## Set `bindings` for a `group`.
|
||||
for (i, pat) in bindings: group.entries[toPreserves i] = pat
|
||||
|
||||
proc grab*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern =
|
||||
## Construct a `Pattern` from type `typ` with pattern `bindings` by integer offset.
|
||||
when typ is ptr | ref:
|
||||
grab(pointerBase(typ), bindings)
|
||||
elif typ.hasPreservesRecordPragma:
|
||||
var rec = DCompoundRec(label: typ.recordLabel.toSymbol)
|
||||
rec.fields.setLen(fieldCount typ)
|
||||
var i: int
|
||||
for _, f in fieldPairs(default typ):
|
||||
rec.fields[i] = lookup(bindings, i)
|
||||
inc i
|
||||
result = rec.toPattern
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||
group.`type`.rec.label = typ.recordLabel.toSymbol
|
||||
bindEntries(group, bindings)
|
||||
group.toPattern
|
||||
elif typ is tuple:
|
||||
var arr = DCompoundArr()
|
||||
arr.items.setLen(fieldCount typ)
|
||||
var i: int
|
||||
for _, f in fieldPairs(default typ):
|
||||
arr.items[i] = lookup(bindings, i)
|
||||
inc i
|
||||
result = arr.toPattern
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||
bindEntries(group, bindings)
|
||||
group.toPattern
|
||||
else:
|
||||
{.error: "grab with indexed bindings not implemented for " & $typ.}
|
||||
|
||||
proc grab*(typ: static typedesc; bindings: sink openArray[(Value, Pattern)]): Pattern =
|
||||
## Construct a `Pattern` from type `typ` with dictionary field `bindings`.
|
||||
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:
|
||||
{.error: "grab with dictionary bindings not implemented for " & $typ.}
|
||||
|
||||
|
@ -226,11 +213,11 @@ proc grabLit*(): Pattern =
|
|||
runnableExamples:
|
||||
from std/unittest import check
|
||||
check:
|
||||
$grabLit() == """<rec lit [<bind <_>>]>"""
|
||||
grabType(dataspacePatterns.DLit)
|
||||
$grabLit() == """<group <rec lit> {0: <bind <_>>}>"""
|
||||
grabType(dataspacePatterns.PatternLit)
|
||||
|
||||
proc grabDict*(): Pattern =
|
||||
grabType(dataspacePatterns.DCompoundDict)
|
||||
grabType(dataspacePatterns.GroupTypeDict)
|
||||
|
||||
proc unpackLiterals*(pr: Value): Value =
|
||||
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):
|
||||
pr = pr.record[0]
|
||||
|
||||
proc inject*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern =
|
||||
## Construct a `Pattern` from `pat` with injected overrides from `bindings`.
|
||||
proc inject*(pattern: sink Pattern; p: Pattern; path: varargs[Value, toPreserves]): Pattern =
|
||||
## Inject `p` inside `pattern` at `path`.
|
||||
## Injects are made at offsets indexed by the discard (`<_>`) patterns in `pat`.
|
||||
proc inject(pat: Pattern; bindings: openArray[(int, Pattern)]; offset: var int): Pattern =
|
||||
case pat.orKind
|
||||
of PatternKind.DDiscard:
|
||||
result = pat
|
||||
for (off, injection) in bindings:
|
||||
if off == offset:
|
||||
result = injection
|
||||
break
|
||||
inc offset
|
||||
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 inject(pat: var Pattern; path: openarray[Value]) =
|
||||
if len(path) == 0:
|
||||
pat = p
|
||||
elif pat.orKind != PatternKind.`group`:
|
||||
raise newException(ValueError, "cannot inject along specified path")
|
||||
else:
|
||||
inject(pat.group.entries[path[0]], path[1..path.high])
|
||||
result = pattern
|
||||
inject(result, path)
|
||||
|
||||
proc grabRecord*(label: Value, fields: varargs[Pattern]): Pattern =
|
||||
runnableExamples:
|
||||
from std/unittest import check
|
||||
import syndicate/actors, preserves
|
||||
import preserves
|
||||
check:
|
||||
$grabRecord("Says".toSymbol, grab(), grab()) ==
|
||||
"""<rec Says [<bind <_>> <bind <_>>]>"""
|
||||
DCompoundRec(label: label, fields: fields.toSeq).toPattern
|
||||
"""<group <rec Says> {0: <bind <_>> 1: <bind <_>>}>"""
|
||||
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 =
|
||||
## 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 =
|
||||
## 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 =
|
||||
## Construct a pattern that grabs some dictionary pairs.
|
||||
## Keys are converted from strings to symbols.
|
||||
result = DCompoundDict().toPattern
|
||||
for (key, val) in bindings.items:
|
||||
result.dcompound.dict.entries[key.toSymbol] = val
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||
for (key, val) in bindings: group.entries[toSymbol key] = 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 =
|
||||
case pat.orKind
|
||||
of PatternKind.DDiscard:
|
||||
of PatternKind.`discard`:
|
||||
discard
|
||||
of PatternKind.DBind:
|
||||
of PatternKind.`bind`:
|
||||
if index < values.len:
|
||||
result = move values[index]
|
||||
inc index
|
||||
of PatternKind.DLit:
|
||||
result = pat.dlit.value.toPreserves
|
||||
of PatternKind.DCompound:
|
||||
result = depattern(pat.dcompound, values, index)
|
||||
of PatternKind.`lit`:
|
||||
result = pat.`lit`.value.toPreserves
|
||||
of PatternKind.`group`:
|
||||
result = depattern(pat.group, values, index)
|
||||
|
||||
proc depattern(comp: DCompound; values: var seq[Value]; index: var int): Value =
|
||||
case comp.orKind
|
||||
of DCompoundKind.rec:
|
||||
result = initRecord(comp.rec.label, comp.rec.fields.len)
|
||||
for i, f in comp.rec.fields:
|
||||
result[i] = depattern(f, values, index)
|
||||
of DCompoundKind.arr:
|
||||
result = initSequence(comp.arr.items.len)
|
||||
for i, e in comp.arr.items:
|
||||
result[i] = depattern(e, values, index)
|
||||
of DCompoundKind.dict:
|
||||
proc depattern(group: PatternGroup; values: var seq[Value]; index: var int): Value =
|
||||
case group.`type`.orKind
|
||||
of GroupTypeKind.rec:
|
||||
result = initRecord(group.`type`.rec.label, group.entries.len)
|
||||
var i: int
|
||||
for key, val in group.entries:
|
||||
if i.fromPreserves key:
|
||||
result[i] = depattern(val, values, index)
|
||||
of GroupTypeKind.arr:
|
||||
result = initSequence(group.entries.len)
|
||||
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)
|
||||
for key, val in comp.dict.entries:
|
||||
for key, val in group.entries:
|
||||
result[key] = depattern(val, values, index)
|
||||
|
||||
proc depattern*(pat: Pattern; values: sink seq[Value]): Value =
|
||||
## 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
|
||||
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 =
|
||||
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
|
||||
Path* = seq[Value]
|
||||
Paths* = seq[Path]
|
||||
Captures* = seq[Value]
|
||||
Analysis* = tuple
|
||||
presentPaths: Paths
|
||||
constPaths: Paths
|
||||
constValues: seq[Value]
|
||||
capturePaths: Paths
|
||||
|
||||
func walk(result: var Analysis; path: var Path; p: Pattern)
|
||||
|
||||
func walk(result: var Analysis; path: var Path; key: int|Value; pat: Pattern) =
|
||||
path.add(key.toPreserves)
|
||||
func walk(result: var Analysis; path: var Path; key: Value; pat: Pattern) =
|
||||
path.add(key)
|
||||
walk(result, path, pat)
|
||||
discard path.pop
|
||||
|
||||
func walk(result: var Analysis; path: var Path; p: Pattern) =
|
||||
case p.orKind
|
||||
of PatternKind.DCompound:
|
||||
case p.dcompound.orKind
|
||||
of DCompoundKind.rec:
|
||||
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:
|
||||
of PatternKind.group:
|
||||
for k, v in p.group.entries: walk(result, path, k, v)
|
||||
of PatternKind.`bind`:
|
||||
result.capturePaths.add(path)
|
||||
walk(result, path, p.dbind.pattern)
|
||||
of PatternKind.DDiscard: discard
|
||||
of PatternKind.DLit:
|
||||
walk(result, path, p.`bind`.pattern)
|
||||
of PatternKind.`discard`:
|
||||
result.presentPaths.add(path)
|
||||
of PatternKind.`lit`:
|
||||
result.constPaths.add(path)
|
||||
result.constValues.add(p.dlit.value.toPreserves)
|
||||
result.constValues.add(p.`lit`.value.toPreserves)
|
||||
|
||||
func analyse*(p: Pattern): Analysis =
|
||||
var path: Path
|
||||
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] =
|
||||
var res = newSeq[Value](paths.len)
|
||||
for i, path in paths:
|
||||
|
@ -408,30 +416,27 @@ func projectPaths*(v: Value; paths: Paths): Option[Captures] =
|
|||
proc matches*(pat: Pattern; pr: Value): bool =
|
||||
let analysis = analyse(pat)
|
||||
assert analysis.constPaths.len == analysis.constValues.len
|
||||
for i, path in analysis.constPaths:
|
||||
let v = step(pr, path)
|
||||
if v.isNone: return false
|
||||
if analysis.constValues[i] != v.get: return false
|
||||
for path in analysis.capturePaths:
|
||||
if isNone step(pr, path): return false
|
||||
true
|
||||
result = checkPresence(pr, analysis.presentPaths)
|
||||
if result:
|
||||
for i, path in analysis.constPaths:
|
||||
let v = step(pr, path)
|
||||
if v.isNone: return false
|
||||
if analysis.constValues[i] != v.get: return false
|
||||
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)
|
||||
assert analysis.constPaths.len == analysis.constValues.len
|
||||
for i, path in analysis.constPaths:
|
||||
let v = step(pr, path)
|
||||
if v.isNone : return @[]
|
||||
if analysis.constValues[i] != v.get: return @[]
|
||||
for path in analysis.capturePaths:
|
||||
let v = step(pr, path)
|
||||
if v.isNone: return @[]
|
||||
result.add(get v)
|
||||
if checkPresence(pr, analysis.presentPaths):
|
||||
for i, path in analysis.constPaths:
|
||||
let v = step(pr, path)
|
||||
if v.isNone : return @[]
|
||||
if analysis.constValues[i] != v.get: return @[]
|
||||
for path in analysis.capturePaths:
|
||||
let v = step(pr, path)
|
||||
if v.isNone: return @[]
|
||||
result.add(get v)
|
||||
|
||||
when isMainModule:
|
||||
let txt = readAll stdin
|
||||
if txt != "":
|
||||
let
|
||||
v = parsePreserves(txt)
|
||||
pat = grab v
|
||||
stdout.writeLine(pat)
|
||||
stdout.writeLine stdin.readAll.parsePreserves.grab
|
||||
|
|
|
@ -29,58 +29,58 @@ type
|
|||
`embedded`* {.preservesEmbedded.}: EmbeddedRef
|
||||
|
||||
|
||||
DLit* {.preservesRecord: "lit".} = object
|
||||
`value`*: AnyAtom
|
||||
|
||||
DBind* {.preservesRecord: "bind".} = object
|
||||
`pattern`*: Pattern
|
||||
|
||||
DDiscard* {.preservesRecord: "_".} = object
|
||||
|
||||
DCompoundKind* {.pure.} = enum
|
||||
GroupTypeKind* {.pure.} = enum
|
||||
`rec`, `arr`, `dict`
|
||||
DCompoundRec* {.preservesRecord: "rec".} = object
|
||||
GroupTypeRec* {.preservesRecord: "rec".} = object
|
||||
`label`*: Value
|
||||
`fields`*: seq[Pattern]
|
||||
|
||||
DCompoundArr* {.preservesRecord: "arr".} = object
|
||||
`items`*: seq[Pattern]
|
||||
GroupTypeArr* {.preservesRecord: "arr".} = object
|
||||
|
||||
GroupTypeDict* {.preservesRecord: "dict".} = object
|
||||
|
||||
`GroupType`* {.preservesOr.} = object
|
||||
case orKind*: GroupTypeKind
|
||||
of GroupTypeKind.`rec`:
|
||||
`rec`*: GroupTypeRec
|
||||
|
||||
DCompoundDict* {.preservesRecord: "dict".} = object
|
||||
`entries`*: Table[Value, Pattern]
|
||||
of GroupTypeKind.`arr`:
|
||||
`arr`*: GroupTypeArr
|
||||
|
||||
`DCompound`* {.preservesOr.} = object
|
||||
case orKind*: DCompoundKind
|
||||
of DCompoundKind.`rec`:
|
||||
`rec`* {.preservesEmbedded.}: DCompoundRec
|
||||
|
||||
of DCompoundKind.`arr`:
|
||||
`arr`* {.preservesEmbedded.}: DCompoundArr
|
||||
|
||||
of DCompoundKind.`dict`:
|
||||
`dict`* {.preservesEmbedded.}: DCompoundDict
|
||||
of GroupTypeKind.`dict`:
|
||||
`dict`*: GroupTypeDict
|
||||
|
||||
|
||||
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
|
||||
case orKind*: PatternKind
|
||||
of PatternKind.`DDiscard`:
|
||||
`ddiscard`*: DDiscard
|
||||
of PatternKind.`discard`:
|
||||
`discard`*: PatternDiscard
|
||||
|
||||
of PatternKind.`DBind`:
|
||||
`dbind`* {.preservesEmbedded.}: DBind
|
||||
of PatternKind.`bind`:
|
||||
`bind`* {.preservesEmbedded.}: PatternBind
|
||||
|
||||
of PatternKind.`DLit`:
|
||||
`dlit`* {.preservesEmbedded.}: DLit
|
||||
of PatternKind.`lit`:
|
||||
`lit`* {.preservesEmbedded.}: PatternLit
|
||||
|
||||
of PatternKind.`DCompound`:
|
||||
`dcompound`* {.preservesEmbedded.}: DCompound
|
||||
of PatternKind.`group`:
|
||||
`group`* {.preservesEmbedded.}: PatternGroup
|
||||
|
||||
|
||||
proc `$`*(x: AnyAtom | DLit | DBind | DDiscard | DCompound | Pattern): string =
|
||||
proc `$`*(x: AnyAtom | GroupType | Pattern): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*(x: AnyAtom | DLit | DBind | DDiscard | DCompound | Pattern): seq[
|
||||
byte] =
|
||||
proc encode*(x: AnyAtom | GroupType | Pattern): seq[byte] =
|
||||
encode(toPreserves(x))
|
||||
|
|
|
@ -251,7 +251,7 @@ proc recv(relay: Relay; buf: openarray[byte]; slice: Slice[int]) =
|
|||
var pr = decode(relay.wireBuf)
|
||||
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)
|
||||
var pr = decode(relay.wireBuf)
|
||||
if pr.isSome: dispatch(relay, pr.get)
|
||||
|
@ -393,11 +393,12 @@ when defined(posix):
|
|||
entity.alive = false
|
||||
close(entity.sock)
|
||||
onStop(turn, kill)
|
||||
publish(turn, ds, TransportConnection(
|
||||
var ass = TransportConnection(
|
||||
`addr`: ta.toPreserves,
|
||||
control: newCap(entity, turn),
|
||||
resolved: entity.relay.peer.accepted,
|
||||
))
|
||||
)
|
||||
publish(turn, ds, ass)
|
||||
run(entity.relay.facet, setup)
|
||||
let buf = new seq[byte]
|
||||
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):
|
||||
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) =
|
||||
let stepPat = grabRecord(stepType, grab())
|
||||
let pat = ?Observe(pattern: ResolvedPathStep?:{1: stepPat}) ?? {0: grabLit(), 1: grab()}
|
||||
let pat = observePattern(
|
||||
ResolvedPathStep?:{1: grabRecord(stepType)},
|
||||
{ @[0.toPreserve]: grabLit(), @[1.toPreserve]: grab() },
|
||||
)
|
||||
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 =
|
||||
var res = ass.preservesTo Resolved
|
||||
if res.isSome:
|
||||
if res.get.orKind == ResolvedKind.accepted and
|
||||
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))
|
||||
var res: Resolved
|
||||
if res.fromPreserves ass:
|
||||
cb(turn, stepDetail.value, origin, res)
|
||||
proc action(turn: var Turn) =
|
||||
stop(turn)
|
||||
result = action
|
||||
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) =
|
||||
## Spawn actors that manage routes and appeasing gatekeepers.
|
||||
let transPat = ?Observe(pattern: !TransportConnection) ?? { 0: grab() }
|
||||
## Spawn actors that manage routes and appease gatekeepers.
|
||||
|
||||
let transPat = observePattern(!TransportConnection, { @[0.toPreserves]: grab() })
|
||||
# Use a generic pattern and type matching
|
||||
# in the during handler because it is easy.
|
||||
|
||||
|
@ -600,20 +598,21 @@ proc spawnRelays*(turn: var Turn; ds: Cap) =
|
|||
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]):
|
||||
for i, transAddr in route.value.transports:
|
||||
connectRoute(turn, ds, route.value, i)
|
||||
|
||||
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(
|
||||
origin: origin, pathStep: step, resolved: next.accepted))
|
||||
origin: origin, pathStep: step, resolved: res))
|
||||
|
||||
type BootProc* = proc (turn: var Turn; ds: Cap) {.closure.}
|
||||
|
||||
proc resolve*(turn: var Turn; ds: Cap; route: Route; bootProc: BootProc) =
|
||||
## 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):
|
||||
bootProc(turn, dst)
|
||||
|
||||
|
|
|
@ -9,32 +9,28 @@ import ./actors, ./bags, ./patterns
|
|||
import ./protocols/dataspacePatterns
|
||||
|
||||
type
|
||||
DCompound = dataspacePatterns.DCompound
|
||||
Pattern = dataspacePatterns.Pattern
|
||||
Path = seq[Value]
|
||||
ClassKind = enum classNone, classRecord, classSequence, classDictionary
|
||||
Class = object
|
||||
kind: ClassKind
|
||||
label: Value
|
||||
arity: int
|
||||
|
||||
func classOf(v: Value): Class =
|
||||
case v.kind
|
||||
of pkRecord: Class(kind: classRecord, label: v.label, arity: v.arity)
|
||||
of pkSequence: Class(kind: classSequence, arity: v.len)
|
||||
of pkRecord: Class(kind: classRecord, label: v.label)
|
||||
of pkSequence: Class(kind: classSequence)
|
||||
of pkDictionary: Class(kind: classDictionary)
|
||||
else: Class(kind: classNone)
|
||||
|
||||
proc classOf(p: Pattern): Class =
|
||||
if p.orKind == PatternKind.DCompound:
|
||||
case p.dcompound.orKind
|
||||
of DCompoundKind.rec:
|
||||
Class(kind: classRecord,
|
||||
label: p.dcompound.rec.label,
|
||||
arity: p.dcompound.rec.fields.len)
|
||||
of DCompoundKind.arr:
|
||||
Class(kind: classSequence, arity: p.dcompound.arr.items.len)
|
||||
of DCompoundKind.dict:
|
||||
if p.orKind == PatternKind.group:
|
||||
case p.group.type.orKind
|
||||
of GroupTypeKind.rec:
|
||||
Class(kind: classRecord, label: p.group.`type`.rec.label)
|
||||
of GroupTypeKind.arr:
|
||||
Class(kind: classSequence)
|
||||
of GroupTypeKind.dict:
|
||||
Class(kind: classDictionary)
|
||||
else:
|
||||
Class(kind: classNone)
|
||||
|
@ -69,13 +65,14 @@ type
|
|||
LeafProc = proc (l: Leaf; v: 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)
|
||||
if result.isNil:
|
||||
new result
|
||||
cont.leafMap[constPaths] = result
|
||||
assert not cont.isEmpty
|
||||
for ass in cont.cache:
|
||||
# TODO: check presence
|
||||
let key = projectPaths(ass, constPaths)
|
||||
if key.isSome:
|
||||
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]()
|
||||
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] =
|
||||
case pat.orKind
|
||||
of PatternKind.DDiscard, PatternKind.DLit:
|
||||
of PatternKind.`discard`, PatternKind.lit:
|
||||
result = (popCount, node)
|
||||
of PatternKind.DBind:
|
||||
result = extendWalk(node, popCount, stepIndex, pat.dbind.pattern, path)
|
||||
of PatternKind.DCompound:
|
||||
of PatternKind.`bind`:
|
||||
result = extendWalk(node, popCount, stepIndex, pat.`bind`.pattern, path)
|
||||
of PatternKind.`group`:
|
||||
let
|
||||
selector: Selector = (popCount, stepIndex,)
|
||||
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):
|
||||
result.nextNode.continuation.cache.incl a
|
||||
result.popCount = 0
|
||||
for step, p in pat.dcompound.pairs:
|
||||
for step, p in pat.group.entries:
|
||||
add(path, step)
|
||||
result = extendWalk(result.nextNode, result.popCount, step, p, path)
|
||||
discard pop(path)
|
||||
|
@ -231,7 +216,7 @@ proc add*(index: var Index; turn: var Turn; pattern: Pattern; observer: Cap) =
|
|||
let
|
||||
cont = index.root.extend(pattern)
|
||||
analysis = analyse pattern
|
||||
constValMap = cont.getLeaves(analysis.constPaths)
|
||||
constValMap = cont.getLeaves(analysis.presentPaths, analysis.constPaths)
|
||||
leaf = constValMap.getLeaf(analysis.constValues)
|
||||
endpoints = leaf.getEndpoints(analysis.capturePaths)
|
||||
# TODO if endpoints.cachedCaptures.len > 0:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Package
|
||||
|
||||
version = "20240408"
|
||||
version = "20240421"
|
||||
author = "Emery Hemingway"
|
||||
description = "Syndicated actors for conversational concurrency"
|
||||
license = "Unlicense"
|
||||
|
|
|
@ -1,103 +1,87 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# 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
|
||||
|
||||
test "patterns":
|
||||
let
|
||||
pat = ?Observe(pattern: !Foo) ?? {0: grab()}
|
||||
text = """<rec Observe [<rec rec [<lit foo> <arr [<bind <_>> <_> <_>]>]> <_>]>"""
|
||||
check($pat == text)
|
||||
suite "example":
|
||||
var pat: Pattern
|
||||
check pat.fromPreserves parsePreserves"""
|
||||
<group <arr> {
|
||||
0: <lit 1>
|
||||
1: <bind <group <arr> {
|
||||
0: <bind <_>>
|
||||
1: <_>
|
||||
}>>
|
||||
2: <_>
|
||||
}>
|
||||
"""
|
||||
|
||||
let
|
||||
worte = @["alles", "in", "ordnung"]
|
||||
observer = Observe(pattern: inject(?:Foo, { 0: ?worte })).toPreserves
|
||||
have = capture(pat, observer).toPreserves.unpackLiterals
|
||||
want = [worte.toPreserves].toPreserves
|
||||
check(have == want)
|
||||
const A = "[1 2 3]"
|
||||
test A:
|
||||
let v = parsePreserves A
|
||||
check:
|
||||
not pat.matches(v)
|
||||
|
||||
type Obj {.preservesDictionary.} = object
|
||||
a, b, c: int
|
||||
|
||||
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":
|
||||
const B = "[1 [2 3] 4]"
|
||||
test B:
|
||||
let
|
||||
obsA = parsePreserves"""<Observe <rec later-than [<lit 1704113731.419243>]> #f>"""
|
||||
obsB = parsePreserves"""<Observe <rec Observe [<rec rec [<lit later-than> <arr [<rec lit [<bind <_>>]>]>]> <_>]> #f>"""
|
||||
patA = """<rec later-than [<lit 1704113731.419243>]>""".parsePreserves.preservesTo(Pattern).get
|
||||
patB = """<rec Observe [<rec rec [<lit later-than> <arr [<rec lit [<bind <_>>]>]>]> <_>]>""".parsePreserves.preservesTo(Pattern).get
|
||||
v = parsePreserves B
|
||||
c = parsePreserves "[[2 3] 2]"
|
||||
check pat.matches(v)
|
||||
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:
|
||||
check patC.matches obsA
|
||||
|
||||
test $patB:
|
||||
checkpoint $obsA
|
||||
check patB.matches obsA
|
||||
|
||||
test "TransportConnection":
|
||||
const D = "[1 [2 3 4] 5]"
|
||||
test D:
|
||||
let
|
||||
pat = TransportConnection ?: { 2: ?:Rejected}
|
||||
text = """<rec connect-transport [<_> <_> <rec rejected [<bind <_>>]>]>"""
|
||||
check $pat == text
|
||||
v = parsePreserves D
|
||||
c = parsePreserves "[[2 3 4] 2]"
|
||||
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
|
||||
|
||||
import std/times
|
||||
import syndicate, syndicate/drivers/timers
|
||||
import syndicate, syndicate/drivers/timers, preserves
|
||||
|
||||
var passCount = 0
|
||||
|
||||
runActor("timer-test") do (turn: var Turn):
|
||||
let timers = newDataspace(turn)
|
||||
|
@ -10,13 +12,21 @@ runActor("timer-test") do (turn: var Turn):
|
|||
|
||||
onPublish(turn, timers, ?LaterThan(seconds: 1356100000)):
|
||||
echo "now in 13th bʼakʼtun"
|
||||
inc passCount
|
||||
|
||||
after(turn, timers, initDuration(seconds = 3)) do (turn: var Turn):
|
||||
echo "third timer expired"
|
||||
stopActor(turn)
|
||||
assert passCount == 3
|
||||
inc passCount
|
||||
|
||||
after(turn, timers, initDuration(seconds = 1)) do (turn: var Turn):
|
||||
echo "first timer expired"
|
||||
assert passCount == 1
|
||||
inc passCount
|
||||
|
||||
after(turn, timers, initDuration(seconds = 2)) do (turn: var Turn):
|
||||
echo "second timer expired"
|
||||
assert passCount == 2
|
||||
inc passCount
|
||||
|
||||
doAssert passCount == 4, $passCount
|
||||
|
|
Loading…
Reference in New Issue