Translate CocoPy subscripts

This change implements simple index-based CocoPy subscripts (and
not the more fully featured slicing ones).

Alongside this, we relax a binder-time check that all dynamic
access types must be strings.  The eval code already handles
numeric (array) accesses, so we will permit these to flow through.
This commit is contained in:
joeduffy 2017-04-11 12:33:30 -07:00
parent 9adfa6a18f
commit aa730b5913
3 changed files with 29 additions and 8 deletions

View file

@ -3,6 +3,7 @@
package binder
import (
"fmt"
"reflect"
"github.com/pulumi/coconut/pkg/compiler/ast"
@ -240,10 +241,25 @@ func (a *astBinder) checkForStatement(node *ast.ForStatement) {
// Expressions
func (a *astBinder) checkExprType(expr ast.Expression, expect symbols.Type) bool {
func (a *astBinder) checkExprType(expr ast.Expression, expect symbols.Type, alts ...symbols.Type) bool {
actual := a.b.ctx.RequireType(expr)
if !types.CanConvert(actual, expect) {
a.b.Diag().Errorf(errors.ErrorIncorrectExprType.At(expr), expect, actual)
conv := false
if conv = types.CanConvert(actual, expect); !conv {
// If the primary didn't convert, check the alternatives.
for _, alt := range alts {
if conv = types.CanConvert(actual, alt); conv {
break
}
}
}
if !conv {
expects := expect.Token().String()
if len(alts) > 0 {
for _, alt := range alts {
expects += fmt.Sprintf(" or %v", alt.Token())
}
}
a.b.Diag().Errorf(errors.ErrorIncorrectExprType.At(expr), expects, actual)
return false
}
return true
@ -382,8 +398,8 @@ func (a *astBinder) checkLoadLocationExpression(node *ast.LoadLocationExpression
}
func (a *astBinder) checkLoadDynamicExpression(node *ast.LoadDynamicExpression) {
// Ensure that the name is either a string or a dynamic.
a.checkExprType(node.Name, types.String)
// Ensure that the name is either a string, number, or a dynamic.
a.checkExprType(node.Name, types.String, types.Number)
// No matter the outcome, a load dynamic always produces a dynamically typed thing.
a.b.ctx.RegisterType(node, types.Dynamic)

View file

@ -1586,8 +1586,8 @@ func (e *evaluator) evalNew(node diag.Diagable, t symbols.Type, args *[]*ast.Cal
for i, arg := range *args {
contract.Assertf(arg.Name != nil, "Expected only named args for new of a record type")
id := tokens.ClassMemberName(arg.Name.Ident)
contract.Assertf(t.TypeMembers()[id] != nil, "Expected named arg to match a type member")
contract.Assertf(t.TypeMembers()[id].Primary(), "Expected named arg to match a primary member")
contract.Assertf(t.TypeMembers()[id] != nil, "Expected named arg %v to match a type member", id)
contract.Assertf(t.TypeMembers()[id].Primary(), "Expected named arg %v to match a primary member", id)
val := argobjs[i]
prop := rt.PropertyKey(id)
addr := obj.GetPropertyAddr(prop, true, true)

View file

@ -580,7 +580,12 @@ class Transformer:
self.not_yet_implemented(node) # value, ctx
def transform_Subscript(self, node):
self.not_yet_implemented(node) # value, slice, ctx
obj = self.transform_expr(node.value)
if isinstance(node.slice, py_ast.Index):
idx = self.transform_expr(node.slice.value)
else:
assert false, "Unsupported slicing type: {}".format(type(node.slice).__name__)
return ast.LoadDynamicExpression(idx, obj, loc=self.loc_from(node))
def transform_Tuple(self, node):
self.not_yet_implemented(node) # elts, ctx