Skip to content

Commit

Permalink
syntax: add tests, s/list/array
Browse files Browse the repository at this point in the history
- Add tests for the syntax package
- Replace the term 'list' with 'array' in most occurrences
  • Loading branch information
pgavlin committed Oct 18, 2023
1 parent bb49f42 commit 593dcae
Show file tree
Hide file tree
Showing 33 changed files with 2,233 additions and 81 deletions.
14 changes: 7 additions & 7 deletions ast/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,21 @@ type nonNilDecl interface {
defaultValue() interface{}
}

type ListDecl[T Node] struct {
type ArrayDecl[T Node] struct {
declNode

Elements []T
}

func (d *ListDecl[T]) GetElements() []T {
func (d *ArrayDecl[T]) GetElements() []T {
if d == nil {
return nil
}
return d.Elements
}

func (d *ListDecl[T]) parse(name string, node syntax.Node) syntax.Diagnostics {
list, ok := node.(*syntax.ListNode)
func (d *ArrayDecl[T]) parse(name string, node syntax.Node) syntax.Diagnostics {
list, ok := node.(*syntax.ArrayNode)
if !ok {
return syntax.Diagnostics{syntax.NodeError(node, fmt.Sprintf("%v must be a list", name), "")}
}
Expand Down Expand Up @@ -181,7 +181,7 @@ func (d *ImportDecl) parse(name string, node syntax.Node) syntax.Diagnostics {
}
}

type ImportListDecl = *ListDecl[*ImportDecl]
type ImportListDecl = *ArrayDecl[*ImportDecl]
type PropertyMapEntry = MapEntry[Expr]
type PropertyMapDecl = *MapDecl[Expr]

Expand Down Expand Up @@ -372,8 +372,8 @@ func exprFieldTypeMismatchError(name string, expected interface{}, actual Expr)
typeName = "a symbol"
case *InterpolateExpr:
typeName = "an interpolated string"
case *ListExpr:
typeName = "a list"
case *ArrayExpr:
typeName = "an array"
case *ObjectExpr:
typeName = "an object"
case BuiltinExpr:
Expand Down
30 changes: 15 additions & 15 deletions ast/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,24 +226,24 @@ func (n *SymbolExpr) String() string {
return fmt.Sprintf("${%v}", n.Property)
}

// A ListExpr represents a list of expressions.
type ListExpr struct {
// A ArrayExpr represents a list of expressions.
type ArrayExpr struct {
exprNode

Elements []Expr
}

// ListSyntax creates a new list expression with the given elements and associated syntax.
func ListSyntax(node *syntax.ListNode, elements ...Expr) *ListExpr {
return &ListExpr{
// ArraySyntax creates a new list expression with the given elements and associated syntax.
func ArraySyntax(node *syntax.ArrayNode, elements ...Expr) *ArrayExpr {
return &ArrayExpr{
exprNode: expr(node),
Elements: elements,
}
}

// List creates a new list expression with the given elements.
func List(elements ...Expr) *ListExpr {
return ListSyntax(syntax.List(), elements...)
// Array creates a new list expression with the given elements.
func Array(elements ...Expr) *ArrayExpr {
return ArraySyntax(syntax.Array(), elements...)
}

// An ObjectExpr represents an object.
Expand Down Expand Up @@ -278,7 +278,7 @@ func Object(entries ...ObjectProperty) *ObjectExpr {
// The syntax tree is parsed using the following rules:
//
// - *syntax.{Null,Boolean,Number}Node is parsed as a *{Null,Boolean,Number}Expr.
// - *syntax.ListNode is parsed as a *ListExpr.
// - *syntax.ArrayNode is parsed as a *ArrayExpr.
// - *syntax.StringNode is parsed as an *InterpolateExpr, a *SymbolExpr, or a *StringExpr. The node's literal is first
// parsed as an interpolated string. If the result contains a single property access with no surrounding text, (i.e.
// the string is of the form "${resource.property}", it is treated as a symbol. If the result contains no property
Expand Down Expand Up @@ -316,7 +316,7 @@ func ParseExpr(node syntax.Node) (Expr, syntax.Diagnostics) {
}

return interpolate, diags
case *syntax.ListNode:
case *syntax.ArrayNode:
var diags syntax.Diagnostics

elements := make([]Expr, node.Len())
Expand All @@ -325,7 +325,7 @@ func ParseExpr(node syntax.Node) (Expr, syntax.Diagnostics) {
diags.Extend(xdiags...)
elements[i] = x
}
return ListSyntax(node, elements...), diags
return ArraySyntax(node, elements...), diags
case *syntax.ObjectNode:

var diags syntax.Diagnostics
Expand Down Expand Up @@ -487,18 +487,18 @@ type JoinExpr struct {
Values Expr
}

func JoinSyntax(node *syntax.ObjectNode, name *StringExpr, args *ListExpr, delimiter Expr, values Expr) *JoinExpr {
func JoinSyntax(node *syntax.ObjectNode, name *StringExpr, args *ArrayExpr, delimiter Expr, values Expr) *JoinExpr {
return &JoinExpr{
builtinNode: builtin(node, name, args),
Delimiter: delimiter,
Values: values,
}
}

func Join(delimiter Expr, values *ListExpr) *JoinExpr {
func Join(delimiter Expr, values *ArrayExpr) *JoinExpr {
name := String("fn::join")
return &JoinExpr{
builtinNode: builtin(nil, name, List(delimiter, values)),
builtinNode: builtin(nil, name, Array(delimiter, values)),
Delimiter: delimiter,
Values: values,
}
Expand Down Expand Up @@ -670,7 +670,7 @@ func parseShortOpen(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr,
}

func parseJoin(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, syntax.Diagnostics) {
list, ok := args.(*ListExpr)
list, ok := args.(*ArrayExpr)
if !ok || len(list.Elements) != 2 {
return nil, syntax.Diagnostics{ExprError(args, "the argument to fn::join must be a two-valued list", "")}
}
Expand Down
16 changes: 8 additions & 8 deletions eval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func (e *evalContext) errorf(expr ast.Expr, format string, a ...any) {
// - SecretExpr -> secretExpr
// - ToBase64Expr -> toBase64Expr
// - ToJSONExpr -> toJSONExpr
// - ListExpr -> listExpr
// - ArrayExpr -> arrayExpr
// - ObjectExpr -> objectExpr
func (e *evalContext) declare(path string, x ast.Expr, base *value) *expr {
switch x := x.(type) {
Expand Down Expand Up @@ -260,12 +260,12 @@ func (e *evalContext) declare(path string, x ast.Expr, base *value) *expr {
case *ast.ToStringExpr:
repr := &toStringExpr{node: x, value: e.declare("", x.Value, nil)}
return newExpr(path, repr, schema.String().Schema(), base)
case *ast.ListExpr:
case *ast.ArrayExpr:
elements := make([]*expr, len(x.Elements))
for i, x := range x.Elements {
elements[i] = e.declare(fmt.Sprintf("%v[%d]", path, i), x, nil)
}
repr := &listExpr{node: x, elements: elements}
repr := &arrayExpr{node: x, elements: elements}
return newExpr(path, repr, schema.Array().Items(schema.Always()).Schema(), base)
case *ast.ObjectExpr:
properties := make(map[string]*expr, len(x.Entries))
Expand Down Expand Up @@ -449,8 +449,8 @@ func (e *evalContext) evaluateExpr(x *expr) *value {
val = e.evaluateBuiltinToJSON(x, repr)
case *toStringExpr:
val = e.evaluateBuiltinToString(x, repr)
case *listExpr:
val = e.evaluateList(x, repr)
case *arrayExpr:
val = e.evaluateArray(x, repr)
case *objectExpr:
val = e.evaluateObject(x, repr)
default:
Expand All @@ -477,8 +477,8 @@ func (e *evalContext) evaluateTypedExpr(x *expr, accept *schema.Schema) (*value,
return v, ok
}

// evaluateList evaluates a list expression.
func (e *evalContext) evaluateList(x *expr, repr *listExpr) *value {
// evaluateArray evaluates an array expression.
func (e *evalContext) evaluateArray(x *expr, repr *arrayExpr) *value {
v := &value{def: x}

array, items := make([]*value, len(repr.elements)), make([]schema.Builder, len(repr.elements))
Expand Down Expand Up @@ -566,7 +566,7 @@ func (e *evalContext) evaluateExprAccess(x *expr, accessors []*propertyAccessor)
for len(accessors) > 0 {
accessor := accessors[0]
switch repr := receiver.repr.(type) {
case *listExpr:
case *arrayExpr:
index, ok := e.arrayIndex(x.repr.syntax(), accessor.accessor, len(repr.elements))
if !ok {
return e.invalidPropertyAccess(x.repr.syntax(), accessors)
Expand Down
2 changes: 1 addition & 1 deletion eval/eval_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ type validationLoc struct {
// and the index is in range, then the returned location will refer to the array element at the given index. Otherwise,
// the returned location will refer to the original expression, but will include an appropriate path prefix.
func (l validationLoc) index(i int) validationLoc {
list, isLiteral := l.x.repr.(*listExpr)
list, isLiteral := l.x.repr.(*arrayExpr)
if isLiteral && i < len(list.elements) {
return validationLoc{
x: list.elements[i],
Expand Down
10 changes: 5 additions & 5 deletions eval/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ func (x *expr) export(environment string) esc.Expr {
ArgSchema: schema.Always().Schema(),
Arg: repr.value.export(environment),
}
case *listExpr:
case *arrayExpr:
ex.List = make([]esc.Expr, len(repr.elements))
for i, el := range repr.elements {
ex.List[i] = el.export(environment)
Expand Down Expand Up @@ -284,14 +284,14 @@ func (x *accessExpr) syntax() ast.Expr {
return x.node
}

// listExpr represents a list literal.
type listExpr struct {
node *ast.ListExpr
// arrayExpr represents an array literal.
type arrayExpr struct {
node *ast.ArrayExpr

elements []*expr
}

func (x *listExpr) syntax() ast.Expr {
func (x *arrayExpr) syntax() ast.Expr {
return x.node
}

Expand Down
2 changes: 1 addition & 1 deletion expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type Expr struct {
// The access, if this is an access expression
Access *AccessExpr `json:"access,omitempty"`

// The list elements, if this isa list expression
// The list elements, if this is a list expression
List []Expr `json:"list,omitempty"`

// The object properties, if this is an object expression.
Expand Down
64 changes: 64 additions & 0 deletions syntax/encoding/node_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2023, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package encoding

import (
"fmt"

"github.com/hashicorp/hcl/v2"
"github.com/pulumi/esc/syntax"
)

type NodeProperty struct {
Key Node `json:"key"`
Value Node `json:"value"`
}

type Node struct {
Literal any `json:"literal,omitempty"`
Array []Node `json:"array,omitempty"`
Object []NodeProperty `json:"object,omitempty"`
Range *hcl.Range `json:"range,omitempty"`
}

func NewNode(n syntax.Node) Node {
switch n := n.(type) {
case nil:
return Node{}
case *syntax.NullNode:
return Node{Range: n.Syntax().Range()}
case *syntax.BooleanNode:
return Node{Literal: n.Value(), Range: n.Syntax().Range()}
case *syntax.NumberNode:
return Node{Literal: n.Value(), Range: n.Syntax().Range()}
case *syntax.StringNode:
return Node{Literal: n.Value(), Range: n.Syntax().Range()}
case *syntax.ArrayNode:
arr := make([]Node, n.Len())
for i := range arr {
arr[i] = NewNode(n.Index(i))
}
return Node{Array: arr, Range: n.Syntax().Range()}
case *syntax.ObjectNode:
obj := make([]NodeProperty, n.Len())
for i := 0; i < n.Len(); i++ {
prop := n.Index(i)
obj[i] = NodeProperty{Key: NewNode(prop.Key), Value: NewNode(prop.Value)}
}
return Node{Object: obj, Range: n.Syntax().Range()}
default:
panic(fmt.Errorf("unexpected node of type %T", n))
}
}
Loading

0 comments on commit 593dcae

Please sign in to comment.