Skip to content

Commit

Permalink
Implemented BooleanExpression
Browse files Browse the repository at this point in the history
Created and Implemented three Formula(s): AndFormula, OrFormula, NotFormula
Implemented parsing for ChoiceRules (BooleanExpression and DataTestExpression)
Added test for choice parsing (failing)
  • Loading branch information
redjack96 committed Aug 20, 2024
1 parent 2444265 commit 0af4a06
Show file tree
Hide file tree
Showing 4 changed files with 426 additions and 15 deletions.
59 changes: 53 additions & 6 deletions internal/asl/choice_rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,67 @@ type ChoiceRule interface {
IsBooleanExpression() bool
}

func ParseRule(json []byte) (ChoiceRule, error) {
// detect if it is a boolean or dataTest expression
if JsonHasAllKeys(json, "Variable", "Next") && JsonNumberOfKeys(json) == 3 {
return ParseDataTestExpr(json)
} else if JsonHasOneKey(json, "And", "Or", "Not") {
return ParseBooleanExpr(json)
} else {
return nil, fmt.Errorf("invalid choice rule: %s", string(json))
}
}

// BooleanExpression is a ChoiceRule that is parsable from combination of And, Or and Not json objects
type BooleanExpression struct {
And []*ChoiceRule
Or []*ChoiceRule
Not *ChoiceRule
Next string // Necessary. The value should match a state name in the StateMachine
Formula Formula
Next string // Necessary. The value should match a state name in the StateMachine
}

func (b *BooleanExpression) Equals(cmp types.Comparable) bool {
b2, ok := cmp.(*BooleanExpression)
if !ok {
return false
}
return b.Next == b2.Next && b.Formula.Equals(b2.Formula)
}

func (b *BooleanExpression) String() string {
return "\t\t\t\t\t" + b.Formula.GetFormulaType() + ": {\n" +
b.Formula.String() +
"\n\t\t\t\t\tNext: " + b.Next +
"\n\t\t\t\t\t}"
}

func (b *BooleanExpression) IsBooleanExpression() bool {
return true
}

func ParseBooleanExpr(jsonRule []byte) (*BooleanExpression, error) {
return &BooleanExpression{}, nil
func ParseBooleanExpr(jsonExpression []byte) (*BooleanExpression, error) {
next, err := JsonExtractString(jsonExpression, "Next")
if err != nil {
return nil, fmt.Errorf("boolean expression doesn't have a Next json field")
}
if JsonHasKey(jsonExpression, "And") {
andFormula, err2 := ParseAnd(jsonExpression)
if err2 != nil {
return nil, err2
}
return &BooleanExpression{Formula: andFormula, Next: next}, nil
} else if JsonHasKey(jsonExpression, "Or") {
orFormula, err2 := ParseOr(jsonExpression)
if err2 != nil {
return nil, err2
}
return &BooleanExpression{Formula: orFormula, Next: next}, nil
} else if JsonHasKey(jsonExpression, "Not") {
notFormula, err2 := ParseNot(jsonExpression)
if err2 != nil {
return nil, err2
}
return &BooleanExpression{Formula: notFormula, Next: next}, nil
}
return nil, fmt.Errorf("invalid boolean expression: %s", string(jsonExpression))
}

// DataTestExpression is a ChoiceRule that is parsable from a Variable, a ComparisonOperator and has a Next field.
Expand Down
159 changes: 159 additions & 0 deletions internal/asl/formula.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package asl

import (
"fmt"
"github.com/buger/jsonparser"
"github.com/grussorusso/serverledge/internal/types"
)

type Formula interface {
types.Comparable
fmt.Stringer
GetFormulaType() string
}

type AndFormula struct {
And []ChoiceRule
}

func (a *AndFormula) Equals(cmp types.Comparable) bool {
otherAnd, ok := cmp.(*AndFormula)
if !ok {
return false
}
for i, choiceRule := range a.And {
if !choiceRule.Equals(otherAnd.And[i]) {
return false
}
}

return true
}

func (a *AndFormula) String() string {
str := "\t\t\t\t\t\tAnd: ["

for i, choiceRule := range a.And {
str += "\n\t\t\t\t\t\t\t" + choiceRule.String()
if i != len(a.And)-1 {
str += ","
}
}

str += "]"

return str
}

func (a *AndFormula) GetFormulaType() string {
return "And"
}

func ParseAnd(jsonBytes []byte) (*AndFormula, error) {
andJson, err := JsonExtract(jsonBytes, "And")
if err != nil {
return nil, fmt.Errorf("failed to parse and formula: %v", err)
}
andArray := make([]ChoiceRule, 0)
_, err = jsonparser.ArrayEach(andJson, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
rule, err := ParseRule(andJson)
if err != nil {
return
}
andArray = append(andArray, rule)
})
if err != nil {
return nil, fmt.Errorf("failed to parse and formula: %v", err)
}

return &AndFormula{And: andArray}, nil
}

