github.com/hashicorp/hcl/v2@v2.20.0/hclsyntax/expression_ops.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package hclsyntax
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/hashicorp/hcl/v2"
    10  	"github.com/zclconf/go-cty/cty"
    11  	"github.com/zclconf/go-cty/cty/convert"
    12  	"github.com/zclconf/go-cty/cty/function"
    13  	"github.com/zclconf/go-cty/cty/function/stdlib"
    14  )
    15  
    16  type Operation struct {
    17  	Impl function.Function
    18  	Type cty.Type
    19  }
    20  
    21  var (
    22  	OpLogicalOr = &Operation{
    23  		Impl: stdlib.OrFunc,
    24  		Type: cty.Bool,
    25  	}
    26  	OpLogicalAnd = &Operation{
    27  		Impl: stdlib.AndFunc,
    28  		Type: cty.Bool,
    29  	}
    30  	OpLogicalNot = &Operation{
    31  		Impl: stdlib.NotFunc,
    32  		Type: cty.Bool,
    33  	}
    34  
    35  	OpEqual = &Operation{
    36  		Impl: stdlib.EqualFunc,
    37  		Type: cty.Bool,
    38  	}
    39  	OpNotEqual = &Operation{
    40  		Impl: stdlib.NotEqualFunc,
    41  		Type: cty.Bool,
    42  	}
    43  
    44  	OpGreaterThan = &Operation{
    45  		Impl: stdlib.GreaterThanFunc,
    46  		Type: cty.Bool,
    47  	}
    48  	OpGreaterThanOrEqual = &Operation{
    49  		Impl: stdlib.GreaterThanOrEqualToFunc,
    50  		Type: cty.Bool,
    51  	}
    52  	OpLessThan = &Operation{
    53  		Impl: stdlib.LessThanFunc,
    54  		Type: cty.Bool,
    55  	}
    56  	OpLessThanOrEqual = &Operation{
    57  		Impl: stdlib.LessThanOrEqualToFunc,
    58  		Type: cty.Bool,
    59  	}
    60  
    61  	OpAdd = &Operation{
    62  		Impl: stdlib.AddFunc,
    63  		Type: cty.Number,
    64  	}
    65  	OpSubtract = &Operation{
    66  		Impl: stdlib.SubtractFunc,
    67  		Type: cty.Number,
    68  	}
    69  	OpMultiply = &Operation{
    70  		Impl: stdlib.MultiplyFunc,
    71  		Type: cty.Number,
    72  	}
    73  	OpDivide = &Operation{
    74  		Impl: stdlib.DivideFunc,
    75  		Type: cty.Number,
    76  	}
    77  	OpModulo = &Operation{
    78  		Impl: stdlib.ModuloFunc,
    79  		Type: cty.Number,
    80  	}
    81  	OpNegate = &Operation{
    82  		Impl: stdlib.NegateFunc,
    83  		Type: cty.Number,
    84  	}
    85  )
    86  
    87  var binaryOps []map[TokenType]*Operation
    88  
    89  func init() {
    90  	// This operation table maps from the operator's token type
    91  	// to the AST operation type. All expressions produced from
    92  	// binary operators are BinaryOp nodes.
    93  	//
    94  	// Binary operator groups are listed in order of precedence, with
    95  	// the *lowest* precedence first. Operators within the same group
    96  	// have left-to-right associativity.
    97  	binaryOps = []map[TokenType]*Operation{
    98  		{
    99  			TokenOr: OpLogicalOr,
   100  		},
   101  		{
   102  			TokenAnd: OpLogicalAnd,
   103  		},
   104  		{
   105  			TokenEqualOp:  OpEqual,
   106  			TokenNotEqual: OpNotEqual,
   107  		},
   108  		{
   109  			TokenGreaterThan:   OpGreaterThan,
   110  			TokenGreaterThanEq: OpGreaterThanOrEqual,
   111  			TokenLessThan:      OpLessThan,
   112  			TokenLessThanEq:    OpLessThanOrEqual,
   113  		},
   114  		{
   115  			TokenPlus:  OpAdd,
   116  			TokenMinus: OpSubtract,
   117  		},
   118  		{
   119  			TokenStar:    OpMultiply,
   120  			TokenSlash:   OpDivide,
   121  			TokenPercent: OpModulo,
   122  		},
   123  	}
   124  }
   125  
   126  type BinaryOpExpr struct {
   127  	LHS Expression
   128  	Op  *Operation
   129  	RHS Expression
   130  
   131  	SrcRange hcl.Range
   132  }
   133  
   134  func (e *BinaryOpExpr) walkChildNodes(w internalWalkFunc) {
   135  	w(e.LHS)
   136  	w(e.RHS)
   137  }
   138  
   139  func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
   140  	impl := e.Op.Impl // assumed to be a function taking exactly two arguments
   141  	params := impl.Params()
   142  	lhsParam := params[0]
   143  	rhsParam := params[1]
   144  
   145  	var diags hcl.Diagnostics
   146  
   147  	givenLHSVal, lhsDiags := e.LHS.Value(ctx)
   148  	givenRHSVal, rhsDiags := e.RHS.Value(ctx)
   149  	diags = append(diags, lhsDiags...)
   150  	diags = append(diags, rhsDiags...)
   151  
   152  	lhsVal, err := convert.Convert(givenLHSVal, lhsParam.Type)
   153  	if err != nil {
   154  		diags = append(diags, &hcl.Diagnostic{
   155  			Severity:    hcl.DiagError,
   156  			Summary:     "Invalid operand",
   157  			Detail:      fmt.Sprintf("Unsuitable value for left operand: %s.", err),
   158  			Subject:     e.LHS.Range().Ptr(),
   159  			Context:     &e.SrcRange,
   160  			Expression:  e.LHS,
   161  			EvalContext: ctx,
   162  		})
   163  	}
   164  	rhsVal, err := convert.Convert(givenRHSVal, rhsParam.Type)
   165  	if err != nil {
   166  		diags = append(diags, &hcl.Diagnostic{
   167  			Severity:    hcl.DiagError,
   168  			Summary:     "Invalid operand",
   169  			Detail:      fmt.Sprintf("Unsuitable value for right operand: %s.", err),
   170  			Subject:     e.RHS.Range().Ptr(),
   171  			Context:     &e.SrcRange,
   172  			Expression:  e.RHS,
   173  			EvalContext: ctx,
   174  		})
   175  	}
   176  
   177  	if diags.HasErrors() {
   178  		// Don't actually try the call if we have errors already, since the
   179  		// this will probably just produce a confusing duplicative diagnostic.
   180  		return cty.UnknownVal(e.Op.Type), diags
   181  	}
   182  
   183  	args := []cty.Value{lhsVal, rhsVal}
   184  	result, err := impl.Call(args)
   185  	if err != nil {
   186  		diags = append(diags, &hcl.Diagnostic{
   187  			// FIXME: This diagnostic is useless.
   188  			Severity:    hcl.DiagError,
   189  			Summary:     "Operation failed",
   190  			Detail:      fmt.Sprintf("Error during operation: %s.", err),
   191  			Subject:     &e.SrcRange,
   192  			Expression:  e,
   193  			EvalContext: ctx,
   194  		})
   195  		return cty.UnknownVal(e.Op.Type), diags
   196  	}
   197  
   198  	return result, diags
   199  }
   200  
   201  func (e *BinaryOpExpr) Range() hcl.Range {
   202  	return e.SrcRange
   203  }
   204  
   205  func (e *BinaryOpExpr) StartRange() hcl.Range {
   206  	return e.LHS.StartRange()
   207  }
   208  
   209  type UnaryOpExpr struct {
   210  	Op  *Operation
   211  	Val Expression
   212  
   213  	SrcRange    hcl.Range
   214  	SymbolRange hcl.Range
   215  }
   216  
   217  func (e *UnaryOpExpr) walkChildNodes(w internalWalkFunc) {
   218  	w(e.Val)
   219  }
   220  
   221  func (e *UnaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
   222  	impl := e.Op.Impl // assumed to be a function taking exactly one argument
   223  	params := impl.Params()
   224  	param := params[0]
   225  
   226  	givenVal, diags := e.Val.Value(ctx)
   227  
   228  	val, err := convert.Convert(givenVal, param.Type)
   229  	if err != nil {
   230  		diags = append(diags, &hcl.Diagnostic{
   231  			Severity:    hcl.DiagError,
   232  			Summary:     "Invalid operand",
   233  			Detail:      fmt.Sprintf("Unsuitable value for unary operand: %s.", err),
   234  			Subject:     e.Val.Range().Ptr(),
   235  			Context:     &e.SrcRange,
   236  			Expression:  e.Val,
   237  			EvalContext: ctx,
   238  		})
   239  	}
   240  
   241  	if diags.HasErrors() {
   242  		// Don't actually try the call if we have errors already, since the
   243  		// this will probably just produce a confusing duplicative diagnostic.
   244  		return cty.UnknownVal(e.Op.Type), diags
   245  	}
   246  
   247  	args := []cty.Value{val}
   248  	result, err := impl.Call(args)
   249  	if err != nil {
   250  		diags = append(diags, &hcl.Diagnostic{
   251  			// FIXME: This diagnostic is useless.
   252  			Severity:    hcl.DiagError,
   253  			Summary:     "Operation failed",
   254  			Detail:      fmt.Sprintf("Error during operation: %s.", err),
   255  			Subject:     &e.SrcRange,
   256  			Expression:  e,
   257  			EvalContext: ctx,
   258  		})
   259  		return cty.UnknownVal(e.Op.Type), diags
   260  	}
   261  
   262  	return result, diags
   263  }
   264  
   265  func (e *UnaryOpExpr) Range() hcl.Range {
   266  	return e.SrcRange
   267  }
   268  
   269  func (e *UnaryOpExpr) StartRange() hcl.Range {
   270  	return e.SymbolRange
   271  }