641 lines
17 KiB
Go
641 lines
17 KiB
Go
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
|
|
}
|
|
}
|