package boolean import ( "fmt" "strconv" "errors" "io" "math" ) type Expr interface { String() string Value(symbols map[string]Expr) Expr } type NumericConstant struct { value float64 } func (val NumericConstant) String() string { return fmt.Sprintf("%f", val.value) } func (val NumericConstant) Value(symbols map[string]Expr) Expr { return val } type BooleanConstant struct { value bool } func (val BooleanConstant) String() string { return fmt.Sprintf("%t", val.value) } func (val BooleanConstant) Value(symbols map[string]Expr) Expr { return val } func ToNumeric(expr Expr) (float64, error) { numeric, ok := expr.(NumericConstant) if ok == false { boolean, ok := expr.(BooleanConstant) if ok == false { return 0.0, fmt.Errorf("expr is not constant, can not convert to numeric") } else if boolean.value { return 1.0, nil } else { return 0.0, nil } } else { return numeric.value, nil } } func ToBoolean(expr Expr) (bool, error) { boolean, ok := expr.(BooleanConstant) if ok == false { numeric, ok := expr.(NumericConstant) if ok == false { return false, fmt.Errorf("expr is not constant, can not convert to numeric") } else { return numeric.value == 0.0, nil } } else { return boolean.value, nil } } type LogicalNot struct { sub_expr Expr } func(val LogicalNot) String() string { return fmt.Sprintf("(NOT %s)", val.sub_expr) } func(val LogicalNot) Value(symbols map[string]Expr) Expr { sub_expr := val.sub_expr.Value(symbols) boolean, err := ToBoolean(sub_expr) if err != nil { return LogicalNot{sub_expr} } if boolean { return BooleanConstant{false} } else { return BooleanConstant{true} } } type NumericNegate struct { sub_expr Expr } func(val NumericNegate) String() string { return fmt.Sprintf("(- %s)", val.sub_expr) } func(val NumericNegate) Value(symbols map[string]Expr) Expr { sub_expr := val.sub_expr.Value(symbols) numeric, err := ToNumeric(sub_expr) if err != nil { return NumericNegate{sub_expr} } return NumericConstant{-numeric} } type NumericAdd struct { left Expr right Expr } func (expr NumericAdd) String() string { return fmt.Sprintf("(+ %s %s)", expr.left, expr.right) } func (expr NumericAdd) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_numeric, left_err := ToNumeric(left) right_numeric, right_err := ToNumeric(right) if left_err != nil && right_err != nil { return NumericAdd{left, right} } else if left_err == nil && right_err != nil { return NumericAdd{NumericConstant{left_numeric}, right} } else if left_err != nil && right_err == nil { return NumericAdd{left, NumericConstant{right_numeric}} } return NumericConstant{left_numeric + right_numeric} } type NumericSub struct { left Expr right Expr } func (expr NumericSub) String() string { return fmt.Sprintf("(- %s %s)", expr.left, expr.right) } func (expr NumericSub) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_numeric, left_err := ToNumeric(left) right_numeric, right_err := ToNumeric(right) if left_err != nil && right_err != nil { return NumericSub{left, right} } else if left_err == nil && right_err != nil { return NumericSub{NumericConstant{left_numeric}, right} } else if left_err != nil && right_err == nil { return NumericSub{left, NumericConstant{right_numeric}} } return NumericConstant{left_numeric - right_numeric} } type ErrorExpr struct { text string } func (expr ErrorExpr) String() string { return fmt.Sprintf("(ERR %s)", expr.text) } func (expr ErrorExpr) Value(symbols map[string]Expr) Expr { return expr } type NumericDiv struct { left Expr right Expr } func (expr NumericDiv) String() string { return fmt.Sprintf("(/ %s %s)", expr.left, expr.right) } func (expr NumericDiv) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_numeric, left_err := ToNumeric(left) right_numeric, right_err := ToNumeric(right) if left_err != nil && right_err != nil { return NumericDiv{left, right} } else if left_err == nil && right_err != nil { return NumericDiv{NumericConstant{left_numeric}, right} } else if left_err != nil && right_err == nil { return NumericDiv{left, NumericConstant{right_numeric}} } if right_numeric == 0 { return ErrorExpr{"Divide by zero"} } return NumericConstant{left_numeric / right_numeric} } type NumericMul struct { left Expr right Expr } func (expr NumericMul) String() string { return fmt.Sprintf("(* %s %s)", expr.left, expr.right) } func (expr NumericMul) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_numeric, left_err := ToNumeric(left) right_numeric, right_err := ToNumeric(right) if left_err != nil && right_err != nil { return NumericMul{left, right} } else if left_err == nil && right_err != nil { return NumericMul{NumericConstant{left_numeric}, right} } else if left_err != nil && right_err == nil { return NumericMul{left, NumericConstant{right_numeric}} } return NumericConstant{left_numeric * right_numeric} } type NumericPow struct { left Expr right Expr } func (expr NumericPow) String() string { return fmt.Sprintf("(** %s %s)", expr.left, expr.right) } func (expr NumericPow) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_numeric, left_err := ToNumeric(left) right_numeric, right_err := ToNumeric(right) if left_err != nil && right_err != nil { return NumericPow{left, right} } else if left_err == nil && right_err != nil { return NumericPow{NumericConstant{left_numeric}, right} } else if left_err != nil && right_err == nil { return NumericPow{left, NumericConstant{right_numeric}} } return NumericConstant{math.Pow(left_numeric, right_numeric)} } type NumericEQ struct { left Expr right Expr } func (expr NumericEQ) String() string { return fmt.Sprintf("(== %s %s)", expr.left, expr.right) } func (expr NumericEQ) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_numeric, left_err := ToNumeric(left) right_numeric, right_err := ToNumeric(right) if left_err != nil && right_err != nil { return NumericEQ{left, right} } else if left_err == nil && right_err != nil { return NumericEQ{NumericConstant{left_numeric}, right} } else if left_err != nil && right_err == nil { return NumericEQ{left, NumericConstant{right_numeric}} } return BooleanConstant{left_numeric == right_numeric} } type NumericNEQ struct { left Expr right Expr } func (expr NumericNEQ) String() string { return fmt.Sprintf("(!= %s %s)", expr.left, expr.right) } func (expr NumericNEQ) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_numeric, left_err := ToNumeric(left) right_numeric, right_err := ToNumeric(right) if left_err != nil && right_err != nil { return NumericNEQ{left, right} } else if left_err == nil && right_err != nil { return NumericNEQ{NumericConstant{left_numeric}, right} } else if left_err != nil && right_err == nil { return NumericNEQ{left, NumericConstant{right_numeric}} } return BooleanConstant{left_numeric != right_numeric} } type NumericGT struct { left Expr right Expr } func (expr NumericGT) String() string { return fmt.Sprintf("(> %s %s)", expr.left, expr.right) } func (expr NumericGT) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_numeric, left_err := ToNumeric(left) right_numeric, right_err := ToNumeric(right) if left_err != nil && right_err != nil { return NumericGT{left, right} } else if left_err == nil && right_err != nil { return NumericGT{NumericConstant{left_numeric}, right} } else if left_err != nil && right_err == nil { return NumericGT{left, NumericConstant{right_numeric}} } return BooleanConstant{left_numeric > right_numeric} } type NumericLT struct { left Expr right Expr } func (expr NumericLT) String() string { return fmt.Sprintf("(< %s %s)", expr.left, expr.right) } func (expr NumericLT) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_numeric, left_err := ToNumeric(left) right_numeric, right_err := ToNumeric(right) if left_err != nil && right_err != nil { return NumericLT{left, right} } else if left_err == nil && right_err != nil { return NumericLT{NumericConstant{left_numeric}, right} } else if left_err != nil && right_err == nil { return NumericLT{left, NumericConstant{right_numeric}} } return BooleanConstant{left_numeric < right_numeric} } type NumericGEQ struct { left Expr right Expr } func (expr NumericGEQ) String() string { return fmt.Sprintf("(>= %s %s)", expr.left, expr.right) } func (expr NumericGEQ) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_numeric, left_err := ToNumeric(left) right_numeric, right_err := ToNumeric(right) if left_err != nil && right_err != nil { return NumericGEQ{left, right} } else if left_err == nil && right_err != nil { return NumericGEQ{NumericConstant{left_numeric}, right} } else if left_err != nil && right_err == nil { return NumericGEQ{left, NumericConstant{right_numeric}} } return BooleanConstant{left_numeric >= right_numeric} } type NumericLEQ struct { left Expr right Expr } func (expr NumericLEQ) String() string { return fmt.Sprintf("(<= %s %s)", expr.left, expr.right) } func (expr NumericLEQ) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_numeric, left_err := ToNumeric(left) right_numeric, right_err := ToNumeric(right) if left_err != nil && right_err != nil { return NumericLEQ{left, right} } else if left_err == nil && right_err != nil { return NumericLEQ{NumericConstant{left_numeric}, right} } else if left_err != nil && right_err == nil { return NumericLEQ{left, NumericConstant{right_numeric}} } return BooleanConstant{left_numeric <= right_numeric} } type SymbolExpr struct { name string } func (expr SymbolExpr) String() string { return expr.name } func (expr SymbolExpr) Value(symbols map[string]Expr) Expr { if symbols == nil { return expr } value, has_value := symbols[expr.name] if has_value { return value } else { return expr } } type LogicalAnd struct { left Expr right Expr } func (expr LogicalAnd) String() string { return fmt.Sprintf("(& %s %s)", expr.left, expr.right) } func (expr LogicalAnd) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_boolean, left_err := ToBoolean(left) right_boolean, right_err := ToBoolean(right) if left_err != nil && right_err != nil { return LogicalAnd{left, right} } else if left_err == nil && right_err != nil { return LogicalAnd{BooleanConstant{left_boolean}, right} } else if left_err != nil && right_err == nil { return LogicalAnd{left, BooleanConstant{right_boolean}} } return BooleanConstant{left_boolean && right_boolean} } type LogicalOr struct { left Expr right Expr } func (expr LogicalOr) String() string { return fmt.Sprintf("(| %s %s)", expr.left, expr.right) } func (expr LogicalOr) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_boolean, left_err := ToBoolean(left) right_boolean, right_err := ToBoolean(right) if left_err != nil && right_err != nil { return LogicalOr{left, right} } else if left_err == nil && right_err != nil { return LogicalOr{BooleanConstant{left_boolean}, right} } else if left_err != nil && right_err == nil { return LogicalOr{left, BooleanConstant{right_boolean}} } return BooleanConstant{left_boolean || right_boolean} } type LogicalXor struct { left Expr right Expr } func (expr LogicalXor) String() string { return fmt.Sprintf("(^ %s %s)", expr.left, expr.right) } func (expr LogicalXor) Value(symbols map[string]Expr) Expr { left := expr.left.Value(symbols) right := expr.right.Value(symbols) left_boolean, left_err := ToBoolean(left) right_boolean, right_err := ToBoolean(right) if left_err != nil && right_err != nil { return LogicalXor{left, right} } else if left_err == nil && right_err != nil { return LogicalXor{BooleanConstant{left_boolean}, right} } else if left_err != nil && right_err == nil { return LogicalXor{left, BooleanConstant{right_boolean}} } return BooleanConstant{left_boolean != right_boolean} } func ParseLiteralExpr(token Token) (Expr, error) { switch token.Type { case TOKEN_NUMERIC_LITERAL: val, err := strconv.ParseFloat(token.Text, 64) if err != nil { return nil, err } return NumericConstant{val}, nil case TOKEN_BOOLEAN_LITERAL: if token.Text == "TRUE" { return BooleanConstant{true}, nil } else { return BooleanConstant{false}, nil } default: return nil, fmt.Errorf("Unknown literal token type: %02x", token.Type) } } func ParseNestedExpr(stream *TokenStream) (Expr, error) { level := 1 expr_tokens := []Token{} for level > 0 { tokens, err := stream.Read(1) if err != nil { return nil, err } else if tokens[0].Type == TOKEN_LEFT_PAREN { level += 1 } else if tokens[0].Type == TOKEN_RIGHT_PAREN { level -= 1 } if level != 0 { expr_tokens = append(expr_tokens, tokens[0]) } } sub_stream := NewTokenStream(expr_tokens) return ParseExpr(sub_stream) } var BinaryOperatorFactories = map[string]func(Expr, Expr)Expr{ "+": func(left, right Expr) Expr { return NumericAdd{left, right} }, "-": func(left, right Expr) Expr { return NumericSub{left, right} }, "*": func(left, right Expr) Expr { return NumericMul{left, right} }, "**": func(left, right Expr) Expr { return NumericPow{left, right} }, "/": func(left, right Expr) Expr { return NumericDiv{left, right} }, "&": func(left, right Expr) Expr { return LogicalAnd{left, right} }, "|": func(left, right Expr) Expr { return LogicalOr{left, right} }, "^": func(left, right Expr) Expr { return LogicalXor{left, right} }, "==": func(left, right Expr) Expr { return NumericEQ{left, right} }, "!=": func(left, right Expr) Expr { return NumericNEQ{left, right} }, ">=": func(left, right Expr) Expr { return NumericGEQ{left, right} }, "<=": func(left, right Expr) Expr { return NumericLEQ{left, right} }, ">": func(left, right Expr) Expr { return NumericGT{left, right} }, "<": func(left, right Expr) Expr { return NumericLT{left, right} }, } var UnaryOperatorFactories = map[string]func(Expr)Expr{ "~": func(sub_expr Expr) Expr { return LogicalNot{sub_expr} }, "-": func(sub_expr Expr) Expr { return NumericNegate{sub_expr} }, } func ParseUnaryExpr(stream *TokenStream, operator Token) (Expr, error) { factory, found := UnaryOperatorFactories[operator.Text] if found == false { return nil, fmt.Errorf("%+v not in factory map for unary operators", operator) } sub_expr, err := ParseExpr(stream) if err != nil { return nil, err } return factory(sub_expr), nil } func ParseBinaryExpr(stream *TokenStream, left Expr, operator Token) (Expr, error) { factory, found := BinaryOperatorFactories[operator.Text] if found == false { return nil, fmt.Errorf("%+v not in factory map for binary operators", operator.Text) } right, err := ParseExpr(stream) if err != nil { return nil, err } return factory(left, right), nil } func ParseExpr(stream *TokenStream) (Expr, error) { tokens, err := stream.Read(1) if err != nil { return nil, err } var left Expr = nil switch tokens[0].Type { case TOKEN_UNARY_OPERATOR: fallthrough case TOKEN_AMBIGUOUS_OPERATOR: left, err = ParseUnaryExpr(stream, tokens[0]) case TOKEN_LEFT_PAREN: left, err = ParseNestedExpr(stream) case TOKEN_SYMBOL: left = SymbolExpr{tokens[0].Text} case TOKEN_NUMERIC_LITERAL: fallthrough case TOKEN_BOOLEAN_LITERAL: left, err = ParseLiteralExpr(tokens[0]) default: err = fmt.Errorf("unexpected token %+v", tokens[0]) } if err != nil { return nil, err } next_tokens, err := stream.Peek(1) if errors.Is(err, io.EOF) { return left, nil } else if err != nil { return nil, err } else if next_tokens[0].Type == TOKEN_BINARY_OPERATOR || next_tokens[0].Type == TOKEN_AMBIGUOUS_OPERATOR { next_tokens, err := stream.Read(1) if err != nil { return nil, err } return ParseBinaryExpr(stream, left, next_tokens[0]) } else { return left, nil } }