├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── go.mod ├── go.sum └── rete ├── alpha.go ├── beta_memory.go ├── beta_memory_test.go ├── common.go ├── consts.go ├── eval.go ├── eval_test.go ├── filter_node.go ├── join_node.go ├── ncc_node.go ├── ncc_partner_node.go ├── negative_node.go ├── network.go ├── network_test.go ├── token.go ├── token_test.go ├── utils.go └── wme.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | .idea/ 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - master 5 | 6 | script: go test -v ./... 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # naive-rete-go 2 | [![Build Status](https://travis-ci.org/GNaive/naive-rete-go.svg?branch=master)](https://travis-ci.org/GNaive/naive-rete-go) 3 | golang RETE algorithm implement 4 | 5 | ### see also 6 | - [naive-rete for Python](https://github.com/GNaive/naive-rete) 7 | 8 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module GNaive/naive-rete-go 2 | 3 | go 1.13 4 | 5 | require github.com/beevik/etree v1.1.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= 2 | github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= 3 | -------------------------------------------------------------------------------- /rete/alpha.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import ( 4 | "container/list" 5 | ) 6 | 7 | type AlphaMemory struct { 8 | items *list.List 9 | successors *list.List 10 | } 11 | 12 | type ConstantTestNode struct { 13 | fieldToTest int 14 | fieldMustEqual string 15 | outputMemory *AlphaMemory 16 | children *list.List 17 | } 18 | 19 | func (node ConstantTestNode) activation(w *WME) { 20 | if node.fieldToTest != NoTest { 21 | if w.fields[node.fieldToTest] != node.fieldMustEqual { 22 | return 23 | } 24 | } 25 | if node.outputMemory != nil { 26 | node.outputMemory.activation(w) 27 | } 28 | for e := node.children.Front(); e != nil; e = e.Next() { 29 | e.Value.(*ConstantTestNode).activation(w) 30 | } 31 | } 32 | 33 | func (node *AlphaMemory) activation(w *WME) { 34 | node.items.PushBack(w) 35 | w.alphaMems.PushBack(node) 36 | for e := node.successors.Front(); e != nil; e = e.Next() { 37 | e.Value.(IReteNode).RightActivation(w) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rete/beta_memory.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import ( 4 | "container/list" 5 | ) 6 | 7 | type BetaMemory struct { 8 | items *list.List 9 | parent IReteNode 10 | children *list.List 11 | RHS *RHS 12 | } 13 | 14 | func (node BetaMemory) GetNodeType() string { 15 | return BetaMemoryNodeTy 16 | } 17 | 18 | func (node BetaMemory) GetItems() *list.List { 19 | return node.items 20 | } 21 | 22 | func (node BetaMemory) GetParent() IReteNode { 23 | return node.parent 24 | } 25 | 26 | func (node BetaMemory) GetChildren() *list.List { 27 | return node.children 28 | } 29 | 30 | func (node *BetaMemory) LeftActivation(t *Token, w *WME, b Env) { 31 | newToken := makeToken(node, t, w, b) 32 | node.items.PushBack(newToken) 33 | for e := node.children.Front(); e != nil; e = e.Next() { 34 | e.Value.(IReteNode).LeftActivation(newToken, nil, nil) 35 | } 36 | } 37 | 38 | func (node BetaMemory) RightActivation(w *WME) { 39 | } 40 | 41 | func (node BetaMemory) GetExecuteParam(s string) interface{} { 42 | if node.RHS == nil { 43 | return nil 44 | } 45 | return node.RHS.Extra[s] 46 | } 47 | 48 | func (node BetaMemory) PopToken() *Token { 49 | e := node.items.Front() 50 | if e == nil { 51 | return nil 52 | } 53 | node.items.Remove(e) 54 | return e.Value.(*Token) 55 | } 56 | -------------------------------------------------------------------------------- /rete/beta_memory_test.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import ( 4 | "container/list" 5 | "testing" 6 | ) 7 | 8 | func Test_beta_memory_items(t *testing.T) { 9 | bm := BetaMemory{ 10 | items: list.New(), 11 | } 12 | newToken := &Token{} 13 | bm.GetItems().PushBack(newToken) 14 | e := bm.GetItems().Front() 15 | token := e.Value.(*Token) 16 | if token != newToken { 17 | t.Error("token error") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rete/common.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | func isVar(v string) bool { 4 | return len(v) > 0 && v[0] == '$' 5 | } 6 | 7 | func varKey(v string) string { 8 | if isVar(v) { 9 | return v[1:] 10 | } 11 | return "" 12 | } 13 | 14 | type Production struct { 15 | lhs LHS 16 | rhs RHS 17 | } 18 | 19 | type LHS struct { 20 | items []interface{} 21 | negative bool 22 | } 23 | 24 | type RHS struct { 25 | tmpl string 26 | Extra map[string]interface{} 27 | } 28 | 29 | type Has struct { 30 | fields [4]string 31 | negative bool 32 | } 33 | 34 | type Filter struct { 35 | tmpl string 36 | } 37 | 38 | func (has Has) contain(s string) int { 39 | for idx, v := range has.fields { 40 | if v == s { 41 | return idx 42 | } 43 | } 44 | return -1 45 | } 46 | 47 | func (has Has) testWme(w *WME) bool { 48 | for idx, v := range has.fields { 49 | if isVar(v) { 50 | continue 51 | } 52 | if v != w.fields[idx] { 53 | return false 54 | } 55 | } 56 | return true 57 | } 58 | 59 | func NewHas(className, id, attr, value string) Has { 60 | return Has{ 61 | fields: [4]string{className, id, attr, value}, 62 | negative: false, 63 | } 64 | } 65 | 66 | func NewNeg(className, id, attr, value string) Has { 67 | return Has{ 68 | fields: [4]string{className, id, attr, value}, 69 | negative: true, 70 | } 71 | } 72 | 73 | func NewLHS(items ...interface{}) LHS { 74 | return LHS{ 75 | items: items, 76 | } 77 | } 78 | 79 | func NewRHS() RHS { 80 | return RHS{ 81 | tmpl: "", 82 | Extra: make(map[string]interface{}), 83 | } 84 | } 85 | 86 | func NewNccRule(items ...interface{}) LHS { 87 | return LHS{ 88 | items: items, 89 | negative: true, 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /rete/consts.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | const ( 4 | ClassName = iota 5 | Identifier 6 | Attribute 7 | Value 8 | NoTest 9 | ) 10 | 11 | const ( 12 | BetaMemoryNodeTy = "beta_memory" 13 | JoinNodeTy = "join_node" 14 | NegativeNodeTy = "negative_node" 15 | NccNodeTy = "ncc_node" 16 | NccPartnerNodeTy = "ncc_parter_node" 17 | FilterNodeTy = "filter_node" 18 | ) 19 | 20 | var FIELDS = []int{ClassName, Identifier, Attribute, Value} 21 | -------------------------------------------------------------------------------- /rete/eval.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "go/ast" 7 | "go/parser" 8 | "go/token" 9 | "reflect" 10 | "runtime/debug" 11 | "strconv" 12 | ) 13 | 14 | func EvalFromString(s string, env Env) (result []reflect.Value, err error) { 15 | exp, err := parser.ParseExpr(s) 16 | if err != nil { 17 | return 18 | } 19 | return Eval(exp, env) 20 | } 21 | 22 | func Eval(exp ast.Expr, env Env) (result []reflect.Value, err error) { 23 | defer func() { 24 | if e := recover(); e != nil { 25 | err = errors.New(fmt.Sprintf("%s %s", e, debug.Stack())) 26 | } 27 | }() 28 | switch exp := exp.(type) { 29 | case *ast.BinaryExpr: 30 | return EvalBinaryExpr(exp, env) 31 | case *ast.BasicLit: 32 | var r interface{} 33 | switch exp.Kind { 34 | case token.INT, token.FLOAT: 35 | r, err = strconv.ParseFloat(exp.Value, 64) 36 | case token.STRING: 37 | r, err = strconv.Unquote(exp.Value) 38 | } 39 | if err != nil { 40 | return 41 | } 42 | result = append(result, reflect.ValueOf(r)) 43 | return result, nil 44 | case *ast.UnaryExpr: 45 | switch exp.Op { 46 | case token.ADD: 47 | return Eval(exp.X, env) 48 | case token.SUB: 49 | result, err = Eval(exp.X, env) 50 | if err != nil { 51 | return 52 | } 53 | val := -result[0].Float() 54 | result = []reflect.Value{reflect.ValueOf(val)} 55 | return 56 | } 57 | case *ast.ParenExpr: 58 | return Eval(exp.X, env) 59 | case *ast.Ident: 60 | return EvalIdent(exp, env) 61 | case *ast.CallExpr: 62 | return EvalCall(exp, env) 63 | } 64 | 65 | return 66 | } 67 | 68 | func EvalCall(exp *ast.CallExpr, env Env) (result []reflect.Value, err error) { 69 | f_val, err := Eval(exp.Fun, env) 70 | if err != nil { 71 | return 72 | } 73 | var args_val []reflect.Value 74 | for _, arg := range exp.Args { 75 | arg_val, err := Eval(arg, env) 76 | if err != nil { 77 | return result, err 78 | } 79 | args_val = append(args_val, arg_val[0]) 80 | } 81 | result = f_val[0].Call(args_val) 82 | return 83 | } 84 | 85 | func EvalIdent(exp *ast.Ident, env Env) (result []reflect.Value, err error) { 86 | v := env[exp.Name] 87 | if v == nil { 88 | err = errors.New(fmt.Sprintf("Ident `%s` undefined", exp)) 89 | } else { 90 | result = append(result, reflect.ValueOf(v)) 91 | } 92 | return 93 | } 94 | 95 | func EvalBinaryExpr(exp *ast.BinaryExpr, env map[string]interface{}) (result []reflect.Value, err error) { 96 | leftResult, err := Eval(exp.X, env) 97 | if err != nil { 98 | return 99 | } 100 | rightResult, err := Eval(exp.Y, env) 101 | if err != nil { 102 | return 103 | } 104 | var r interface{} 105 | left := value2float(leftResult[0]) 106 | right := value2float(rightResult[0]) 107 | 108 | switch exp.Op { 109 | case token.GTR: 110 | r = left > right 111 | case token.LSS: 112 | r = left < right 113 | case token.GEQ: 114 | r = left >= right 115 | case token.LEQ: 116 | r = left <= right 117 | case token.EQL: 118 | r = left == right 119 | case token.ADD: 120 | r = left + right 121 | case token.SUB: 122 | r = left - right 123 | case token.MUL: 124 | r = left * right 125 | case token.QUO: 126 | r = left / right 127 | default: 128 | err = errors.New(fmt.Sprintf("OP `%s` undefined", exp.Op)) 129 | return 130 | } 131 | result = append(result, reflect.ValueOf(r)) 132 | return result, nil 133 | } 134 | 135 | func value2float(v reflect.Value) float64 { 136 | switch v.Kind() { 137 | case reflect.Float64: 138 | return v.Float() 139 | case reflect.String: 140 | r, _ := strconv.ParseFloat(v.String(), 64) 141 | return r 142 | } 143 | return v.Float() 144 | } 145 | -------------------------------------------------------------------------------- /rete/eval_test.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func F(a string, b string) map[string]interface{} { 9 | result := make(map[string]interface{}) 10 | result["a"] = a 11 | result["b"] = b 12 | return result 13 | } 14 | 15 | func TestEvalFromString(t *testing.T) { 16 | env := make(map[string]interface{}) 17 | env["F"] = F 18 | result, err := EvalFromString(`F("hello", "world")`, env) 19 | if err != nil { 20 | t.Error(err) 21 | } else { 22 | fmt.Println("eval result:", result[0]) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /rete/filter_node.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import ( 4 | "container/list" 5 | ) 6 | 7 | type FilterNode struct { 8 | parent IReteNode 9 | children *list.List 10 | tmpl string 11 | } 12 | 13 | func (node FilterNode) GetNodeType() string { 14 | return FilterNodeTy 15 | } 16 | func (node FilterNode) GetItems() *list.List { 17 | return nil 18 | } 19 | func (node FilterNode) GetParent() IReteNode { 20 | return node.parent 21 | } 22 | func (node FilterNode) GetChildren() *list.List { 23 | return node.children 24 | } 25 | func (node *FilterNode) RightActivation(w *WME) { 26 | } 27 | func (node *FilterNode) LeftActivation(t *Token, w *WME, b Env) { 28 | all_binding := t.AllBinding() 29 | for k, v := range b { 30 | all_binding[k] = v 31 | } 32 | result, err := EvalFromString(node.tmpl, all_binding) 33 | if err != nil || len(result) == 0 { 34 | return 35 | } 36 | if !result[0].Bool() { 37 | return 38 | } 39 | for e := node.children.Front(); e != nil; e = e.Next() { 40 | child := e.Value.(IReteNode) 41 | child.LeftActivation(t, w, b) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /rete/join_node.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import "container/list" 4 | 5 | type TestAtJoinNode struct { 6 | fieldOfArg1 int 7 | conditionNumberOfArg2 int 8 | fieldOfArg2 int 9 | } 10 | 11 | type JoinNode struct { 12 | parent IReteNode 13 | children *list.List 14 | amem *AlphaMemory 15 | tests *list.List 16 | has *Has 17 | } 18 | 19 | func (node JoinNode) GetNodeType() string { 20 | return JoinNodeTy 21 | } 22 | func (node JoinNode) GetItems() *list.List { 23 | return nil 24 | } 25 | func (node JoinNode) GetParent() IReteNode { 26 | return node.parent 27 | } 28 | func (node JoinNode) GetChildren() *list.List { 29 | return node.children 30 | } 31 | func (node *JoinNode) RightActivation(w *WME) { 32 | parent := node.parent 33 | // dummy join 34 | if parent.GetParent().GetNodeType() == BetaMemoryNodeTy { 35 | b := node.makeBinding(w) 36 | for _e := node.children.Front(); _e != nil; _e = _e.Next() { 37 | child := _e.Value.(IReteNode) 38 | child.LeftActivation(nil, w, b) 39 | } 40 | return 41 | } 42 | for e := parent.GetItems().Front(); e != nil; e = e.Next() { 43 | t := e.Value.(*Token) 44 | if node.performJoinTests(t, w) { 45 | b := node.makeBinding(w) 46 | for _e := node.children.Front(); _e != nil; _e = _e.Next() { 47 | child := _e.Value.(IReteNode) 48 | child.LeftActivation(t, w, b) 49 | } 50 | } 51 | } 52 | } 53 | func (node *JoinNode) LeftActivation(t *Token, w *WME, b Env) { 54 | for e := node.amem.items.Front(); e != nil; e = e.Next() { 55 | w := e.Value.(*WME) 56 | if node.performJoinTests(t, w) { 57 | b := node.makeBinding(w) 58 | for _e := node.children.Front(); _e != nil; _e = _e.Next() { 59 | child := _e.Value.(IReteNode) 60 | child.LeftActivation(t, w, b) 61 | } 62 | } 63 | } 64 | } 65 | func (node *JoinNode) performJoinTests(t *Token, w *WME) bool { 66 | for e := node.tests.Front(); e != nil; e = e.Next() { 67 | test := e.Value.(*TestAtJoinNode) 68 | arg1 := w.fields[test.fieldOfArg1] 69 | wme2 := t.get_wmes()[test.conditionNumberOfArg2] 70 | arg2 := wme2.fields[test.fieldOfArg2] 71 | if arg1 != arg2 { 72 | return false 73 | } 74 | } 75 | return true 76 | } 77 | func (node *JoinNode) makeBinding(w *WME) Env { 78 | b := make(Env) 79 | for idx, v := range node.has.fields { 80 | if isVar(v) { 81 | b[varKey(v)] = w.fields[idx] 82 | } 83 | } 84 | return b 85 | } 86 | -------------------------------------------------------------------------------- /rete/ncc_node.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import "container/list" 4 | 5 | type NccNode struct { 6 | parent IReteNode 7 | children *list.List 8 | items *list.List 9 | partner *NccPartnerNode 10 | } 11 | 12 | func (node *NccNode) GetNodeType() string { 13 | return NccNodeTy 14 | } 15 | func (node *NccNode) GetParent() IReteNode { 16 | return node.parent 17 | } 18 | func (node *NccNode) GetItems() *list.List { 19 | return node.items 20 | } 21 | func (node *NccNode) GetChildren() *list.List { 22 | return node.children 23 | } 24 | func (node *NccNode) LeftActivation(t *Token, w *WME, b Env) { 25 | newToken := makeToken(node, t, w, b) 26 | node.items.PushBack(newToken) 27 | 28 | newToken.nccResults = list.New() 29 | buffer := node.partner.newResultBuffer 30 | for e := buffer.Front(); e != nil; e = e.Next() { 31 | result := e.Value.(*Token) 32 | result.owner = newToken 33 | newToken.nccResults.PushBack(result) 34 | buffer.Remove(e) 35 | } 36 | if newToken.nccResults.Len() > 0 { 37 | return 38 | } 39 | for e := node.children.Front(); e != nil; e = e.Next() { 40 | e.Value.(IReteNode).LeftActivation(newToken, nil, nil) 41 | } 42 | } 43 | func (node NccNode) RightActivation(w *WME) { 44 | } 45 | -------------------------------------------------------------------------------- /rete/ncc_partner_node.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import "container/list" 4 | 5 | type NccPartnerNode struct { 6 | parent IReteNode 7 | children *list.List 8 | nccNode IReteNode 9 | numberOfConjuncts int 10 | newResultBuffer *list.List 11 | } 12 | 13 | func (node NccPartnerNode) GetNodeType() string { 14 | return NccPartnerNodeTy 15 | } 16 | func (node NccPartnerNode) GetParent() IReteNode { 17 | return node.parent 18 | } 19 | func (node NccPartnerNode) GetItems() *list.List { 20 | return nil 21 | } 22 | func (node NccPartnerNode) GetChildren() *list.List { 23 | return node.children 24 | } 25 | func (node NccPartnerNode) LeftActivation(t *Token, w *WME, b Env) { 26 | nccNode := node.nccNode 27 | newResult := makeToken(node, t, w, b) 28 | ownersT := t 29 | ownersW := w 30 | for i := 1; i <= node.numberOfConjuncts; i++ { 31 | ownersW = ownersT.wme 32 | ownersT = ownersT.parent 33 | } 34 | for e := nccNode.GetItems().Front(); e != nil; e = e.Next() { 35 | item := e.Value.(*Token) 36 | if item.parent == ownersT && item.wme == ownersW { 37 | item.nccResults.PushBack(item) 38 | newResult.owner = item 39 | item.deleteTokenAndDescendents() 40 | return 41 | } 42 | } 43 | node.newResultBuffer.PushBack(newResult) 44 | } 45 | func (node NccPartnerNode) RightActivation(w *WME) { 46 | } 47 | -------------------------------------------------------------------------------- /rete/negative_node.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import ( 4 | "container/list" 5 | ) 6 | 7 | type NegativeJoinResult struct { 8 | owner *Token 9 | wme *WME 10 | } 11 | type NegativeNode struct { 12 | parent IReteNode 13 | children *list.List 14 | items *list.List 15 | amem *AlphaMemory 16 | tests *list.List 17 | } 18 | 19 | func (node NegativeNode) GetNodeType() string { 20 | return NegativeNodeTy 21 | } 22 | func (node NegativeNode) GetParent() IReteNode { 23 | return node.parent 24 | } 25 | func (node NegativeNode) GetItems() *list.List { 26 | return node.items 27 | } 28 | func (node *NegativeNode) GetChildren() *list.List { 29 | return node.children 30 | } 31 | func (node *NegativeNode) LeftActivation(t *Token, w *WME, b Env) { 32 | newToken := makeToken(node, t, w, b) 33 | node.items.PushBack(newToken) 34 | 35 | newToken.joinResults = list.New() 36 | for e := node.amem.items.Front(); e != nil; e = e.Next() { 37 | w := e.Value.(*WME) 38 | if node.perform_join_tests(newToken, w) { 39 | jr := &NegativeJoinResult{ 40 | owner: newToken, 41 | wme: w, 42 | } 43 | newToken.joinResults.PushBack(jr) 44 | w.negativeJoinResults = list.New() 45 | w.negativeJoinResults.PushBack(jr) 46 | } 47 | } 48 | if newToken.joinResults.Len() == 0 { 49 | for e := node.children.Front(); e != nil; e = e.Next() { 50 | child := e.Value.(IReteNode) 51 | child.LeftActivation(newToken, nil, nil) 52 | } 53 | } 54 | } 55 | func (node *NegativeNode) RightActivation(w *WME) { 56 | for e := node.items.Front(); e != nil; e = e.Next() { 57 | t := e.Value.(*Token) 58 | if node.perform_join_tests(t, w) { 59 | if t.joinResults.Len() == 0 { 60 | t.deleteTokenAndDescendents() 61 | } 62 | jr := &NegativeJoinResult{ 63 | owner: t, 64 | wme: w, 65 | } 66 | t.joinResults.PushBack(jr) 67 | w.negativeJoinResults = list.New() 68 | w.negativeJoinResults.PushBack(jr) 69 | } 70 | } 71 | } 72 | func (node *NegativeNode) perform_join_tests(t *Token, w *WME) bool { 73 | for e := node.tests.Front(); e != nil; e = e.Next() { 74 | test := e.Value.(*TestAtJoinNode) 75 | arg1 := w.fields[test.fieldOfArg1] 76 | wme2 := t.get_wmes()[test.conditionNumberOfArg2] 77 | arg2 := wme2.fields[test.fieldOfArg2] 78 | if arg1 != arg2 { 79 | return false 80 | } 81 | } 82 | return true 83 | } 84 | -------------------------------------------------------------------------------- /rete/network.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import ( 4 | "bytes" 5 | "container/list" 6 | "log" 7 | "runtime/debug" 8 | ) 9 | 10 | type IReteNode interface { 11 | GetNodeType() string 12 | GetItems() *list.List 13 | GetParent() IReteNode 14 | GetChildren() *list.List 15 | LeftActivation(t *Token, w *WME, b Env) 16 | RightActivation(w *WME) 17 | } 18 | 19 | type Network struct { 20 | alphaRoot *ConstantTestNode 21 | betaRoot IReteNode 22 | objects Env // for rhs result 23 | PNodes []*BetaMemory 24 | halt bool 25 | LogBuf *bytes.Buffer 26 | } 27 | 28 | func NewNetwork() *Network { 29 | workMemory := &AlphaMemory{ 30 | items: list.New(), 31 | successors: list.New(), 32 | } 33 | alphaRoot := &ConstantTestNode{ 34 | fieldToTest: NoTest, 35 | fieldMustEqual: "", 36 | outputMemory: workMemory, 37 | children: list.New(), 38 | } 39 | betaRoot := &BetaMemory{ 40 | items: list.New(), 41 | parent: nil, 42 | children: list.New(), 43 | } 44 | return &Network{ 45 | alphaRoot: alphaRoot, 46 | betaRoot: betaRoot, 47 | objects: make(Env), 48 | PNodes: []*BetaMemory{}, 49 | halt: false, 50 | LogBuf: &bytes.Buffer{}, 51 | } 52 | } 53 | 54 | func (n *Network) AddObject(key string, obj interface{}) { 55 | n.objects[key] = obj 56 | } 57 | 58 | func (n Network) GetObjects() Env { 59 | return n.objects 60 | } 61 | 62 | func (n Network) GetObject(key string) interface{} { 63 | return n.objects[key] 64 | } 65 | 66 | func (n *Network) Halt() { 67 | n.halt = true 68 | } 69 | 70 | func (n *Network) ExecuteRules(env Env) (err error) { 71 | for _, pNode := range n.PNodes { 72 | for elem := pNode.GetItems().Front(); elem != nil; elem = elem.Next() { 73 | token := elem.Value.(*Token) 74 | if pNode.RHS == nil || len(pNode.RHS.tmpl) == 0 { 75 | continue 76 | } 77 | handler := env[pNode.RHS.tmpl] 78 | if handler == nil { 79 | continue 80 | } 81 | func() { 82 | defer func() { 83 | l := log.New(n.LogBuf, "RHS `"+pNode.RHS.tmpl+"` ", log.Lshortfile) 84 | if e := recover(); e != nil { 85 | l.Printf("%s %s", e, debug.Stack()) 86 | } 87 | 88 | }() 89 | handler.(func(network *Network, token *Token))( 90 | n, token, 91 | ) 92 | }() 93 | if n.halt { 94 | return nil 95 | } 96 | } 97 | } 98 | return nil 99 | } 100 | 101 | func (n *Network) AddProduction(lhs LHS, rhs RHS) *BetaMemory { 102 | currentNode := n.buildOrShareNetworkForConditions(n.betaRoot, lhs, LHS{}) 103 | node := n.buildOrShareBetaMemory(currentNode) 104 | memory := node.(*BetaMemory) 105 | memory.RHS = &rhs 106 | n.PNodes = append(n.PNodes, memory) 107 | return memory 108 | } 109 | 110 | func (n *Network) AddProductionFromXML(s string) (result []*BetaMemory, err error) { 111 | ps, err := FromXML(s) 112 | if err != nil { 113 | return result, err 114 | } 115 | for _, p := range ps { 116 | result = append(result, n.AddProduction(p.lhs, p.rhs)) 117 | } 118 | return result, nil 119 | } 120 | 121 | func (n *Network) AddWME(w *WME) { 122 | n.alphaRoot.activation(w) 123 | } 124 | 125 | func (n Network) buildOrShareNetworkForConditions( 126 | parent IReteNode, rule LHS, earlierConds LHS) IReteNode { 127 | currentNode := parent 128 | condsHigherUp := earlierConds 129 | for _, cond := range rule.items { 130 | switch cond.(type) { 131 | case Has: 132 | cond := cond.(Has) 133 | if !cond.negative { 134 | currentNode = n.buildOrShareBetaMemory(currentNode) 135 | tests := n.getJoinTestsFromCondition(cond, condsHigherUp) 136 | am := n.buildOrShareAlphaMemory(cond) 137 | currentNode = n.buildOrShareJoinNode(currentNode, am, tests, &cond) 138 | } else { 139 | tests := n.getJoinTestsFromCondition(cond, condsHigherUp) 140 | am := n.buildOrShareAlphaMemory(cond) 141 | currentNode = n.buildOrShareNegativeNode(currentNode, am, tests) 142 | } 143 | case Filter: 144 | cond := cond.(Filter) 145 | currentNode = n.buildOrShareFilterNode(currentNode, cond) 146 | case LHS: 147 | cond := cond.(LHS) 148 | if cond.negative { 149 | currentNode = n.buildOrShareNccNodes(currentNode, cond, condsHigherUp) 150 | } 151 | } 152 | condsHigherUp.items = append(condsHigherUp.items, cond) 153 | } 154 | return currentNode 155 | } 156 | 157 | func (n Network) buildOrShareFilterNode(parent IReteNode, f Filter) IReteNode { 158 | for e := parent.GetChildren().Front(); e != nil; e = e.Next() { 159 | child := e.Value.(IReteNode) 160 | if child.GetNodeType() == FilterNodeTy { 161 | child := child.(*FilterNode) 162 | if child.tmpl == f.tmpl { 163 | return child 164 | } 165 | } 166 | } 167 | filter_node := &FilterNode{ 168 | parent: parent, 169 | children: list.New(), 170 | tmpl: f.tmpl, 171 | } 172 | parent.GetChildren().PushBack(filter_node) 173 | return filter_node 174 | } 175 | 176 | func (n Network) buildOrShareNccNodes(parent IReteNode, ncc LHS, earlier LHS) IReteNode { 177 | bottomOfSubnetwork := n.buildOrShareNetworkForConditions(parent, ncc, earlier) 178 | for e := parent.GetChildren().Front(); e != nil; e = e.Next() { 179 | child := e.Value.(IReteNode) 180 | if child.GetNodeType() == NccNodeTy { 181 | child := child.(*NccNode) 182 | if child.partner.parent == bottomOfSubnetwork { 183 | return child 184 | } 185 | } 186 | } 187 | nccNode := &NccNode{ 188 | parent: parent, 189 | children: list.New(), 190 | items: list.New(), 191 | } 192 | nccPartnerNode := &NccPartnerNode{ 193 | parent: bottomOfSubnetwork, 194 | children: list.New(), 195 | newResultBuffer: list.New(), 196 | numberOfConjuncts: len(ncc.items), 197 | nccNode: nccNode, 198 | } 199 | nccNode.partner = nccPartnerNode 200 | parent.GetChildren().PushBack(nccNode) 201 | bottomOfSubnetwork.GetChildren().PushBack(nccPartnerNode) 202 | n.updateNewNodeWithMatchesAbove(nccNode) 203 | n.updateNewNodeWithMatchesAbove(nccPartnerNode) 204 | return nccNode 205 | } 206 | 207 | func (n Network) buildOrShareBetaMemory(parent IReteNode) IReteNode { 208 | for e := parent.GetChildren().Front(); e != nil; e = e.Next() { 209 | if e.Value.(IReteNode).GetNodeType() == BetaMemoryNodeTy { 210 | return e.Value.(IReteNode) 211 | } 212 | } 213 | node := &BetaMemory{ 214 | items: list.New(), 215 | parent: parent, 216 | children: list.New(), 217 | } 218 | parent.GetChildren().PushBack(node) 219 | n.updateNewNodeWithMatchesAbove(node) 220 | return node 221 | } 222 | 223 | func (n Network) buildOrShareJoinNode( 224 | parent IReteNode, amem *AlphaMemory, tests *list.List, h *Has) IReteNode { 225 | for e := parent.GetChildren().Front(); e != nil; e = e.Next() { 226 | if e.Value.(IReteNode).GetNodeType() != JoinNodeTy { 227 | continue 228 | } 229 | node := e.Value.(*JoinNode) 230 | if node.amem == amem && node.tests == tests { 231 | return node 232 | } 233 | } 234 | node := &JoinNode{ 235 | parent: parent, 236 | children: list.New(), 237 | amem: amem, 238 | tests: tests, 239 | has: h, 240 | } 241 | parent.GetChildren().PushBack(node) 242 | amem.successors.PushBack(node) 243 | return node 244 | } 245 | 246 | func (n Network) buildOrShareNegativeNode(parent IReteNode, amem *AlphaMemory, tests *list.List) IReteNode { 247 | for e := parent.GetChildren().Front(); e != nil; e = e.Next() { 248 | if e.Value.(IReteNode).GetNodeType() != NegativeNodeTy { 249 | continue 250 | } 251 | node := e.Value.(*NegativeNode) 252 | if node.amem == amem && node.tests == tests { 253 | return node 254 | } 255 | } 256 | node := &NegativeNode{ 257 | parent: parent, 258 | children: list.New(), 259 | amem: amem, 260 | tests: tests, 261 | items: list.New(), 262 | } 263 | parent.GetChildren().PushBack(node) 264 | amem.successors.PushBack(node) 265 | n.updateNewNodeWithMatchesAbove(node) 266 | return node 267 | } 268 | 269 | func (n Network) buildOrShareAlphaMemory(c Has) *AlphaMemory { 270 | currentNode := n.alphaRoot 271 | for field, sym := range c.fields { 272 | if !isVar(sym) { 273 | currentNode = n.buildOrShareConstantTestNode(currentNode, field, sym) 274 | } 275 | } 276 | if currentNode.outputMemory != nil { 277 | return currentNode.outputMemory 278 | } 279 | am := &AlphaMemory{ 280 | items: list.New(), 281 | successors: list.New(), 282 | } 283 | currentNode.outputMemory = am 284 | for e := n.alphaRoot.outputMemory.items.Front(); e != nil; e = e.Next() { 285 | w := e.Value.(*WME) 286 | if c.testWme(w) { 287 | am.activation(w) 288 | } 289 | } 290 | return am 291 | } 292 | 293 | func (n Network) buildOrShareConstantTestNode( 294 | parent *ConstantTestNode, field int, symbol string) *ConstantTestNode { 295 | for e := parent.children.Front(); e != nil; e = e.Next() { 296 | child := e.Value.(*ConstantTestNode) 297 | if child.fieldToTest == field && child.fieldMustEqual == symbol { 298 | return child 299 | } 300 | } 301 | node := &ConstantTestNode{ 302 | fieldToTest: field, 303 | fieldMustEqual: symbol, 304 | outputMemory: nil, 305 | children: list.New(), 306 | } 307 | parent.children.PushBack(node) 308 | return node 309 | } 310 | 311 | func (n Network) getJoinTestsFromCondition(c Has, earlierConds LHS) *list.List { 312 | ret := list.New() 313 | for vField1, v := range c.fields { 314 | if !isVar(v) { 315 | continue 316 | } 317 | for condIdx, cond := range earlierConds.items { 318 | switch cond.(type) { 319 | case Has: 320 | cond := cond.(Has) 321 | vField2 := cond.contain(v) 322 | if vField2 == -1 || cond.negative { 323 | continue 324 | } 325 | node := &TestAtJoinNode{vField1, condIdx, vField2} 326 | ret.PushBack(node) 327 | } 328 | } 329 | } 330 | return ret 331 | } 332 | 333 | func (n Network) updateNewNodeWithMatchesAbove(node IReteNode) { 334 | parent := node.GetParent() 335 | if parent == nil { 336 | return 337 | } 338 | switch parent.GetNodeType() { 339 | case BetaMemoryNodeTy: 340 | for e := parent.GetItems().Front(); e != nil; e = e.Next() { 341 | t := e.Value.(*Token) 342 | node.LeftActivation(t, nil, nil) 343 | } 344 | case JoinNodeTy: 345 | parent := parent.(*JoinNode) 346 | savedChildren := parent.children 347 | hackChildren := list.New() 348 | hackChildren.PushBack(node) 349 | parent.children = hackChildren 350 | for e := parent.amem.items.Front(); e != nil; e = e.Next() { 351 | w := e.Value.(*WME) 352 | parent.RightActivation(w) 353 | } 354 | parent.children = savedChildren 355 | case NegativeNodeTy: 356 | for e := parent.GetItems().Front(); e != nil; e = e.Next() { 357 | t := e.Value.(*Token) 358 | node.LeftActivation(t, nil, nil) 359 | } 360 | case NccNodeTy: 361 | for e := parent.GetItems().Front(); e != nil; e = e.Next() { 362 | t := e.Value.(*Token) 363 | node.LeftActivation(t, nil, nil) 364 | } 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /rete/network_test.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestNetworkAddWME(t *testing.T) { 9 | n := NewNetwork() 10 | c0 := NewHas("Object", "$x", "on", "$y") 11 | c1 := NewHas("Object", "$y", "left_of", "$z") 12 | am0 := n.buildOrShareAlphaMemory(c0) 13 | am1 := n.buildOrShareAlphaMemory(c1) 14 | wmes := []*WME{ 15 | NewWME("Object", "B1", "on", "B2"), 16 | NewWME("Object", "B2", "left_of", "B3"), 17 | NewWME("Object", "B2", "on", "table"), 18 | } 19 | for idx := range wmes { 20 | n.AddWME(wmes[idx]) 21 | } 22 | if am0.items.Len() != 2 || am1.items.Len() != 1 { 23 | t.Error("add wme error") 24 | } 25 | } 26 | 27 | func TestCase0(t *testing.T) { 28 | n := NewNetwork() 29 | env := make(Env) 30 | env["F"] = func(network *Network, token *Token) { 31 | x := token.GetBinding("x") 32 | y := token.GetBinding("y") 33 | z := token.GetBinding("z") 34 | dummy := token.GetRHSParam("dummy") 35 | ret := fmt.Sprintf("%s, %s, %s, %v", x, y, z, dummy) 36 | network.AddObject("result", ret) 37 | network.Halt() 38 | } 39 | 40 | c0 := NewHas("Object", "$x", "on", "$y") 41 | c1 := NewHas("Object", "$y", "left_of", "$z") 42 | c2 := NewHas("Object", "$z", "color", "red") 43 | m := make(map[string]interface{}) 44 | m["dummy"] = 1 45 | n.AddProduction(NewLHS(c0, c1, c2), RHS{ 46 | tmpl: `F`, 47 | Extra: m, 48 | }) 49 | wmes := []*WME{ 50 | NewWME("Object", "B1", "on", "B2"), 51 | NewWME("Object", "B1", "on", "B3"), 52 | NewWME("Object", "B1", "color", "red"), 53 | NewWME("Object", "B2", "on", "table"), 54 | NewWME("Object", "B2", "left_of", "B3"), 55 | NewWME("Object", "B2", "color", "blue"), 56 | NewWME("Object", "B3", "left_of", "B4"), 57 | NewWME("Object", "B3", "on", "table"), 58 | NewWME("Object", "B3", "color", "red"), 59 | } 60 | for idx := range wmes { 61 | n.AddWME(wmes[idx]) 62 | } 63 | err := n.ExecuteRules(env) 64 | if err != nil { 65 | t.Error(err) 66 | } 67 | if n.GetObject("result") != "B1, B2, B3, 1" { 68 | t.Error(n.GetObject("result")) 69 | fmt.Print(n.LogBuf) 70 | } 71 | 72 | } 73 | 74 | func TestNegativeNode(t *testing.T) { 75 | n := NewNetwork() 76 | c0 := NewHas("Object", "$x", "on", "$y") 77 | c1 := NewNeg("Object", "$y", "color", "blue") 78 | p := n.AddProduction(NewLHS(c0, c1), NewRHS()) 79 | 80 | wmes := []*WME{ 81 | NewWME("Object", "B1", "on", "B2"), 82 | NewWME("Object", "B1", "on", "B3"), 83 | NewWME("Object", "B2", "color", "blue"), 84 | NewWME("Object", "B3", "color", "red"), 85 | } 86 | for idx := range wmes { 87 | n.AddWME(wmes[idx]) 88 | } 89 | 90 | expect := ">" 91 | for e := p.GetItems().Front(); e != nil; e = e.Next() { 92 | tok := e.Value.(*Token) 93 | if fmt.Sprint(tok) != expect { 94 | t.Error("error result") 95 | } 96 | x, y := tok.GetBinding("x"), tok.GetBinding("y") 97 | if x != "B1" || y != "B3" { 98 | t.Error("error binding") 99 | } 100 | } 101 | } 102 | 103 | func TestNccNode(t *testing.T) { 104 | n := NewNetwork() 105 | c0 := NewHas("Object", "$x", "on", "$y") 106 | c1 := NewHas("Object", "$y", "left_of", "$z") 107 | c2 := NewHas("Object", "$z", "color", "red") 108 | c3 := NewHas("Object", "$z", "on", "$w") 109 | p := n.AddProduction(NewLHS(c0, c1, NewNccRule(c2, c3)), NewRHS()) 110 | wmes := []*WME{ 111 | NewWME("Object", "B1", "on", "B2"), 112 | NewWME("Object", "B1", "on", "B3"), 113 | NewWME("Object", "B1", "color", "red"), 114 | NewWME("Object", "B2", "on", "table"), 115 | NewWME("Object", "B2", "left_of", "B3"), 116 | NewWME("Object", "B2", "color", "blue"), 117 | NewWME("Object", "B3", "left_of", "B4"), 118 | NewWME("Object", "B3", "on", "table"), 119 | NewWME("Object", "B3", "color", "red"), 120 | } 121 | for idx := range wmes { 122 | n.AddWME(wmes[idx]) 123 | } 124 | expect := ">" 125 | for e := p.GetItems().Front(); e != nil; e = e.Next() { 126 | tok := e.Value.(*Token) 127 | if fmt.Sprint(tok) != expect { 128 | t.Error(tok) 129 | } 130 | x, y := tok.GetBinding("x"), tok.GetBinding("y") 131 | if x != "B1" || y != "B3" { 132 | t.Error("error binding") 133 | } 134 | } 135 | } 136 | 137 | func TestFromXML(t *testing.T) { 138 | data := ` 139 | 140 | 141 | 142 | 143 | 144 | 145 | 1]]> 146 | 147 | Handler 148 | 149 | 150 | 151 | 152 | 153 | 10]]> 154 | 155 | Handler 156 | 157 | ` 158 | n := NewNetwork() 159 | env := make(Env) 160 | env["Handler"] = func(network *Network, token *Token) { 161 | fmt.Println(token) 162 | } 163 | _, err := n.AddProductionFromXML(data) 164 | if err != nil { 165 | t.Error(err) 166 | return 167 | } 168 | 169 | wmes := []*WME{ 170 | NewWME("Request", "nil", "user_id", "100001"), 171 | NewWME("SKU", "1", "quantity", "2"), 172 | NewWME("SKU", "2", "quantity", "20"), 173 | } 174 | for idx := range wmes { 175 | n.AddWME(wmes[idx]) 176 | } 177 | n.ExecuteRules(env) 178 | } 179 | 180 | func TestFromJSON(t *testing.T) { 181 | data := ` 182 | { 183 | "productions": [ 184 | { 185 | "lhs": [ 186 | { 187 | "attribute": "quantity", 188 | "classname": "ProductSKU", 189 | "identifier": "1", 190 | "tag": "has", 191 | "value": "$quantity" 192 | } 193 | ], 194 | "rhs": { 195 | "kind": "quota:user:sku", 196 | "quota": 1 197 | } 198 | }, 199 | { 200 | "lhs": [ 201 | { 202 | "attribute": "quantity", 203 | "classname": "ProductSKU", 204 | "identifier": "$sku_id", 205 | "tag": "has", 206 | "value": "$quantity" 207 | } 208 | ], 209 | "rhs": { 210 | "kind": "quota:user:sku", 211 | "quota": 3 212 | } 213 | } 214 | ] 215 | }` 216 | _, err := FromJSON(data) 217 | if err != nil { 218 | t.Error(err) 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /rete/token.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import ( 4 | "container/list" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | type Env map[string]interface{} 10 | 11 | type Token struct { 12 | parent *Token 13 | wme *WME 14 | node IReteNode 15 | children *list.List 16 | joinResults *list.List // used in negative nodes 17 | nccResults *list.List 18 | owner *Token 19 | binding Env 20 | } 21 | 22 | func (tok *Token) get_wmes() []*WME { 23 | var ret []*WME 24 | _ws := list.New() 25 | _ws.PushFront(tok.wme) 26 | for tok.parent != nil { 27 | tok = tok.parent 28 | _ws.PushFront(tok.wme) 29 | } 30 | for e := _ws.Front(); e != nil; e = e.Next() { 31 | ret = append(ret, e.Value.(*WME)) 32 | } 33 | return ret 34 | } 35 | 36 | func makeToken(node IReteNode, parent *Token, w *WME, b Env) *Token { 37 | tok := &Token{ 38 | parent: parent, 39 | wme: w, 40 | node: node, 41 | children: list.New(), 42 | binding: b, 43 | } 44 | if parent != nil { 45 | parent.children.PushBack(tok) 46 | } 47 | if w != nil { 48 | w.tokens.PushBack(tok) 49 | } 50 | return tok 51 | } 52 | 53 | func (tok *Token) deleteTokenAndDescendents() { 54 | for tok.children != nil && tok.children.Len() > 0 { 55 | e := tok.children.Front() 56 | child := e.Value.(*Token) 57 | child.deleteTokenAndDescendents() 58 | tok.children.Remove(e) 59 | } 60 | removeByValue(tok.node.GetItems(), tok) 61 | if tok.wme != nil { 62 | removeByValue(tok.wme.tokens, tok) 63 | } 64 | if tok.parent != nil { 65 | removeByValue(tok.parent.children, tok) 66 | } 67 | } 68 | 69 | func (tok Token) String() string { 70 | ret := []string{} 71 | wmes := tok.get_wmes() 72 | for _, v := range wmes { 73 | s := fmt.Sprintf("%s", v) 74 | ret = append(ret, s) 75 | } 76 | return fmt.Sprintf("", strings.Join(ret, ", ")) 77 | } 78 | 79 | func (tok *Token) GetBinding(k string) interface{} { 80 | var v interface{} 81 | t := tok 82 | if t.binding != nil { 83 | v = t.binding[k] 84 | } 85 | for v == nil && t.parent != nil { 86 | t = t.parent 87 | if t.binding != nil { 88 | v = t.binding[k] 89 | } 90 | } 91 | return v 92 | } 93 | 94 | func (tok *Token) GetRHSParam(k string) interface{} { 95 | node, ok := tok.node.(*BetaMemory) 96 | if !ok { 97 | return nil 98 | } 99 | return node.GetExecuteParam(k) 100 | } 101 | 102 | func (tok *Token) AllBinding() Env { 103 | var path []*Token 104 | t := tok 105 | for t != nil { 106 | path = append(path, t) 107 | t = t.parent 108 | } 109 | result := make(Env) 110 | for _, t := range path { 111 | for k, v := range t.binding { 112 | result[k] = v 113 | } 114 | } 115 | return result 116 | } 117 | -------------------------------------------------------------------------------- /rete/token_test.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import ( 4 | "container/list" 5 | "testing" 6 | ) 7 | 8 | func Test_make_token(t *testing.T) { 9 | node := BetaMemory{ 10 | items: list.New(), 11 | parent: nil, 12 | children: list.New(), 13 | } 14 | w := &WME{ 15 | fields: [4]string{"Object", "B1", "on", "table"}, 16 | alphaMems: list.New(), 17 | tokens: list.New(), 18 | } 19 | token := makeToken(&node, nil, w, nil) 20 | if token.node.GetNodeType() != BetaMemoryNodeTy { 21 | t.Error("token node type error") 22 | } 23 | if token.wme != w { 24 | t.Error("token wme error") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /rete/utils.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import ( 4 | "container/list" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | 9 | "github.com/beevik/etree" 10 | ) 11 | 12 | func contain(l *list.List, value interface{}) *list.Element { 13 | if l == nil { 14 | return nil 15 | } 16 | for e := l.Front(); e != nil; e = e.Next() { 17 | if e.Value == value { 18 | return e 19 | } 20 | } 21 | return nil 22 | } 23 | 24 | func removeByValue(l *list.List, value interface{}) bool { 25 | if e := contain(l, value); e != nil { 26 | l.Remove(e) 27 | return true 28 | } 29 | return false 30 | } 31 | 32 | func FromXML(s string) (result []Production, err error) { 33 | doc := etree.NewDocument() 34 | err = doc.ReadFromString(s) 35 | if err != nil { 36 | return result, err 37 | } 38 | root := doc.Root() 39 | if root == nil { 40 | return result, errors.New("Not XML") 41 | } 42 | 43 | for _, ep := range root.ChildElements() { 44 | if ep.Tag != "production" { 45 | continue 46 | } 47 | p := Production{ 48 | rhs: NewRHS(), 49 | } 50 | for idx, hand := range ep.ChildElements() { 51 | if idx == 0 { 52 | p.lhs = XMLParseLHS(hand) 53 | } else if idx == 1 { 54 | for _, attr := range hand.Attr { 55 | p.rhs.Extra[attr.Key] = attr.Value 56 | } 57 | p.rhs.tmpl = hand.Text() 58 | } 59 | } 60 | result = append(result, p) 61 | } 62 | return result, nil 63 | } 64 | 65 | func XMLParseLHS(root *etree.Element) LHS { 66 | r := NewLHS() 67 | for _, e := range root.ChildElements() { 68 | switch e.Tag { 69 | case "has", "neg": 70 | className, identity, attribute, value := "", "", "", "" 71 | for _, attr := range e.Attr { 72 | if attr.Key == "classname" { 73 | className = attr.Value 74 | } else if attr.Key == "identifier" { 75 | identity = attr.Value 76 | } else if attr.Key == "attribute" { 77 | attribute = attr.Value 78 | } else if attr.Key == "value" { 79 | value = attr.Value 80 | } 81 | } 82 | var has Has 83 | if e.Tag == "has" { 84 | has = NewHas(className, identity, attribute, value) 85 | } else { 86 | has = NewNeg(className, identity, attribute, value) 87 | } 88 | r.items = append(r.items, has) 89 | case "filter": 90 | f := Filter{tmpl: e.Text()} 91 | r.items = append(r.items, f) 92 | case "ncc": 93 | _rule := XMLParseLHS(e) 94 | _rule.negative = true 95 | r.items = append(r.items, _rule) 96 | } 97 | } 98 | return r 99 | } 100 | 101 | func FromJSON(s string) (r []Production, err error) { 102 | root := make(map[string]interface{}) 103 | err = json.Unmarshal([]byte(s), &root) 104 | if err != nil { 105 | return r, err 106 | } 107 | if root["productions"] == nil { 108 | return r, errors.New("no productions") 109 | } 110 | ps, ok := root["productions"].([]interface{}) 111 | if !ok { 112 | return r, errors.New("productions not List") 113 | } 114 | for _, p := range ps { 115 | production := Production{} 116 | p, ok := p.(map[string]interface{}) 117 | if !ok { 118 | message := fmt.Sprintf("production not Object: %s", p) 119 | return r, errors.New(message) 120 | } 121 | rhsObj, ok := p["rhs"].(map[string]interface{}) 122 | production.rhs.Extra = rhsObj 123 | if rhsObj["tmpl"] != nil { 124 | production.rhs.tmpl = rhsObj["tmpl"].(string) 125 | } 126 | if !ok { 127 | message := fmt.Sprintf("rhs not Object: %s", p["rhs"]) 128 | return r, errors.New(message) 129 | } 130 | lhs, ok := p["lhs"].([]interface{}) 131 | if !ok { 132 | message := fmt.Sprintf("lhs not List: %s", p["lhs"]) 133 | return r, errors.New(message) 134 | } 135 | production.lhs, err = JSONParseLHS(lhs) 136 | if err != nil { 137 | return r, err 138 | } 139 | r = append(r, production) 140 | } 141 | return r, err 142 | } 143 | 144 | func JSONParseLHS(lhs []interface{}) (r LHS, err error) { 145 | for _, e := range lhs { 146 | cond, ok := e.(map[string]interface{}) 147 | if !ok { 148 | message := fmt.Sprintf("lhs element not Object: %s", e) 149 | return r, errors.New(message) 150 | } 151 | switch cond["tag"] { 152 | case "has", "neg": 153 | class, ok0 := cond["classname"].(string) 154 | id, ok1 := cond["identifier"].(string) 155 | attr, ok2 := cond["attribute"].(string) 156 | value, ok3 := cond["value"].(string) 157 | if !ok0 || !ok1 || !ok2 || !ok3 { 158 | message := fmt.Sprintf("condition missing fields: %s", cond) 159 | return r, errors.New(message) 160 | } 161 | if cond["tag"] == "has" { 162 | r.items = append(r.items, NewHas(class, id, attr, value)) 163 | } else { 164 | r.items = append(r.items, NewNeg(class, id, attr, value)) 165 | } 166 | case "filter": 167 | tmpl, ok := cond["tmpl"].(string) 168 | if !ok { 169 | message := fmt.Sprintf("filter tmpl not string: %s", cond) 170 | return r, errors.New(message) 171 | } 172 | r.items = append(r.items, Filter{tmpl: tmpl}) 173 | case "ncc": 174 | ncc, ok := cond["items"].([]interface{}) 175 | if !ok { 176 | message := fmt.Sprintf("lhs not List: %s", cond["items"]) 177 | return r, errors.New(message) 178 | } 179 | _rule, err := JSONParseLHS(ncc) 180 | if err != nil { 181 | return r, err 182 | } 183 | _rule.negative = true 184 | r.items = append(r.items, _rule) 185 | default: 186 | message := fmt.Sprintf("tag error: %s", cond["tag"]) 187 | return r, errors.New(message) 188 | 189 | } 190 | } 191 | return r, err 192 | } 193 | -------------------------------------------------------------------------------- /rete/wme.go: -------------------------------------------------------------------------------- 1 | package rete 2 | 3 | import ( 4 | "container/list" 5 | "fmt" 6 | ) 7 | 8 | type WME struct { 9 | fields [4]string 10 | alphaMems *list.List 11 | tokens *list.List 12 | negativeJoinResults *list.List 13 | } 14 | 15 | func RemoveWME(w *WME) { 16 | for e := w.alphaMems.Front(); e != nil; e = e.Next() { 17 | amem := e.Value.(*AlphaMemory) 18 | removeByValue(amem.items, w) 19 | } 20 | for w.tokens != nil && w.tokens.Len() > 0 { 21 | e := w.tokens.Front() 22 | t := e.Value.(*Token) 23 | t.deleteTokenAndDescendents() 24 | w.tokens.Remove(e) 25 | } 26 | for e := w.negativeJoinResults.Front(); e != nil; e = e.Next() { 27 | jr := e.Value.(*NegativeJoinResult) 28 | removeByValue(jr.owner.joinResults, jr) 29 | if jr.owner.joinResults.Len() == 0 { 30 | for i := jr.owner.node.GetChildren().Front(); i != nil; i = i.Next() { 31 | child := i.Value.(IReteNode) 32 | child.LeftActivation(jr.owner, nil, nil) 33 | } 34 | } 35 | } 36 | } 37 | 38 | func NewWME(className, id, attr, value string) *WME { 39 | return &WME{ 40 | fields: [4]string{className, id, attr, value}, 41 | alphaMems: list.New(), 42 | tokens: list.New(), 43 | negativeJoinResults: list.New(), 44 | } 45 | } 46 | 47 | func (wme *WME) Equal(w *WME) bool { 48 | return wme.fields == w.fields 49 | } 50 | 51 | func (wme *WME) String() string { 52 | return fmt.Sprintf("%s", wme.fields) 53 | } 54 | --------------------------------------------------------------------------------