Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
newm4n authored Aug 29, 2023
2 parents 3ae9c8f + cb643c1 commit d402097
Show file tree
Hide file tree
Showing 48 changed files with 776 additions and 476 deletions.
2 changes: 1 addition & 1 deletion ast/ExpressionAtom.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,5 +413,5 @@ func (e *ExpressionAtom) Evaluate(dataContext IDataContext, memory *WorkingMemor
return e.Value, nil
}

panic("should not be reached")
return reflect.Value{}, fmt.Errorf("this portion of code should not be reached")
}
31 changes: 20 additions & 11 deletions ast/KnowledgeBase.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"bytes"
"fmt"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
"io"
"sort"
"strings"
Expand Down Expand Up @@ -84,7 +83,6 @@ func (lib *KnowledgeLibrary) LoadKnowledgeBaseFromReader(reader io.Reader, overw
if r := recover(); r != nil {
retKb = nil
retErr = fmt.Errorf("panic recovered during LoadKnowledgeBaseFromReader, recover \"%v\". send us your report to https://github.com/hyperjumptech/grule-rule-engine/issues", r)
logrus.Panicf("panic recovered during LoadKnowledgeBaseFromReader, recover \"%v\". send us your report to https://github.com/hyperjumptech/grule-rule-engine/issues", r)
}
}()

