blob: 909c788a2c785353e6a77781bc5a12322939724b [file] [log] [blame]
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
},
}
}