salix/expr.go

211 lines
6.8 KiB
Go
Raw Normal View History

2023-10-29 01:10:17 +00:00
package salix
import (
"reflect"
"strings"
2023-10-31 01:38:53 +00:00
"go.elara.ws/salix/ast"
2023-10-29 01:10:17 +00:00
)
func (t *Template) evalExpr(expr ast.Expr, local map[string]any) (any, error) {
val, err := t.getValue(expr.First, local)
2023-10-29 01:10:17 +00:00
if err != nil {
return nil, err
}
a := reflect.ValueOf(val)
for _, exprB := range expr.Rest {
val, err := t.getValue(exprB.First, local)
2023-10-29 01:10:17 +00:00
if err != nil {
return nil, err
}
b := reflect.ValueOf(val)
2023-10-31 00:16:45 +00:00
result, err := t.performOp(a, b, exprB.Operator)
if err != nil {
return nil, err
}
a = reflect.ValueOf(result)
2023-10-29 01:10:17 +00:00
}
return a.Interface(), nil
}
func (t *Template) performOp(a, b reflect.Value, op ast.Operator) (result any, err error) {
2023-10-31 00:16:45 +00:00
if op.Value == "in" {
a, b, err = handleIn(op, a, b)
if err != nil {
return nil, err
2024-06-07 02:06:45 +00:00
}
} else if !a.IsValid() || !b.IsValid() {
return handleNil(op, a, b)
2023-10-29 01:10:17 +00:00
} else if b.CanConvert(a.Type()) {
b = b.Convert(a.Type())
} else {
2023-12-18 00:55:13 +00:00
return nil, ast.PosError(op, "mismatched types in expression (%s and %s)", a.Type(), b.Type())
2023-10-29 01:10:17 +00:00
}
2023-10-31 00:16:45 +00:00
switch op.Value {
2023-10-29 01:10:17 +00:00
case "==":
2023-10-31 00:16:45 +00:00
return a.Equal(b), nil
case "!=":
return !a.Equal(b), nil
2023-10-29 01:10:17 +00:00
case "&&":
if a.Kind() != reflect.Bool || b.Kind() != reflect.Bool {
2023-12-18 00:55:13 +00:00
return nil, ast.PosError(op, "logical operations may only be performed on boolean values")
2023-10-29 01:10:17 +00:00
}
2023-10-31 00:16:45 +00:00
return a.Bool() && b.Bool(), nil
2023-10-29 01:10:17 +00:00
case "||":
if a.Kind() != reflect.Bool || b.Kind() != reflect.Bool {
2023-12-18 00:55:13 +00:00
return nil, ast.PosError(op, "logical operations may only be performed on boolean values")
2023-10-29 01:10:17 +00:00
}
2023-10-31 00:16:45 +00:00
return a.Bool() || b.Bool(), nil
2023-10-29 01:10:17 +00:00
case ">=":
switch a.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2023-10-31 00:16:45 +00:00
return a.Int() >= b.Int(), nil
2023-10-29 01:10:17 +00:00
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2023-10-31 00:16:45 +00:00
return a.Uint() >= b.Uint(), nil
2023-10-29 01:10:17 +00:00
case reflect.Float64, reflect.Float32:
2023-10-31 00:16:45 +00:00
return a.Float() >= b.Float(), nil
2023-10-29 01:10:17 +00:00
}
case "<=":
switch a.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2023-10-31 00:16:45 +00:00
return a.Int() <= b.Int(), nil
2023-10-29 01:10:17 +00:00
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2023-10-31 00:16:45 +00:00
return a.Uint() <= b.Uint(), nil
2023-10-29 01:10:17 +00:00
case reflect.Float64, reflect.Float32:
2023-10-31 00:16:45 +00:00
return a.Float() <= b.Float(), nil
2023-10-29 01:10:17 +00:00
}
case ">":
switch a.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2023-10-31 00:16:45 +00:00
return a.Int() > b.Int(), nil
2023-10-29 01:10:17 +00:00
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2023-10-31 00:16:45 +00:00
return a.Uint() > b.Uint(), nil
2023-10-29 01:10:17 +00:00
case reflect.Float64, reflect.Float32:
2023-10-31 00:16:45 +00:00
return a.Float() > b.Float(), nil
2023-10-29 01:10:17 +00:00
}
case "<":
switch a.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2023-10-31 00:16:45 +00:00
return a.Int() < b.Int(), nil
2023-10-29 01:10:17 +00:00
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2023-10-31 00:16:45 +00:00
return a.Uint() < b.Uint(), nil
2023-10-29 01:10:17 +00:00
case reflect.Float64, reflect.Float32:
2023-10-31 00:16:45 +00:00
return a.Float() < b.Float(), nil
2023-10-29 01:10:17 +00:00
}
case "+":
switch a.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2023-10-31 00:16:45 +00:00
return a.Int() + b.Int(), nil
2023-10-29 01:10:17 +00:00
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2023-10-31 00:16:45 +00:00
return a.Uint() + b.Uint(), nil
2023-10-29 01:10:17 +00:00
case reflect.Float64, reflect.Float32:
2023-10-31 00:16:45 +00:00
return a.Float() + b.Float(), nil
2023-10-31 22:49:00 +00:00
case reflect.String:
return a.String() + b.String(), nil
2023-10-29 01:10:17 +00:00
}
case "-":
switch a.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2023-10-31 00:16:45 +00:00
return a.Int() - b.Int(), nil
2023-10-29 01:10:17 +00:00
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2023-10-31 00:16:45 +00:00
return a.Uint() - b.Uint(), nil
2023-10-29 01:10:17 +00:00
case reflect.Float64, reflect.Float32:
2023-10-31 00:16:45 +00:00
return a.Float() - b.Float(), nil
2023-10-29 01:10:17 +00:00
}
case "*":
switch a.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2023-10-31 00:16:45 +00:00
return a.Int() * b.Int(), nil
2023-10-29 01:10:17 +00:00
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2023-10-31 00:16:45 +00:00
return a.Uint() * b.Uint(), nil
2023-10-29 01:10:17 +00:00
case reflect.Float64, reflect.Float32:
2023-10-31 00:16:45 +00:00
return a.Float() * b.Float(), nil
2023-10-29 01:10:17 +00:00
}
case "/":
switch a.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2023-10-31 00:16:45 +00:00
return a.Int() / b.Int(), nil
2023-10-29 01:10:17 +00:00
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2023-10-31 00:16:45 +00:00
return a.Uint() / b.Uint(), nil
2023-10-29 01:10:17 +00:00
case reflect.Float64, reflect.Float32:
2023-10-31 00:16:45 +00:00
return a.Float() / b.Float(), nil
2023-10-29 01:10:17 +00:00
}
2023-10-30 23:59:37 +00:00
case "%":
switch a.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
2023-10-31 00:16:45 +00:00
return a.Int() % b.Int(), nil
2023-10-30 23:59:37 +00:00
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
2023-10-31 00:16:45 +00:00
return a.Uint() % b.Uint(), nil
2023-10-30 23:59:37 +00:00
case reflect.Float64, reflect.Float32:
2023-12-18 00:55:13 +00:00
return nil, ast.PosError(op, "modulus operation cannot be performed on floats")
2023-10-31 00:16:45 +00:00
}
2023-10-29 01:10:17 +00:00
case "in":
if a.Kind() == reflect.String && b.Kind() == reflect.String {
2023-10-31 00:16:45 +00:00
return strings.Contains(b.String(), a.String()), nil
2023-11-04 01:33:41 +00:00
} else if b.Kind() == reflect.Map {
return b.MapIndex(a).IsValid(), nil
} else if b.Kind() == reflect.Slice || b.Kind() == reflect.Array {
2023-10-29 01:10:17 +00:00
for i := 0; i < b.Len(); i++ {
if a.Equal(b.Index(i)) {
2023-10-31 00:16:45 +00:00
return true, nil
2023-10-29 01:10:17 +00:00
}
}
2023-10-31 00:16:45 +00:00
return false, nil
2023-10-29 01:10:17 +00:00
}
}
return false, ast.PosError(op, "unknown operator: %q", op.Value)
2023-10-29 01:10:17 +00:00
}
func handleIn(op ast.Operator, a, b reflect.Value) (c, d reflect.Value, err error) {
switch b.Kind() {
case reflect.Slice, reflect.Array:
if a.CanConvert(b.Type().Elem()) {
a = a.Convert(b.Type().Elem())
} else {
return a, b, ast.PosError(op, "mismatched types in expression (%s and %s)", a.Type(), b.Type())
}
case reflect.Map:
if a.CanConvert(b.Type().Key()) {
a = a.Convert(b.Type().Key())
} else {
return a, b, ast.PosError(op, "mismatched types in expression (%s and %s)", a.Type(), b.Type())
}
case reflect.String:
if a.Kind() != reflect.String {
return a, b, ast.PosError(op, "mismatched types in expression (%s and %s)", a.Type(), b.Type())
}
default:
return a, b, ast.PosError(op, "the in operator can only be used on strings, arrays, and slices (got %s and %s)", a.Type(), b.Type())
}
return a, b, nil
}
func handleNil(op ast.Operator, a, b reflect.Value) (any, error) {
if !a.IsValid() && !b.IsValid() {
return true, nil
} else if !a.IsValid() {
return nil, ast.PosError(op, "nil must be on the right side of an expression")
} else if !b.IsValid() {
if op.Value != "==" && op.Value != "!=" {
return nil, ast.PosError(op, "invalid operator for nil value (expected == or !=, got %s)", op.Value)
}
switch a.Kind() {
case reflect.Chan, reflect.Slice, reflect.Map, reflect.Func, reflect.Interface, reflect.Pointer:
if op.Value == "==" {
return a.IsNil(), nil
} else {
return !a.IsNil(), nil
}
default:
return nil, ast.PosError(op, "values of type %s cannot be compared against nil", a.Type())
}
}
return nil, nil
}