Patterns: refactor

This commit is contained in:
Emery Hemingway 2023-03-23 14:06:44 -05:00
parent 71955f257d
commit b1627a491a
6 changed files with 261 additions and 351 deletions

View File

@ -12,11 +12,11 @@ runnableExamples:
me = "user"
presenceHandle = publish(turn, ds, Present(username: me))
onMessage(turn, ds, ?Says) do (who: string; what: string):
onMessage(turn, ds, grabType Says) do (who: string; what: string):
echo who, ": ", what
retract(turn, presenceHandle)
during(turn, ds, ?Present) do (username: string):
during(turn, ds, grabType Present) do (username: string):
echo "[", username, " arrived]"
message(turn, ds, Says(who: me, what: "users are losers"))
do:
@ -37,10 +37,25 @@ else:
export SturdyRef
export Actor, Assertion, Facet, Handle, Ref, Symbol, Turn, TurnAction,
`$`, `?`, addCallback, analyse, asyncCheck, bootDataspace,
drop, facet, future, grab, grabType, inFacet, message, newDataspace, publish,
`$`, addCallback, analyse, asyncCheck, bootDataspace,
drop, facet, future, grab, grabLit, inFacet, message, newDataspace, publish,
retract, replace, run, stop, unembed
proc `!`*(typ: static typedesc): Pattern {.inline.} =
patterns.dropType(typ)
proc `?`*(typ: static typedesc): Pattern {.inline.} =
patterns.grabType(typ)
proc `?`*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern {.inline.} =
patterns.grab(typ, bindings)
proc `??`*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern {.inline.} =
patterns.inject(pat, bindings)
proc `?`*[T](val: T): Pattern {.inline.} =
patterns.grab[T](val)
type
Observe* = dataspace.Observe[Ref]
PublishProc = proc (turn: var Turn; v: Assertion; h: Handle) {.closure, gcsafe.}

View File

