From b1627a491a4bc21b0dbf00e5b1a68ca8e51ab793 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Thu, 23 Mar 2023 14:06:44 -0500 Subject: [PATCH] Patterns: refactor --- src/syndicate.nim | 23 +- src/syndicate/actors.nim | 8 +- src/syndicate/patterns.nim | 574 ++++++++++++------------------ src/syndicate/skeletons.nim | 3 + syndicate.nimble | 2 +- tests/test_simpleChatProtocol.nim | 2 +- 6 files changed, 261 insertions(+), 351 deletions(-) diff --git a/src/syndicate.nim b/src/syndicate.nim index 56237d4..2f51005 100644 --- a/src/syndicate.nim +++ b/src/syndicate.nim @@ -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.} diff --git a/src/syndicate/actors.nim b/src/syndicate/actors.nim index 28a3119..2217eed 100644 --- a/src/syndicate/actors.nim +++ b/src/syndicate/actors.nim @@ -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) diff --git a/src/syndicate/patterns.nim b/src/syndicate/patterns.nim index e06e0ec..1843b74 100644 --- a/src/syndicate/patterns.nim +++ b/src/syndicate/patterns.nim @@ -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""">""") == + """ ]> }> <_>]>""" + 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) == "" + $grab(3.14) == "" + $grab([0, 1, 2, 3]) == " ]>" + $grab(Present(username: "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]) == + """> > >]>""" + 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) == + "> >]>" + $(grabType Rect) == + "> >]> > >]>]>" + $(grabType ColoredRect) == + "> rect: > >]> > >]>]>}>" + patternOfType(typ, true) - type Point = tuple[x: int; y: int] - assert $(?Point) == "> >]>" - - type Rect {.preservesRecord: "rect".} = tuple[a: Point; B: Point] - assert $(?Rect) == "> >]> > >]>]>" - - type ColoredRect {.preservesDictionary.} = tuple[color: string; rect: Rect] - assert $(?ColoredRect) == ">, rect: > >]> > >]>]>}>" - 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() }) == " <_> >]>" - - 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() }) == " >]>" + $grab(Rect, { 0: grab() }) == "> <_>]>]>" + 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() == """>]>""" + grabType(dataspacePatterns.DLit[void]) - type Point* {.preservesRecord: "point".} = object - x, y: int - - assert $(?Point) == "> >]>" - # Capture two values from a value of `Point`. - - assert $(?Point ?? { 0: ?DLit }) == " >]> <_>]>]>" - # 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 }) == " >]>]>]>" - # 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()}) == """ >]>]>""" + 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()) == + """> >]>""" + 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("""""", 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) diff --git a/src/syndicate/skeletons.nim b/src/syndicate/skeletons.nim index e6170b3..c840e87 100644 --- a/src/syndicate/skeletons.nim +++ b/src/syndicate/skeletons.nim @@ -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 diff --git a/syndicate.nimble b/syndicate.nimble index 09bae00..6af8708 100644 --- a/syndicate.nimble +++ b/syndicate.nimble @@ -1,6 +1,6 @@ # Package -version = "20221213" +version = "20230326" author = "Emery Hemingway" description = "Syndicated actors for conversational concurrency" license = "Unlicense" diff --git a/tests/test_simpleChatProtocol.nim b/tests/test_simpleChatProtocol.nim index b78ceb3..02a47dc 100644 --- a/tests/test_simpleChatProtocol.nim +++ b/tests/test_simpleChatProtocol.nim @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: ☭ 2022 Emery Hemingway +# SPDX-FileCopyrightText: ☭ Emery Hemingway # SPDX-License-Identifier: Unlicense import std/asyncdispatch