332 lines
8.2 KiB
Go
332 lines
8.2 KiB
Go
|
package hil
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"strconv"
|
||
|
|
||
|
"github.com/hashicorp/hil/ast"
|
||
|
)
|
||
|
|
||
|
// NOTE: All builtins are tested in engine_test.go
|
||
|
|
||
|
func registerBuiltins(scope *ast.BasicScope) *ast.BasicScope {
|
||
|
if scope == nil {
|
||
|
scope = new(ast.BasicScope)
|
||
|
}
|
||
|
if scope.FuncMap == nil {
|
||
|
scope.FuncMap = make(map[string]ast.Function)
|
||
|
}
|
||
|
|
||
|
// Implicit conversions
|
||
|
scope.FuncMap["__builtin_BoolToString"] = builtinBoolToString()
|
||
|
scope.FuncMap["__builtin_FloatToInt"] = builtinFloatToInt()
|
||
|
scope.FuncMap["__builtin_FloatToString"] = builtinFloatToString()
|
||
|
scope.FuncMap["__builtin_IntToFloat"] = builtinIntToFloat()
|
||
|
scope.FuncMap["__builtin_IntToString"] = builtinIntToString()
|
||
|
scope.FuncMap["__builtin_StringToInt"] = builtinStringToInt()
|
||
|
scope.FuncMap["__builtin_StringToFloat"] = builtinStringToFloat()
|
||
|
scope.FuncMap["__builtin_StringToBool"] = builtinStringToBool()
|
||
|
|
||
|
// Math operations
|
||
|
scope.FuncMap["__builtin_IntMath"] = builtinIntMath()
|
||
|
scope.FuncMap["__builtin_FloatMath"] = builtinFloatMath()
|
||
|
scope.FuncMap["__builtin_BoolCompare"] = builtinBoolCompare()
|
||
|
scope.FuncMap["__builtin_FloatCompare"] = builtinFloatCompare()
|
||
|
scope.FuncMap["__builtin_IntCompare"] = builtinIntCompare()
|
||
|
scope.FuncMap["__builtin_StringCompare"] = builtinStringCompare()
|
||
|
scope.FuncMap["__builtin_Logical"] = builtinLogical()
|
||
|
return scope
|
||
|
}
|
||
|
|
||
|
func builtinFloatMath() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeInt},
|
||
|
Variadic: true,
|
||
|
VariadicType: ast.TypeFloat,
|
||
|
ReturnType: ast.TypeFloat,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
op := args[0].(ast.ArithmeticOp)
|
||
|
result := args[1].(float64)
|
||
|
for _, raw := range args[2:] {
|
||
|
arg := raw.(float64)
|
||
|
switch op {
|
||
|
case ast.ArithmeticOpAdd:
|
||
|
result += arg
|
||
|
case ast.ArithmeticOpSub:
|
||
|
result -= arg
|
||
|
case ast.ArithmeticOpMul:
|
||
|
result *= arg
|
||
|
case ast.ArithmeticOpDiv:
|
||
|
result /= arg
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result, nil
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinIntMath() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeInt},
|
||
|
Variadic: true,
|
||
|
VariadicType: ast.TypeInt,
|
||
|
ReturnType: ast.TypeInt,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
op := args[0].(ast.ArithmeticOp)
|
||
|
result := args[1].(int)
|
||
|
for _, raw := range args[2:] {
|
||
|
arg := raw.(int)
|
||
|
switch op {
|
||
|
case ast.ArithmeticOpAdd:
|
||
|
result += arg
|
||
|
case ast.ArithmeticOpSub:
|
||
|
result -= arg
|
||
|
case ast.ArithmeticOpMul:
|
||
|
result *= arg
|
||
|
case ast.ArithmeticOpDiv:
|
||
|
if arg == 0 {
|
||
|
return nil, errors.New("divide by zero")
|
||
|
}
|
||
|
|
||
|
result /= arg
|
||
|
case ast.ArithmeticOpMod:
|
||
|
if arg == 0 {
|
||
|
return nil, errors.New("divide by zero")
|
||
|
}
|
||
|
|
||
|
result = result % arg
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result, nil
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinBoolCompare() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeInt, ast.TypeBool, ast.TypeBool},
|
||
|
Variadic: false,
|
||
|
ReturnType: ast.TypeBool,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
op := args[0].(ast.ArithmeticOp)
|
||
|
lhs := args[1].(bool)
|
||
|
rhs := args[2].(bool)
|
||
|
|
||
|
switch op {
|
||
|
case ast.ArithmeticOpEqual:
|
||
|
return lhs == rhs, nil
|
||
|
case ast.ArithmeticOpNotEqual:
|
||
|
return lhs != rhs, nil
|
||
|
default:
|
||
|
return nil, errors.New("invalid comparison operation")
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinFloatCompare() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeInt, ast.TypeFloat, ast.TypeFloat},
|
||
|
Variadic: false,
|
||
|
ReturnType: ast.TypeBool,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
op := args[0].(ast.ArithmeticOp)
|
||
|
lhs := args[1].(float64)
|
||
|
rhs := args[2].(float64)
|
||
|
|
||
|
switch op {
|
||
|
case ast.ArithmeticOpEqual:
|
||
|
return lhs == rhs, nil
|
||
|
case ast.ArithmeticOpNotEqual:
|
||
|
return lhs != rhs, nil
|
||
|
case ast.ArithmeticOpLessThan:
|
||
|
return lhs < rhs, nil
|
||
|
case ast.ArithmeticOpLessThanOrEqual:
|
||
|
return lhs <= rhs, nil
|
||
|
case ast.ArithmeticOpGreaterThan:
|
||
|
return lhs > rhs, nil
|
||
|
case ast.ArithmeticOpGreaterThanOrEqual:
|
||
|
return lhs >= rhs, nil
|
||
|
default:
|
||
|
return nil, errors.New("invalid comparison operation")
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinIntCompare() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeInt, ast.TypeInt, ast.TypeInt},
|
||
|
Variadic: false,
|
||
|
ReturnType: ast.TypeBool,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
op := args[0].(ast.ArithmeticOp)
|
||
|
lhs := args[1].(int)
|
||
|
rhs := args[2].(int)
|
||
|
|
||
|
switch op {
|
||
|
case ast.ArithmeticOpEqual:
|
||
|
return lhs == rhs, nil
|
||
|
case ast.ArithmeticOpNotEqual:
|
||
|
return lhs != rhs, nil
|
||
|
case ast.ArithmeticOpLessThan:
|
||
|
return lhs < rhs, nil
|
||
|
case ast.ArithmeticOpLessThanOrEqual:
|
||
|
return lhs <= rhs, nil
|
||
|
case ast.ArithmeticOpGreaterThan:
|
||
|
return lhs > rhs, nil
|
||
|
case ast.ArithmeticOpGreaterThanOrEqual:
|
||
|
return lhs >= rhs, nil
|
||
|
default:
|
||
|
return nil, errors.New("invalid comparison operation")
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinStringCompare() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeInt, ast.TypeString, ast.TypeString},
|
||
|
Variadic: false,
|
||
|
ReturnType: ast.TypeBool,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
op := args[0].(ast.ArithmeticOp)
|
||
|
lhs := args[1].(string)
|
||
|
rhs := args[2].(string)
|
||
|
|
||
|
switch op {
|
||
|
case ast.ArithmeticOpEqual:
|
||
|
return lhs == rhs, nil
|
||
|
case ast.ArithmeticOpNotEqual:
|
||
|
return lhs != rhs, nil
|
||
|
default:
|
||
|
return nil, errors.New("invalid comparison operation")
|
||
|
}
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinLogical() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeInt},
|
||
|
Variadic: true,
|
||
|
VariadicType: ast.TypeBool,
|
||
|
ReturnType: ast.TypeBool,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
op := args[0].(ast.ArithmeticOp)
|
||
|
result := args[1].(bool)
|
||
|
for _, raw := range args[2:] {
|
||
|
arg := raw.(bool)
|
||
|
switch op {
|
||
|
case ast.ArithmeticOpLogicalOr:
|
||
|
result = result || arg
|
||
|
case ast.ArithmeticOpLogicalAnd:
|
||
|
result = result && arg
|
||
|
default:
|
||
|
return nil, errors.New("invalid logical operator")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result, nil
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinFloatToInt() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeFloat},
|
||
|
ReturnType: ast.TypeInt,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
return int(args[0].(float64)), nil
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinFloatToString() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeFloat},
|
||
|
ReturnType: ast.TypeString,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
return strconv.FormatFloat(
|
||
|
args[0].(float64), 'g', -1, 64), nil
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinIntToFloat() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeInt},
|
||
|
ReturnType: ast.TypeFloat,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
return float64(args[0].(int)), nil
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinIntToString() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeInt},
|
||
|
ReturnType: ast.TypeString,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
return strconv.FormatInt(int64(args[0].(int)), 10), nil
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinStringToInt() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeInt},
|
||
|
ReturnType: ast.TypeString,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
v, err := strconv.ParseInt(args[0].(string), 0, 0)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return int(v), nil
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinStringToFloat() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeString},
|
||
|
ReturnType: ast.TypeFloat,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
v, err := strconv.ParseFloat(args[0].(string), 64)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return v, nil
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinBoolToString() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeBool},
|
||
|
ReturnType: ast.TypeString,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
return strconv.FormatBool(args[0].(bool)), nil
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func builtinStringToBool() ast.Function {
|
||
|
return ast.Function{
|
||
|
ArgTypes: []ast.Type{ast.TypeString},
|
||
|
ReturnType: ast.TypeBool,
|
||
|
Callback: func(args []interface{}) (interface{}, error) {
|
||
|
v, err := strconv.ParseBool(args[0].(string))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return v, nil
|
||
|
},
|
||
|
}
|
||
|
}
|