Skip to content

Latest commit

 

History

History
304 lines (236 loc) · 9.49 KB

GRL_en.md

File metadata and controls

304 lines (236 loc) · 9.49 KB

Grule Rule Language (GRL)

GRL_cn GRL_de GRL_en GRL_id GRL_pl

About | Tutorial | Rule Engine | GRL | GRL JSON | RETE Algorithm | Functions | FAQ | Benchmark


The GRL is a DSL (Domain Specific Language) designed for Grule. It's a simplified language to be used for defining rule condition criteria and actions to be executed if the criteria are met.

The language has the following structure:

rule <RuleName> <RuleDescription> [salience <priority>] {
    when
        <boolean expression>
    then
        <assignment or operation expression>
}

RuleName: Identifies a specific rule. The name must be unique in the entire knowledge base, consist of one word and it must not contain white space.

RuleDescription: Describes the rule for human consumption. The description should be enclosed in double quotes.

Salience (optional, default 0): Defines the importance of the rule. Lower values indicate rules of lower priority. The salience value is used to specify a priority-sorted order when multiple rules are encountered. Salience will accept negative values, so you may wish to use those to mark rules that you barely even care about. Rule engines are declarative so you can't be guaranteed in which order your rules will be evaluated. As such, consider salience to be a hint to the engine that helps it decide what to do in the event of a conflict.

Boolean Expression: A predicate expression that will be evaluated by the rule engine to identify whether or not a specific rule's action is a candidate for execution with the current facts.

Assignment or Operation Expression: This is the action to be taken should the rule evaluate to true. You are not limited to a single expression and can supply a list of them, separated by the ; character. The action statements are meant to modify the current fact values, make calculations, log some statements, etc...

Boolean Expression

A boolean expression should be familiar to most, if not all programmers.

when
     contains(User.Name, "robert") &&
     User.Age > 35
then
     ...

Constants and Literals

Literal Description Example
String Holds a string literal, enclosed with double (") or single (') quotes "This is a string" or 'this is a string'
Integer Holds an integer value and may preceded with negative symbol - 1 or 34 or 42344 or -553
Real Holds a real value 234.4553, -234.3, 314E-2, .32, 12.32E12
Boolean Holds a boolean value true, TRUE, False

More examples can be found at GRL Literals.

Note: Special characters in strings must be escaped following the same rules used for strings in Go. However, backtick strings are not supported.

Operators supported

Type Operator
Math +, -, /, *, %
Bit-wise operators |, &
Logical operators &&, ||
Comparison operators <, <=, >, >=, ==, !=

Operator precedence

Grule follows operator precedence in Go.

Precedence Operator
5 *, /, %, &
4 +, -, |
3 ==, !=, <, <=, >, >=
2 &&
1 ||

Comments

Comments also follow the standard Go format.

// This is a comment
// And this

/* And also this */

/*
   As well as this
*/

Array/Slice and Map

Since version 1.6.0, Grule supports accessing facts in array/slice or map.

Suppose you have a fact structure like the following:

type MyFact struct {
    AnIntArray   []int
    AStringArray []string
    SubFacts     []*MyFact
    SubMaps      map[string]*MyFact
}

You can evaluate those slices and maps from your rule with:

    when 
       Fact.AnIntArray[1] == 12 &&
       Fact.AStringArray[12] != "SomeText" &&
       Fact.SubFacts[1].SubFacts[2].AnIntArray[12] > 100 &&
       Fact.SubMaps["Key"].AnIntArray[0] == 1000
    then
       ...

Rule execution will panic if your rule tries to access an array element that is out of bounds.

Assigning values into Array/Slice and Map

You can set an array value if the index you specify is valid.

   then
      Fact.AnIntArray[10] = 12;
      Fact.SubMap["AKey"].AStringArray[1] = "New Value";
      Fact.AnotherMap[Fact.SomeFunction()] = "Another Value";

There are a couple of functions you can use to work with array/slice and map. Those can be found at Function page.

Negation

A unary negation symbol ! is supported by GRL in addition to NEQ != symbol. It is to be used in front of a boolean expression or expression atom.

For example in expression atom:

when 
    !FunctionReturnTrue() ||
    !false
then
    ... 

or in expression:

when
    !(you.IsOk() || !today.isMonday())
then
    ...

Function call

Any visible function can be called from your rule so long as they return 0 or 1 value. For example:

    when
        Fact.FunctionA() == "text" ||
        Fact.FunctionB("arg") == "text" ||
        Fact.FunctionC(Fact.Field, true)
    then
        Fact.CallFunction();
        Fact.Value = Fact.CallFunction();
        ...

In version 1.6.0, Grule can chain function calls and value accessors. For example;

    when
        Fact.Function().StringField == "" ||
        Fact.Function("contant").ObjField.OtherFunction() &&
        ...
    then
        Fact.CallFunction().CallAnotherFunction();
        ...

Also introduced in 1.6.0, you can call functions on literatl constants. For example:

    when
        "AString   ".Trim().ToUpper().HasSuffix("ING")
    then
        Fact.Result = Fact.ReturnStringFunc().Trim().ToLower();

For a list of available functions, consult Function Page.

Examples

rule SpeedUp "When testcar is speeding up we keep increase the speed."  {
    when
        TestCar.SpeedUp == true && TestCar.Speed < TestCar.MaxSpeed
    then
        TestCar.Speed = TestCar.Speed + TestCar.SpeedIncrement;
            DistanceRecord.TotalDistance = DistanceRecord.TotalDistance + TestCar.Speed;
}

rule StartSpeedDown "When testcar is speeding up and over max speed we change to speed down."  {
    when
        TestCar.SpeedUp == true && TestCar.Speed >= TestCar.MaxSpeed
    then
        TestCar.SpeedUp = false;
            log("Now we slow down");
}

rule SlowDown "When testcar is slowing down we keep decreasing the speed."  {
    when
        TestCar.SpeedUp == false && TestCar.Speed > 0
    then
        TestCar.Speed = TestCar.Speed - TestCar.SpeedIncrement;
        DistanceRecord.TotalDistance = DistanceRecord.TotalDistance + TestCar.Speed;
}

rule SetTime "When Distance Recorder time not set, set it." {
    when
        isNil(DistanceRecord.TestTime)
    then
        log("Set the test time");
        DistanceRecord.TestTime = now();
}

Debugging GRL Syntax

Your application, you can test if a GRL script or snippet contains a GRL syntax error.

        RuleWithError := `
        rule ErrorRule1 "Rule with error"  salience 10{
            when
              Pogo.Compare(User.Name, "Calo")  
            then
              User.Name = "Success";
              Log(User.Name)
              Retract("AgeNameCheck");
        }
        `

	// Build normally
	err := ruleBuilder.BuildRuleFromResource("Test", "0.1.1", pkg.NewBytesResource([]byte(RuleWithError)))

	// If the err != nil something is wrong.
	if err != nil {
		// Cast the error into pkg.GruleErrorReporter with typecast checking.
		// Typecast checking is necessary because the err might not only parsing error.
		if reporter, ok := err.(*pkg.GruleErrorReporter); ok {
			// Lets iterate all the error we get during parsing.
			for i, er := range reporter.Errors {
				fmt.Printf("detected error #%d : %s\n", i, er.Error())
			}
		} else {
			// Well, its an error but not GruleErrorReporter instance. could be IO error.
			t.Error("There should be GruleErrorReporter")
			t.FailNow()
		}
	}

This will print

detected error #0 : grl error on 8:6 missing ';' at 'Retract'

IDE Support

Visual Studio Code: https://marketplace.visualstudio.com/items?itemName=avisdsouza.grule-syntax