diff --git a/src/syndicate.nim b/src/syndicate.nim index 4af3041..fee9b48 100644 --- a/src/syndicate.nim +++ b/src/syndicate.nim @@ -27,158 +27,16 @@ runnableExamples: import std/[macros, tables, typetraits] import preserves import ./syndicate/[actors, dataspaces, durings, patterns] +import ./syndicate/protocols/dataspace from ./syndicate/relays import connectStdio, connectUnix export Actor, Assertion, Facet, Handle, Ref, Symbol, Turn, TurnAction, - `$`, `?`, `??`, bootDataspace, connectStdio, connectUnix, drop, facet, + `$`, `?`, analyse, bootDataspace, connectStdio, connectUnix, drop, facet, future, grab, message, newDataspace, publish, retract, replace, run, stop, unembed -proc `?`*[T](val: T): Pattern = - ## Construct a `Pattern` from value of type `T`. - when T is Pattern: result = val - elif 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 enum or 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)) - -proc `?`*(T: static typedesc): Pattern = - ## Construct a `Pattern` from type `T`. - runnableExamples: - import preserves - - 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: > >]> > >]>]>}>" - - ## Derive a `Pattern` from type `T`. - ## This works for `tuple` and `object` types but in the - ## general case will return a wildcard binding. - when T is ref: - ?pointerBase(T) - elif T.hasPreservesRecordPragma: - var - label = T.recordLabel.tosymbol(Ref) - fields = newSeq[Pattern]() - for key, val in fieldPairs(default T): - 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: - 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 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. - 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: - 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) - else: - {.error: "no preserves pragma on " & $T.} - type + Observe* = dataspace.Observe[Ref] PublishProc = proc (turn: var Turn; v: Assertion; h: Handle) {.closure.} RetractProc = proc (turn: var Turn; h: Handle) {.closure.} MessageProc = proc (turn: var Turn; v: Assertion) {.closure.} diff --git a/src/syndicate/patterns.nim b/src/syndicate/patterns.nim index 7d8cf66..62ec271 100644 --- a/src/syndicate/patterns.nim +++ b/src/syndicate/patterns.nim @@ -7,7 +7,7 @@ import preserves import ./protocols/dataspacePatterns from ./actors import Ref -export dataspacePatterns.`$`, PatternKind, DCompoundKind +export dataspacePatterns.`$`, PatternKind, DCompoundKind, AnyAtomKind type Assertion = Preserve[Ref] @@ -63,6 +63,7 @@ proc `?`*(x: sink Symbol): Pattern = ?AnyAtom(orKind: AnyAtomKind.`symbol`, symbol: x) proc `?`*[T](pr: Preserve[T]): Pattern = + ## Convert a `Preserve` value to a `Pattern`. assert not pr.embedded case pr.kind of pkBoolean: ?pr.bool @@ -75,9 +76,9 @@ proc `?`*[T](pr: Preserve[T]): Pattern = of pkRecord: ?DCompoundRec( label: pr.label, - fields: map[Preserve[T], Pattern](pr.fields, `?`[T])) + fields: map[Preserve[T], Pattern](pr.fields, `?`)) of pkSequence: - ?DCompoundArr(items: map(pr.sequence, `?`[T])) + ?DCompoundArr(items: map(pr.sequence, `?`)) of pkSet: raise newException( ValueError, "cannot construct a pattern over a set literal") of pkDictionary: @@ -87,38 +88,311 @@ proc `?`*[T](pr: Preserve[T]): Pattern = of pkEmbedded: raiseAssert "cannot construct a pattern over a embedded literal" -proc `??`*(pat: Pattern): Pattern = - ## Construct a `Pattern` that matches a `Pattern`. - case pat.orKind - of PatternKind.DDiscard, PatternKind.DBind: - result = pat - of PatternKind.DLit: - result = ?(pat.toPreserve(Ref)) - of PatternKind.DCompound: - case pat.dcompound.orKind - of DCompoundKind.rec: - var fields = move pat.dcompound.rec.fields - result = ?(pat.toPreserve(Ref)) - result.dcompound.rec.fields[1].dcompound.arr.items = fields - of DCompoundKind.arr: - var items = move pat.dcompound.arr.items - result = ?(pat.toPreserve(Ref)) - result.dcompound.rec.fields[0].dcompound.arr.items = items - of DCompoundKind.dict: - # var entries = move pat.dcompound.dict.entries - # result = ?(pat.toPreserve(Ref)) - stderr.writeLine "pattern construction from DCompoundKind not implemented" - raiseAssert "not implemented" - proc drop*(): Pattern = Pattern(orKind: PatternKind.DDiscard) -proc grab*(): Pattern = ?DBind(pattern: drop()) + ## Create a pattern to match any value without capture. -proc `?_`*(): Pattern = drop() -proc `?*`*(): Pattern = grab() +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 = + ## 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)) + +proc `?`*(T: static typedesc): Pattern = + ## Derive a `Pattern` from type `T`. + ## This works for `tuple` and `object` types but in the + ## general case will return a wildcard binding. + runnableExamples: + import preserves + + 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 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. + 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: + 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) + else: + raiseAssert("no preserves pragma on " & $T) + +#[ +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`. + runnableExamples: + import preserves + + 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 + echo "override ", result.dcompound.rec.fields[0].dcompound.arr.items + 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 + + else: + raiseAssert "cannot override " & $pat + proc recordPattern*(label: Preserve[Ref], fields: varargs[Pattern]): Pattern = ?DCompoundRec(label: label, fields: fields.toSeq)