Expand All @@ -94,7 +92,10 @@ func (lib *KnowledgeLibrary) LoadKnowledgeBaseFromReader(reader io.Reader, overw

return nil, err
}
knowledgeBase := catalog.BuildKnowledgeBase()
knowledgeBase, err := catalog.BuildKnowledgeBase()
if err != nil {
return nil, err
}
if overwrite {
lib.Library[fmt.Sprintf("%s:%s", knowledgeBase.Name, knowledgeBase.Version)] = knowledgeBase

Expand Down Expand Up @@ -127,21 +128,25 @@ func (lib *KnowledgeLibrary) StoreKnowledgeBaseToWriter(writer io.Writer, name,

// NewKnowledgeBaseInstance will create a new instance based on KnowledgeBase blue print
// identified by its name and version
func (lib *KnowledgeLibrary) NewKnowledgeBaseInstance(name, version string) *KnowledgeBase {
func (lib *KnowledgeLibrary) NewKnowledgeBaseInstance(name, version string) (*KnowledgeBase, error) {
knowledgeBase, ok := lib.Library[fmt.Sprintf("%s:%s", name, version)]
if ok {
newClone := knowledgeBase.Clone(pkg.NewCloneTable())
newClone, err := knowledgeBase.Clone(pkg.NewCloneTable())
if err != nil {
return nil, err
}
if knowledgeBase.IsIdentical(newClone) {
AstLog.Debugf("Successfully create instance [%s:%s]", newClone.Name, newClone.Version)

return newClone
return newClone, nil
}
AstLog.Fatalf("ORIGIN : %s", knowledgeBase.GetSnapshot())
AstLog.Fatalf("CLONE : %s", newClone.GetSnapshot())
panic("The clone is not identical")

return nil, fmt.Errorf("the clone is not identical")
}

return nil
return nil, fmt.Errorf("specified knowledge base name and version not exist")
}

// KnowledgeBase is a collection of RuleEntries. It has a name and version.
Expand Down Expand Up @@ -210,7 +215,7 @@ func (e *KnowledgeBase) GetSnapshot() string {
}

// Clone will clone this instance of KnowledgeBase and produce another (structure wise) identical instance.
func (e *KnowledgeBase) Clone(cloneTable *pkg.CloneTable) *KnowledgeBase {
func (e *KnowledgeBase) Clone(cloneTable *pkg.CloneTable) (*KnowledgeBase, error) {
clone := &KnowledgeBase{
Name: e.Name,
Version: e.Version,
Expand All @@ -228,10 +233,14 @@ func (e *KnowledgeBase) Clone(cloneTable *pkg.CloneTable) *KnowledgeBase {
}
}
if e.WorkingMemory != nil {
clone.WorkingMemory = e.WorkingMemory.Clone(cloneTable)
wm, err := e.WorkingMemory.Clone(cloneTable)
if err != nil {
return nil, err
}
clone.WorkingMemory = wm
}

return clone
return clone, nil
}

// AddRuleEntry add ruleentry into this knowledge base.
Expand Down
3 changes: 2 additions & 1 deletion ast/RuleEntry.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ func (e *RuleEntry) Evaluate(ctx context.Context, dataContext IDataContext, memo
}
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("Error while evaluating rule %s, panic recovered", e.RuleName)
err = fmt.Errorf("error while evaluating rule %s, panic recovered", e.RuleName)
can = false
}
}()
if e.Retracted {
Expand Down
8 changes: 4 additions & 4 deletions ast/Serializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ type Catalog struct {
// BuildKnowledgeBase will rebuild a knowledgebase from this Catalog.
// the rebuilt KnowledgeBase is identical to the original KnowledgeBase from
// which this Catalog was built.
func (cat *Catalog) BuildKnowledgeBase() *KnowledgeBase {
func (cat *Catalog) BuildKnowledgeBase() (*KnowledgeBase, error) {
workingMem := &WorkingMemory{
Name: cat.MemoryName,
Version: cat.MemoryVersion,
Expand Down Expand Up @@ -280,7 +280,7 @@ func (cat *Catalog) BuildKnowledgeBase() *KnowledgeBase {
}
importTable[amet.AstID] = n
default:
panic("Unrecognized meta type")
return nil, fmt.Errorf("unrecognized meta type")
}
}

Expand Down Expand Up @@ -407,7 +407,7 @@ func (cat *Catalog) BuildKnowledgeBase() *KnowledgeBase {
whenScope.Expression = importTable[amet.ExpressionID].(*Expression)
}
default:
panic("Unrecognized meta type")
return nil, fmt.Errorf("unknown AST type")
}
}

Expand Down Expand Up @@ -450,7 +450,7 @@ func (cat *Catalog) BuildKnowledgeBase() *KnowledgeBase {
}
}

return knowledgeBase
return knowledgeBase, nil
}

// Equals used for testing purpose, to ensure that two catalog
Expand Down
20 changes: 10 additions & 10 deletions ast/WorkingMemory.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func (workingMem *WorkingMemory) Equals(that *WorkingMemory) bool {
}

// Clone will clone this WorkingMemory. The new clone will have an identical structure
func (workingMem *WorkingMemory) Clone(cloneTable *pkg.CloneTable) *WorkingMemory {
func (workingMem *WorkingMemory) Clone(cloneTable *pkg.CloneTable) (*WorkingMemory, error) {
AstLog.Debugf("Cloning working memory %s:%s", workingMem.Name, workingMem.Version)
clone := NewWorkingMemory(workingMem.Name, workingMem.Version)

Expand All @@ -154,7 +154,7 @@ func (workingMem *WorkingMemory) Clone(cloneTable *pkg.CloneTable) *WorkingMemor
clone.expressionSnapshotMap[k] = cloneTable.Records[expr.AstID].CloneInstance.(*Expression)
} else {

panic(fmt.Sprintf("expression %s is not on the clone table - %s", expr.GrlText, expr.GetSnapshot()))
return nil, fmt.Errorf("expression %s is not on the clone table - %s", expr.GrlText, expr.GetSnapshot())
}
}
}
Expand All @@ -166,7 +166,7 @@ func (workingMem *WorkingMemory) Clone(cloneTable *pkg.CloneTable) *WorkingMemor
clone.expressionAtomSnapshotMap[k] = cloneTable.Records[exprAtm.AstID].CloneInstance.(*ExpressionAtom)
} else {

panic(fmt.Sprintf("expression atom %s is not on the clone table. ASTID %s", exprAtm.GrlText, exprAtm.AstID))
return nil, fmt.Errorf("expression atom %s is not on the clone table. ASTID %s", exprAtm.GrlText, exprAtm.AstID)
}
}
}
Expand All @@ -178,7 +178,7 @@ func (workingMem *WorkingMemory) Clone(cloneTable *pkg.CloneTable) *WorkingMemor
clone.variableSnapshotMap[key] = cloneTable.Records[variable.AstID].CloneInstance.(*Variable)
} else {

panic(fmt.Sprintf("variable %s is not on the clone table", variable.GrlText))
return nil, fmt.Errorf("variable %s is not on the clone table", variable.GrlText)
}
}
}
Expand All @@ -194,12 +194,12 @@ func (workingMem *WorkingMemory) Clone(cloneTable *pkg.CloneTable) *WorkingMemor
clone.expressionVariableMap[clonedVari][k2] = cloneTable.Records[expr.AstID].CloneInstance.(*Expression)
} else {

panic(fmt.Sprintf("expression %s is not on the clone table", expr.GrlText))
return nil, fmt.Errorf("expression %s is not on the clone table", expr.GrlText)
}
}
} else {

panic(fmt.Sprintf("variable %s is not on the clone table", key.GrlText))
return nil, fmt.Errorf("variable %s is not on the clone table", key.GrlText)
}
}
}
Expand All @@ -215,23 +215,23 @@ func (workingMem *WorkingMemory) Clone(cloneTable *pkg.CloneTable) *WorkingMemor
clone.expressionAtomVariableMap[clonedVari][k2] = cloneTable.Records[expr.AstID].CloneInstance.(*ExpressionAtom)
} else {

panic(fmt.Sprintf("expression atom %s is not on the clone table", expr.GrlText))
return nil, fmt.Errorf("expression atom %s is not on the clone table", expr.GrlText)
}
}
} else {

panic(fmt.Sprintf("variable %s is not on the clone table", key.GrlText))
return nil, fmt.Errorf("variable %s is not on the clone table", key.GrlText)
}
}
}

