From aa730b5913427a2069f35d6cbb14c4e7aa741001 Mon Sep 17 00:00:00 2001 From: joeduffy Date: Tue, 11 Apr 2017 12:33:30 -0700 Subject: [PATCH] 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. --- pkg/compiler/binder/stmtexpr.go | 26 +++++++++++++++++++++----- pkg/eval/eval.go | 4 ++-- tools/cocopy/cocopy/lib/compiler.py | 7 ++++++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/pkg/compiler/binder/stmtexpr.go b/pkg/compiler/binder/stmtexpr.go index 034843cb3..6e8d6ba9c 100644 --- a/pkg/compiler/binder/stmtexpr.go +++ b/pkg/compiler/binder/stmtexpr.go @@ -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) diff --git a/pkg/eval/eval.go b/pkg/eval/eval.go index f08aa876f..9c6fdd9d3 100644 --- a/pkg/eval/eval.go +++ b/pkg/eval/eval.go @@ -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) diff --git a/tools/cocopy/cocopy/lib/compiler.py b/tools/cocopy/cocopy/lib/compiler.py index 029664575..978739a45 100644 --- a/tools/cocopy/cocopy/lib/compiler.py +++ b/tools/cocopy/cocopy/lib/compiler.py @@ -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