@ -7,10 +7,10 @@ import ../syndicate/protocols/[protocol, sturdy]
export Handle
template generateIdType(T: untyped) =
type T* = distinct Natural
proc `==`*(x, y: T): bool {.borrow.}
proc `$`*(id: T): string {.borrow.}
template generateIdType(typ: untyped) =
type typ* = distinct Natural
proc `==`*(x, y: typ): bool {.borrow.}
proc `$`*(id: typ): string {.borrow.}
generateIdType(ActorId)
generateIdType(FacetId)

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
# SPDX-FileCopyrightText: ☭ Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/[options, sequtils, tables, typetraits]
@ -10,390 +10,254 @@ from ./actors import Ref
export dataspacePatterns.`$`, PatternKind, DCompoundKind, AnyAtomKind
type
AnyAtom* = dataspacePatterns.AnyAtom[Ref]
DBind* = dataspacePatterns.DBind[Ref]
DCompound* = dataspacePatterns.DCompound[Ref]
DCompoundArr* = dataspacePatterns.DCompoundArr[Ref]
DCompoundDict* = dataspacePatterns.DCompoundDict[Ref]
DCompoundRec* = dataspacePatterns.DCompoundRec[Ref]
DLit* = dataspacePatterns.DLit[Ref]
AnyAtom = dataspacePatterns.AnyAtom[Ref]
DBind = dataspacePatterns.DBind[Ref]
DCompound = dataspacePatterns.DCompound[Ref]
DCompoundArr = dataspacePatterns.DCompoundArr[Ref]
DCompoundDict = dataspacePatterns.DCompoundDict[Ref]
DCompoundRec = dataspacePatterns.DCompoundRec[Ref]
DLit = dataspacePatterns.DLit[Ref]
Pattern* = dataspacePatterns.Pattern[Ref]
proc `?`*(d: sink DBind): Pattern =
proc toPattern(d: sink DBind): Pattern =
Pattern(orKind: PatternKind.DBind, dbind: d)
proc `?`*(d: sink DLit): Pattern =
proc toPattern(d: sink DLit): Pattern =
Pattern(orKind: PatternKind.DLit, dlit: d)
proc `?`*(aa: sink AnyAtom): Pattern =
?DLit(value: aa)
proc toPattern(aa: sink AnyAtom): Pattern =
DLit(value: aa).toPattern
proc `?`*(d: sink DCompound): Pattern =
proc toPattern(d: sink DCompound): Pattern =
Pattern(orKind: PatternKind.DCompound, dcompound: d)
proc `?`*(d: sink DCompoundRec): Pattern =
?DCompound(orKind: DCompoundKind.rec, rec: d)
proc toPattern(d: sink DCompoundRec): Pattern =
DCompound(orKind: DCompoundKind.rec, rec: d).toPattern
proc `?`*(d: sink DCompoundArr): Pattern =
?DCompound(orKind: DCompoundKind.arr, arr: d)
proc toPattern(d: sink DCompoundArr): Pattern =
DCompound(orKind: DCompoundKind.arr, arr: d).toPattern
proc `?`*(d: sink DCompoundDict): Pattern =
?DCompound(orKind: DCompoundKind.dict, dict: d)
proc toPattern(d: sink DCompoundDict): Pattern =
DCompound(orKind: DCompoundKind.dict, dict: d).toPattern
proc `?`*(x: bool): Pattern =
?AnyAtom(orKind: AnyAtomKind.`bool`, bool: x)
proc drop*(): Pattern {.inline.} = Pattern(orKind: PatternKind.DDiscard)
## Create a pattern to match any value without capture.
proc `?`*(x: float32): Pattern =
?AnyAtom(orKind: AnyAtomKind.`float`, float: x)
proc grab*(): Pattern {.inline.} = DBind(pattern: drop()).toPattern
## Create a pattern to capture any value.
proc `?`*(x: float64): Pattern =
?AnyAtom(orKind: AnyAtomKind.`double`, double: x)
proc `?`*(x: int): Pattern =
?AnyAtom(orKind: AnyAtomKind.`int`, int: x)
proc `?`*(s: sink string): Pattern =
?AnyAtom(orKind: AnyAtomKind.`string`, string: s)
proc `?`*(x: sink seq[byte]): Pattern =
?AnyAtom(orKind: AnyAtomKind.`bytes`, bytes: x)
proc `?`*(x: sink Symbol): Pattern =
?AnyAtom(orKind: AnyAtomKind.`symbol`, symbol: x)
proc `?`*[T](pr: Preserve[T]): Pattern =
proc grab*[T](pr: Preserve[T]): 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>}> <_>]>"""
assert not pr.embedded
case pr.kind
of pkBoolean: ?pr.bool
of pkFloat: ?pr.float
of pkDouble: ?pr.double
of pkSignedInteger: ?(int pr.int) # TODO: overflow!
of pkString: ?pr.string
of pkByteString: ?pr.bytes
of pkSymbol: ?pr.symbol
of pkBoolean:
AnyAtom(orKind: AnyAtomKind.`bool`, bool: pr.bool).toPattern
of pkFloat:
AnyAtom(orKind: AnyAtomKind.`float`, float: pr.float).toPattern
of pkDouble:
AnyAtom(orKind: AnyAtomKind.`double`, double: pr.double).toPattern
of pkSignedInteger:
AnyAtom(orKind: AnyAtomKind.`int`, int: pr.int).toPattern
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:
?DCompoundRec(
label: cast[Preserve[Ref]](pr.label), # TODO: don't cast like this
fields: map[Preserve[T], Pattern](pr.fields, `?`))
if (pr.isRecord("_") and pr.arity == 0) or (pr.isRecord("bind") and pr.arity == 1):
drop()
else:
DCompoundRec(
label: cast[Preserve[Ref]](pr.label), # TODO: don't cast like this
fields: map[Preserve[T], Pattern](pr.fields, grab)).toPattern
of pkSequence:
?DCompoundArr(items: map(pr.sequence, `?`))
DCompoundArr(items: map(pr.sequence, grab)).toPattern
of pkSet: raise newException(
ValueError, "cannot construct a pattern over a set literal")
of pkDictionary:
var dict = DCompoundDict()
for key, val in pr.pairs: dict.entries[cast[Preserve[Ref]](key)] = ?val
?dict
for key, val in pr.pairs: dict.entries[cast[Preserve[Ref]](key)] = grab val
dict.toPattern
of pkEmbedded:
raiseAssert "cannot construct a pattern over a embedded literal"
# TODO: can patterns be constructed over embedded literals?
drop()
proc drop*(): Pattern = Pattern(orKind: PatternKind.DDiscard)
## Create a pattern to match any value without capture.
proc grab*(): Pattern = ?DBind(pattern: drop())
## Create a pattern to capture any value.
proc `?`*[A, B](table: TableRef[A,B]): Pattern =
raiseAssert "not implemented"
#[
proc `?`*(pat: sink Pattern): Pattern =
## Construct a `Pattern` that matches a `Pattern`.
case pat.orKind
of PatternKind.DDiscard: result = pat
of PatternKind.DBind: result = drop()
of PatternKind.DLit: result = ?pat.toPreserve(Ref)
of PatternKind.DCompound:
case pat.dcompound.orKind:
of DCompoundKind.rec:
var fields = move pat.dcompound.rec.fields
for f in fields.mitems: f = ?(move f)
result = ?pat.toPreserve(Ref)
# echo "need to stuff fields into ", result, " at ", result.dcompound.rec.fields[1].dcompound.arr.items
result.dcompound.rec.fields[1].dcompound.arr.items = fields
#[
of DCompoundKind.arr
pat.dcompound.arr
of DCompoundKind.dict
pat.dcompound.dict
]#
else: raiseAssert "`?` not implemented for pattern " & $pat
]#
#[
proc `?`*(patterns: openArray[Pattern]): Pattern =
raiseAssert "got it in the right place"
#[
result = DCompoundArr()
for e in val:
if i > arr.items.high: arr.items.setLen(succ i)
arr.items[i] = pat
for pat in arr.items.mitems:
if pat.isNil: pat = drop()
result = ?DCompound(
orKind: DCompoundKind.arr,
arr: arr)
]#
]#
proc `?`*[T](val: sink T): Pattern =
proc grab*[T](val: T): Pattern =
## Construct a `Pattern` from value of type `T`.
#[
when T is Pattern:
case val.orKind
of PatternKind.DDiscard, PatternKind.DBind: result = val
of PatternKind.DLit: result = ?val.toPreserve(Ref)
of PatternKind.DCompound:
case val.dcompound.orKind:
of DCompoundKind.rec:
var fields = move val.dcompound.rec.fields
for f in fields.mitems: f = ?(move f)
result = ?val.toPreserve(Ref)
# echo "need to stuff fields into ", result, " at ", result.dcompound.rec.fields[1].dcompound.arr.items
result.dcompound.rec.fields[1].dcompound.arr.items = fields
#[
of DCompoundKind.arr
val.dcompound.arr
of DCompoundKind.dict
val.dcompound.dict
]#
else: raiseAssert "`?` not implemented for pattern " & $val
]#
when T is Ref:
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
value: AnyAtom(
orKind: AnyAtomKind.embedded,
embedded: embed(val))))
elif T is ptr | ref:
if system.`==`(val, nil): result = ?(Symbol "null")
else: result = ?(val[])
elif T is bool:
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
value: AnyAtom(
orKind: AnyAtomKind.bool,
bool: val)))
elif T is float32:
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
value: AnyAtom(
orKind: AnyAtomKind.float,
float: val)))
elif T is float64:
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
value: AnyAtom(
orKind: AnyAtomKind.double,
double: val)))
elif T is SomeInteger:
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
value: AnyAtom(
orKind: AnyAtomKind.int,
int: AnyAtomInt val)))
elif T is string:
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
value: AnyAtom(
orKind: AnyAtomKind.string,
string: val)))
elif T is seq[byte]:
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
value: AnyAtom(
orKind: AnyAtomKind.bytes,
bytes: val)))
elif T is array | seq:
let arr = DCompoundArr(items: newSeq[Pattern](val.len))
for i, e in val.mitems: arr.items[i] = ?(move e)
result = ?arr
elif T is Symbol:
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
value: AnyAtom(
orKind: AnyAtomKind.symbol,
symbol: Symbol $val)))
elif T.hasPreservesRecordPragma:
var
label = T.recordLabel.tosymbol(Ref)
fields = newSeq[Pattern]()
for f in fields(val):
fields.add ?f
result = ?DCompound(
orKind: DCompoundKind.rec,
rec: DCompoundRec(
label: label, fields: fields))
else:
?(toPreserve(val, Ref))
runnableExamples:
import syndicate/protocols/simpleChatProtocol
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(Present(username: "Carol")) == """<rec Present [<lit "Carol">]>"""
grab (toPreserve(val, Ref))
proc `?`*(T: static typedesc): Pattern =
## Derive a `Pattern` from type `T`.
proc patternOfType(typ: static typedesc; `bind`: static bool): Pattern =
when typ is ref:
patternOfType(pointerBase(typ), `bind`)
elif typ.hasPreservesRecordPragma:
var rec = DCompoundRec(label: typ.recordLabel.tosymbol(Ref))
for _, f in fieldPairs(default typ):
add(rec.fields, patternOfType(typeof f, `bind`))
result = rec.toPattern
elif typ.hasPreservesDictionaryPragma:
var dict = DCompoundDict()
for key, val in fieldPairs(default typ):
dict.entries[toSymbol(key, Ref)] = patternOfType(typeof val, `bind`)
dict.toPattern
elif typ is tuple:
var arr = DCompoundArr()
for _, f in fieldPairs(default typ):
add(arr.items, patternOfType(typeof f, `bind`))
arr.toPattern
elif typ is array:
var arr = DCompoundArr()
arr.items.setLen(len(typ))
for e in arr.items.mitems: e = grab()
arr.toPattern
else:
if `bind`: grab()
else: drop()
proc grabType*(typ: static typedesc): Pattern =
## Derive a `Pattern` from type `typ`.
## This works for `tuple` and `object` types but in the
## general case will return a wildcard binding.
runnableExamples:
import preserves
from std/unittest import check
check:
$grabType(array[3, int]) ==
"""<arr [<bind <_>> <bind <_>> <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 <_>>]>"
$(grabType Rect) ==
"<rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>"
$(grabType ColoredRect) ==
"<dict {color: <bind <_>> rect: <rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>}>"
patternOfType(typ, true)
type Point = tuple[x: int; y: int]
assert $(?Point) == "<arr [<bind <_>> <bind <_>>]>"
type Rect {.preservesRecord: "rect".} = tuple[a: Point; B: Point]
assert $(?Rect) == "<rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>"
type ColoredRect {.preservesDictionary.} = tuple[color: string; rect: Rect]
assert $(?ColoredRect) == "<dict {color: <bind <_>>, rect: <rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>}>"
when T is Pattern:
raiseAssert "? for pattern"
elif T is ref:
?pointerBase(T)
elif T is array:
var arr = DCompoundArr(items: newSeq[Pattern](len(T)))
for p in arr.items.mitems: p = grab()
result = ?arr
elif T.hasPreservesRecordPragma:
var
label = T.recordLabel.tosymbol(Ref)
fields = newSeq[Pattern]()
for key, val in fieldPairs(default T):
when typeOf(val) is Pattern:
fields.add drop()
else:
fields.add ?(typeOf val)
result = ?DCompound(
orKind: DCompoundKind.rec,
rec: DCompoundRec(
label: label, fields: fields))
elif T.hasPreservesDictionaryPragma:
var dict = DCompoundDict()
for key, val in fieldPairs(default T):
dict.entries[key.toSymbol(Ref)] = ?(typeOf val)
?DCompound(
orKind: DCompoundKind.dict,
dict: dict)
elif T.hasPreservesTuplePragma or T is tuple:
raiseAssert "got a tuple"
var arr = DCompoundArr()
for key, val in fieldPairs(default T):
arr.items.add ?(typeOf val)
?DCompound(
orKind: DCompoundKind.arr,
arr: arr)
else:
grab() # otherwise an abritrary capture
proc dropType*(typ: static typedesc): Pattern =
## Derive a `Pattern` from type `typ` without any bindings.
patternOfType(typ, false)
proc fieldCount(T: typedesc): int =
for _, _ in fieldPairs(default T):
inc result
proc `?`*(T: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern =
## Construct a `Pattern` from type `T` that selectively captures fields.
proc match(bindings: sink openArray[(int, Pattern)]; i: int; pat: var Pattern): bool =
for (j, b) in bindings:
if i == j:
pat = b
return true
proc grab*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern =
## Construct a `Pattern` from type `typ` that selectively captures fields.
runnableExamples:
import preserves
type Point = tuple[x: int; y: int; z: int]
assert $(Point ? { 2: grab() }) == "<arr [<_> <_> <bind <_>>]>"
when T is ref:
`?`(pointerBase(T), bindings)
elif T.hasPreservesRecordPragma:
var
label = T.recordLabel.tosymbol(Ref)
fields = newSeq[Pattern](fieldCount T)
for (i, pat) in bindings:
fields[i] = pat
for pat in fields.mitems:
if pat.isNil: pat = drop()
result = ?DCompound(
orKind: DCompoundKind.rec,
rec: DCompoundRec(
label: label, fields: fields))
elif T is tuple:
from std/unittest import check
type
Point = tuple[x: int; y: int]
Rect {.preservesRecord: "rect".} = tuple[a: Point; B: Point]
ColoredRect {.preservesDictionary.} = tuple[color: string; rect: Rect]
check:
$grab(Point, { 1: grab() }) == "<arr [<_> <bind <_>>]>"
$grab(Rect, { 0: grab() }) == "<rec rect [<bind <_>> <arr [<_> <_>]>]>"
when typ is ref:
grab(pointerBase(typ), bindings)
elif typ.hasPreservesRecordPragma:
var rec = DCompoundRec(label: typ.recordLabel.tosymbol(Ref))
rec.fields.setLen(fieldCount typ)
var i: int
for _, f in fieldPairs(default typ):
if not match(bindings, i, rec.fields[i]):
rec.fields[i] = dropType(typeof f)
inc i
result = rec.toPattern
elif typ is tuple:
var arr = DCompoundArr()
for (i, pat) in bindings:
if i > arr.items.high: arr.items.setLen(succ i)
arr.items[i] = pat
for pat in arr.items.mitems:
if pat.isNil: pat = drop()
result = ?DCompound(
orKind: DCompoundKind.arr,
arr: arr)
arr.items.setLen(fieldCount typ)
var i: int
for _, f in fieldPairs(default typ):
if not match(bindings, i, arr.items[i]):
arr.items[i] = dropType(typeof f)
inc i
result = arr.toPattern
else:
raiseAssert("no preserves pragma on " & $T)
{.error: "grab with bindings not implemented for " & $typ.}
#[
proc `?`*(pat: sink Pattern; bindings: sink openArray[(int, Pattern)]): Pattern =
## Construct a `Pattern` from `pat` with overrides from `bindings`.
result = pat
assert not result.isNil
case result.orKind
of PatternKind.DDiscard, PatternKind.DBind: discard
of PatternKind.DCompound:
case result.dcompound.orKind
of `rec`:
echo "need to override record fields ", result.dcompound.rec.fields, " with ", bindings
var foo = result.dcompound.rec.fields[1] ? bindings
echo "recursing into fields returned ", foo
result.dcompound.rec.fields[1] = foo
of `arr`:
echo "need to override array items ", result.dcompound.arr.items, " with ", bindings
for i in result.dcompound.arr.items.mitems:
i = drop()
for (i, pat) in bindings:
result.dcompound.arr.items[i] = pat
else:
raiseAssert $result.dcompound.orKind
else:
raiseAssert $result.orKind
]#
proc `??`*(pat: sink Pattern; bindings: sink openArray[(int, Pattern)]): Pattern =
## Create a `Pattern` that matches or captures from a `Pattern` over `pat`.
proc grabLit*(): Pattern =
runnableExamples:
import preserves
import syndicate/protocols/simpleChatProtocol
from std/unittest import check
check:
$grabLit() == """<rec lit [<bind <_>>]>"""
grabType(dataspacePatterns.DLit[void])
type Point* {.preservesRecord: "point".} = object
x, y: int
assert $(?Point) == "<rec point [<bind <_>> <bind <_>>]>"
# Capture two values from a value of `Point`.
assert $(?Point ?? { 0: ?DLit }) == "<rec rec [<lit point> <arr [<rec lit [<bind <_>>]> <_>]>]>"
# Match a pattern of `Point` with a literal value set in the first field and capture that value.
assert $(?tuple[x: int, y: int] ?? { 1: ?DLit }) == "<rec arr [<arr [<_> <rec lit [<bind <_>>]>]>]>"
# Match a pattern over a tuple with a literal value set in the second field and capture that value.
case pat.orKind
of PatternKind.DCompound:
case pat.dcompound.orKind:
of DCompoundKind.rec:
let fieldsLen = pat.dcompound.rec.fields.len
pat.dcompound.rec.fields.setLen 0
result = ?pat
result.dcompound.rec.fields[1].dcompound.arr.items.setLen fieldsLen
for (i, p) in bindings:
result.dcompound.rec.fields[1].dcompound.arr.items[i] = p
for e in result.dcompound.rec.fields[1].dcompound.arr.items.mitems:
if e.isNil: e = drop()
of DCompoundKind.arr:
let itemsLen = pat.dcompound.arr.items.len
pat.dcompound.arr.items.setLen 0
result = ?pat
result.dcompound.rec.fields[0].dcompound.arr.items.setLen itemsLen
for (i, p) in bindings:
result.dcompound.rec.fields[0].dcompound.arr.items[i] = p
for e in result.dcompound.rec.fields[0].dcompound.arr.items.mitems:
if e.isNil: e = drop()
of DCompoundKind.dict:
let keys = pat.dcompound.dict.entries.keys.toSeq
clear pat.dcompound.dict.entries
result = ?pat
raiseAssert "?? not implemented for dictionaries"
else:
raiseAssert "cannot override " & $pat
proc inject*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern =
## Construct a `Pattern` from `pat` with injected overrides from `bindings`.
## Injects are made at offsets indexed by the discard (`<_>`) patterns in `pat`.
runnableExamples:
import syndicate/protocols/simpleChatProtocol
from std/unittest import check
check:
$inject(dropType(Says), {1: grabLit()}) == """<rec Says [<_> <rec lit [<bind <_>>]>]>"""
proc inject(pat: Pattern; bindings: openArray[(int, Pattern)]; offset: var int): Pattern =
case pat.orKind
of PatternKind.DDiscard:
var replaced = false
for (i, injection) in bindings:
if i == offset:
result = injection
replaced = true
break
if not replaced:
result = drop()
inc offset
of PatternKind.DBind, 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 recordPattern*(label: Preserve[Ref], fields: varargs[Pattern]): Pattern =
?DCompoundRec(label: label, fields: fields.toSeq)
runnableExamples:
from std/unittest import check
import syndicate/actors, preserves
check:
$recordPattern("Says".toSymbol(Ref), grab(), grab()) ==
"""<rec Says [<bind <_>> <bind <_>>]>"""
DCompoundRec(label: label, fields: fields.toSeq).toPattern
type
Value = Preserve[Ref]
@ -445,6 +309,17 @@ func projectPaths*(v: Value; paths: seq[Path]): seq[Value] =
if vv.isSome: result[i] = get(vv)
func matches*(pat: Pattern; pr: Value): bool =
runnableExamples:
import preserves
from syndicate/actors import Ref
from std/unittest import check, checkpoint
import syndicate/protocols/simpleChatProtocol
let pat = grabType(Says)
let val = parsePreserves("""<Says "Mike" "Hello world!">""", Ref)
check matches(pat, val)
let analysis = analyse(pat)
assert analysis.constPaths.len == analysis.constValues.len
for i, path in analysis.constPaths:
@ -456,6 +331,24 @@ func matches*(pat: Pattern; pr: Value): bool =
true
func capture*(pat: Pattern; pr: Value): seq[Value] =
runnableExamples:
import preserves
from syndicate/actors import Ref
from std/unittest import check, checkpoint
from syndicate/protocols/simpleChatProtocol import Says
from syndicate/protocols/dataspace import Observe
type Observe = dataspace.Observe[Ref]
let
pat = grab(Says, {0: grab("Mike"), 1: grab()})
obs = Observe(pattern: pat)
obsPat = inject(grab(Observe(pattern: grabType(Says))), {0: grabLit()})
obsVal = toPreserve(obs, Ref)
checkpoint "observer pattern: " & $obsPat
checkpoint "value: " & $obsVal
check capture(obsPat, obsVal) == @[toPreserve("Mike", Ref)]
let analysis = analyse(pat)
assert analysis.constPaths.len == analysis.constValues.len
for i, path in analysis.constPaths:
@ -467,11 +360,10 @@ func capture*(pat: Pattern; pr: Value): seq[Value] =
if v.isNone: return @[]
result.add(get v)
when isMainModule:
let txt = readAll stdin
if txt != "":
let
v = parsePreserves(txt)
pat = ?v
pat = grab v
stdout.writeLine(pat)

View File

@ -4,8 +4,11 @@
import std/[hashes, lists, options, sets, tables]
import preserves
import ./actors, ./bags, ./patterns
import ./protocols/dataspacePatterns
type
DCompound = dataspacePatterns.DCompound[Ref]
Pattern = dataspacePatterns.Pattern[Ref]
Value = Preserve[Ref]
Path = seq[Value]
ClassKind = enum classNone, classRecord, classSequence, classDictionary

View File

@ -1,6 +1,6 @@
# Package
version = "20221213"
version = "20230326"
author = "Emery Hemingway"
description = "Syndicated actors for conversational concurrency"
license = "Unlicense"

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: ☭ 2022 Emery Hemingway
# SPDX-FileCopyrightText: ☭ Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/asyncdispatch