type OrFormula struct {
Or []ChoiceRule
}

func ParseOr(jsonBytes []byte) (*OrFormula, error) {
orRule, err := JsonExtract(jsonBytes, "Or")
if err != nil {
return nil, fmt.Errorf("failed to parse Or formula: %v", err)
}
orArray := make([]ChoiceRule, 0)
_, err = jsonparser.ArrayEach(orRule, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
rule, err := ParseRule(orRule)
if err != nil {
return
}
orArray = append(orArray, rule)
})
if err != nil {
return nil, fmt.Errorf("failed to parse Or formula: %v", err)
}

return &OrFormula{Or: orArray}, nil
}

func (o *OrFormula) Equals(cmp types.Comparable) bool {
otherOr, ok := cmp.(*OrFormula)
if !ok {
return false
}
for i, choiceRule := range o.Or {
if !choiceRule.Equals(otherOr.Or[i]) {
return false
}
}

return true
}

func (o *OrFormula) String() string {
str := "\t\t\t\t\t\tOr: ["

for i, choiceRule := range o.Or {
str += "\n\t\t\t\t\t\t\t" + choiceRule.String()
if i != len(o.Or)-1 {
str += ","
}
}

str += "]"

return str
}

func (o *OrFormula) GetFormulaType() string {
return "Or"
}

type NotFormula struct {
Not ChoiceRule
}

func ParseNot(jsonBytes []byte) (*NotFormula, error) {
notJson, err := JsonExtract(jsonBytes, "Not")
if err != nil {
return nil, fmt.Errorf("failed to parse Not formula: %v", err)
}
notRule, err := ParseRule(notJson)
if err != nil {
return nil, fmt.Errorf("failed to parse Not formula: %v", err)
}
return &NotFormula{Not: notRule}, nil
}

func (n *NotFormula) Equals(cmp types.Comparable) bool {
otherNot, ok := cmp.(*NotFormula)
if !ok {
return false
}
return otherNot.Not.Equals(n.Not)
}

func (n *NotFormula) String() string {
return fmt.Sprintf("\t\t\t\t\t\tNot: %s", n.Not.String())
}

func (n *NotFormula) GetFormulaType() string {
return "Not"
}
34 changes: 34 additions & 0 deletions internal/asl/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,40 @@ import (
"strconv"
)

func JsonHasKey(json []byte, key string) bool {
_, dataType, _, err := jsonparser.Get(json, key)
if err != nil || dataType == jsonparser.NotExist {
return false
}
return true
}

func JsonHasAllKeys(json []byte, keys ...string) bool {
for _, key := range keys {
if !JsonHasKey(json, key) {
return false
}
}
return true
}

func JsonHasOneKey(json []byte, keys ...string) bool {
for _, key := range keys {
if JsonHasKey(json, key) {
return true
}
}
return false
}

func JsonExtract(json []byte, key string) ([]byte, error) {
value, _, _, err := jsonparser.Get(json, key)
if err != nil {
return []byte(""), err
}
return value, nil
}

func JsonExtractString(json []byte, key string) (string, error) {
value, _, _, err := jsonparser.Get(json, key)
if err != nil {
Expand Down
Loading

0 comments on commit 0af4a06

Please sign in to comment.