if workingMem.Equals(clone) {
clone.DebugContent()

return clone
return clone, nil
}

panic("Clone not equals the origin.")
return nil, fmt.Errorf("clone not equals the origin")
}

// IndexVariables will index all expression and expression atoms that contains a speciffic variable name
Expand Down
8 changes: 7 additions & 1 deletion editor/EvaluationRoute.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,13 @@ func InitializeEvaluationRoute(router *mux.HyperMux) {
}

eng1 := &engine.GruleEngine{MaxCycle: 5}
kb := knowledgeLibrary.NewKnowledgeBaseInstance("Evaluator", "0.0.1")
kb, err := knowledgeLibrary.NewKnowledgeBaseInstance("Evaluator", "0.0.1")
if err != nil {
writer.WriteHeader(http.StatusBadRequest)
_, _ = writer.Write([]byte(fmt.Sprintf("Grule Error : %s", err.Error())))

return
}
err = eng1.Execute(dataContext, kb)
if err != nil {
writer.WriteHeader(http.StatusBadRequest)
Expand Down
2 changes: 1 addition & 1 deletion editor/StaticRoute.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package editor

import (
"embed"
"github.com/hyperjumptech/grule-rule-engine/editor/mime"
mux "github.com/hyperjumptech/hyper-mux"
"github.com/sirupsen/logrus"
"grule-rule-engine/editor/mime"
"net/http"
"os"
"strings"
Expand Down
2 changes: 1 addition & 1 deletion editor/cmd/Main.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package main

import (
"github.com/hyperjumptech/grule-rule-engine/editor"
"github.com/sirupsen/logrus"
"grule-rule-engine/editor"
)

func main() {
Expand Down
39 changes: 26 additions & 13 deletions engine/GruleEngine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ func TestGrule_Execute(t *testing.T) {
err = rb.BuildRuleFromResource("Test", "0.1.1", pkg.NewBytesResource([]byte(rules)))
assert.NoError(t, err)
engine := NewGruleEngine()
kb := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
kb, err := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
assert.NoError(t, err)
start := time.Now()
err = engine.Execute(dctx, kb)
assert.NoError(t, err)
Expand Down Expand Up @@ -158,7 +159,8 @@ func TestEngine_ExecuteErr(t *testing.T) {

engine := NewGruleEngine()
engine.ReturnErrOnFailedRuleEvaluation = true
kb := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
kb, err := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
assert.NoError(t, err)
err = engine.Execute(dctx, kb)
assert.Error(t, err)
}
Expand Down Expand Up @@ -215,7 +217,8 @@ func TestEngine_ComplexRule1(t *testing.T) {
rb := builder.NewRuleBuilder(lib)
err = rb.BuildRuleFromResource("Test", "0.1.1", pkg.NewBytesResource([]byte(complexRule1)))
assert.NoError(t, err)
kb := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
kb, err := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
assert.NoError(t, err)

engine := NewGruleEngine()
err = engine.Execute(dctx, kb)
Expand Down Expand Up @@ -250,7 +253,8 @@ func TestEngine_ComplexRule2(t *testing.T) {
rb := builder.NewRuleBuilder(lib)
err = rb.BuildRuleFromResource("Test", "0.1.1", pkg.NewBytesResource([]byte(complexRule2)))
assert.NoError(t, err)
kb := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
kb, err := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
assert.NoError(t, err)

engine := NewGruleEngine()
err = engine.Execute(dctx, kb)
Expand Down Expand Up @@ -286,7 +290,8 @@ func TestEngine_ComplexRule3(t *testing.T) {
rb := builder.NewRuleBuilder(lib)
err = rb.BuildRuleFromResource("Test", "0.1.1", pkg.NewBytesResource([]byte(complexRule3)))
assert.NoError(t, err)
kb := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
kb, err := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
assert.NoError(t, err)

engine := NewGruleEngine()
err = engine.Execute(dctx, kb)
Expand Down Expand Up @@ -323,7 +328,8 @@ func TestEngine_ComplexRule4(t *testing.T) {
rb := builder.NewRuleBuilder(lib)
err = rb.BuildRuleFromResource("Test", "0.1.1", pkg.NewBytesResource([]byte(complexRule4)))
assert.NoError(t, err)
kb := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
kb, err := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
assert.NoError(t, err)

engine := NewGruleEngine()
err = engine.Execute(dctx, kb)
Expand Down Expand Up @@ -352,7 +358,8 @@ func TestEngine_OperatorPrecedence(t *testing.T) {
rb := builder.NewRuleBuilder(lib)
err = rb.BuildRuleFromResource("Test", "0.1.1", pkg.NewBytesResource([]byte(OpPresedenceRule)))
assert.NoError(t, err)
kb := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
kb, err := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
assert.NoError(t, err)

engine := NewGruleEngine()
err = engine.Execute(dctx, kb)
Expand Down Expand Up @@ -398,7 +405,8 @@ And another`
rb := builder.NewRuleBuilder(lib)
err = rb.BuildRuleFromResource("Test", "0.1.1", pkg.NewBytesResource([]byte(escapedRules)))
assert.NoError(t, err)
kb := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
kb, err := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
assert.NoError(t, err)

assert.False(t, es.Result1)
assert.False(t, es.Result2)
Expand Down Expand Up @@ -439,7 +447,8 @@ rule KeepSleep "test string escaping" salience 10 {
}
`)))
assert.NoError(t, err)
kb := lib.NewKnowledgeBaseInstance("TestTimer", "0.1.1")
kb, err := lib.NewKnowledgeBaseInstance("TestTimer", "0.1.1")
assert.NoError(t, err)
engine := NewGruleEngine()
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
Expand Down Expand Up @@ -514,7 +523,8 @@ func TestGruleEngine_FetchMatchingRules_Having_Same_Salience(t *testing.T) {
rb := builder.NewRuleBuilder(lib)
err = rb.BuildRuleFromResource("conflict_rules_test", "0.1.1", pkg.NewBytesResource([]byte(duplicateRules)))
assert.NoError(t, err)
kb := lib.NewKnowledgeBaseInstance("conflict_rules_test", "0.1.1")
kb, err := lib.NewKnowledgeBaseInstance("conflict_rules_test", "0.1.1")
assert.NoError(t, err)

//When
engine := NewGruleEngine()
Expand All @@ -537,7 +547,8 @@ func TestEngine_FetchMatchingRulesErr(t *testing.T) {

engine := NewGruleEngine()
engine.ReturnErrOnFailedRuleEvaluation = true
kb := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
kb, err := lib.NewKnowledgeBaseInstance("Test", "0.1.1")
assert.NoError(t, err)
mr, err := engine.FetchMatchingRules(dctx, kb)
assert.Error(t, err)
assert.Nil(t, mr)
Expand Down Expand Up @@ -598,7 +609,8 @@ func TestGruleEngine_FetchMatchingRules_Having_Diff_Salience(t *testing.T) {
rb := builder.NewRuleBuilder(lib)
err = rb.BuildRuleFromResource("conflict_rules_test", "0.1.1", pkg.NewBytesResource([]byte(duplicateRulesWithDiffSalience)))
assert.NoError(t, err)
kb := lib.NewKnowledgeBaseInstance("conflict_rules_test", "0.1.1")
kb, err := lib.NewKnowledgeBaseInstance("conflict_rules_test", "0.1.1")
assert.NoError(t, err)

//When
engine := NewGruleEngine()
Expand Down Expand Up @@ -661,7 +673,8 @@ func TestGruleEngine_Follows_logical_operator_precedence(t *testing.T) {
rb := builder.NewRuleBuilder(lib)
err = rb.BuildRuleFromResource("logical_operator_rules_test", "0.1.1", pkg.NewBytesResource([]byte(logicalOperatorPrecedenceRules)))
assert.NoError(t, err)
kb := lib.NewKnowledgeBaseInstance("logical_operator_rules_test", "0.1.1")
kb, err := lib.NewKnowledgeBaseInstance("logical_operator_rules_test", "0.1.1")
assert.NoError(t, err)

//When
engine := NewGruleEngine()
Expand Down
Loading

0 comments on commit d402097

Please sign in to comment.