blob: b7be33d235b6349c3ac1212400ba2e2f1ef06580 [file] [log] [blame]
Marcel van Lohuizend96ad3d2018-12-10 15:30:20 +01001// Copyright 2018 The CUE Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// This file implements scopes and the objects they contain.
16
17package parser
18
19import (
20 "bytes"
21 "fmt"
22
23 "cuelang.org/go/cue/ast"
24 "cuelang.org/go/cue/token"
25)
26
27// resolve resolves all identifiers in a file. Unresolved identifiers are
28// recorded in Unresolved.
29func resolve(f *ast.File, errFn func(pos token.Pos, msg string)) {
30 walk(&scope{errFn: errFn}, f)
31}
32
Marcel van Lohuizenea2de892019-04-03 22:34:44 +020033func resolveExpr(e ast.Expr, errFn func(pos token.Pos, msg string)) {
34 f := &ast.File{}
35 walk(&scope{file: f, errFn: errFn}, e)
36}
37
Marcel van Lohuizend96ad3d2018-12-10 15:30:20 +010038// A Scope maintains the set of named language entities declared
39// in the scope and a link to the immediately surrounding (outer)
40// scope.
41//
42type scope struct {
43 file *ast.File
44 outer *scope
45 node ast.Node
46 index map[string]ast.Node
47
48 errFn func(p token.Pos, msg string)
49}
50
51func newScope(f *ast.File, outer *scope, node ast.Node, decls []ast.Decl) *scope {
52 const n = 4 // initial scope capacity
53 s := &scope{
54 file: f,
55 outer: outer,
56 node: node,
57 index: make(map[string]ast.Node, n),
58 errFn: outer.errFn,
59 }
60 for _, d := range decls {
61 switch x := d.(type) {
62 case *ast.Field:
63 if name, ok := ast.LabelName(x.Label); ok {
64 s.insert(name, x.Value)
65 }
66 case *ast.Alias:
67 name := x.Ident.Name
68 s.insert(name, x)
69 // Handle imports
70 }
71 }
72 return s
73}
74
75func (s *scope) insert(name string, n ast.Node) {
76 if _, existing := s.lookup(name); existing != nil {
77 _, isAlias1 := n.(*ast.Alias)
78 _, isAlias2 := existing.(*ast.Alias)
79 if isAlias1 != isAlias2 {
80 s.errFn(n.Pos(), "cannot have alias and non-alias with the same name")
81 return
82 } else if isAlias1 || isAlias2 {
83 s.errFn(n.Pos(), "cannot have two aliases with the same name in the same scope")
84 return
85 }
86 }
87 s.index[name] = n
88}
89
90func (s *scope) lookup(name string) (obj, node ast.Node) {
91 last := s
92 for s != nil {
93 if n, ok := s.index[name]; ok {
94 if last.node == n {
95 return nil, n
96 }
97 return s.node, n
98 }
99 s, last = s.outer, s
100 }
101 return nil, nil
102}
103
104func (s *scope) After(n ast.Node) {}
105func (s *scope) Before(n ast.Node) (w visitor) {
106 switch x := n.(type) {
107 case *ast.File:
108 s := newScope(x, s, x, x.Decls)
109 // Support imports.
110 for _, d := range x.Decls {
111 walk(s, d)
112 }
113 return nil
114
115 case *ast.StructLit:
116 return newScope(s.file, s, x, x.Elts)
117
118 case *ast.ComprehensionDecl:
119 s = scopeClauses(s, x.Clauses)
120
121 case *ast.ListComprehension:
122 s = scopeClauses(s, x.Clauses)
123
124 case *ast.Field:
125 switch label := x.Label.(type) {
126 case *ast.Interpolation:
127 walk(s, label)
Marcel van Lohuizend96ad3d2018-12-10 15:30:20 +0100128 case *ast.TemplateLabel:
129 s := newScope(s.file, s, x, nil)
130 name, _ := ast.LabelName(label)
131 s.insert(name, x.Label) // Field used for entire lambda.
132 walk(s, x.Value)
133 return nil
134 }
135 // Disallow referring to the current LHS name (this applies recursively)
136 if x.Value != nil {
137 walk(s, x.Value)
138 }
139 return nil
140
141 case *ast.Alias:
142 // Disallow referring to the current LHS name.
143 name := x.Ident.Name
144 saved := s.index[name]
145 delete(s.index, name) // The same name may still appear in another scope
146
147 if x.Expr != nil {
148 walk(s, x.Expr)
149 }
150 s.index[name] = saved
151 return nil
152
153 case *ast.ImportSpec:
154 return nil
155
156 case *ast.SelectorExpr:
157 walk(s, x.X)
158 return nil
159
Marcel van Lohuizend96ad3d2018-12-10 15:30:20 +0100160 case *ast.Ident:
161 if obj, node := s.lookup(x.Name); node != nil {
162 x.Node = node
163 x.Scope = obj
164 } else {
165 s.file.Unresolved = append(s.file.Unresolved, x)
166 }
167 return nil
168 }
169 return s
170}
171
172func scopeClauses(s *scope, clauses []ast.Clause) *scope {
173 for _, c := range clauses {
174 if f, ok := c.(*ast.ForClause); ok { // TODO(let): support let clause
175 walk(s, f.Source)
176 s = newScope(s.file, s, f, nil)
177 if f.Key != nil {
178 s.insert(f.Key.Name, f.Key)
179 }
180 s.insert(f.Value.Name, f.Value)
181 } else {
182 walk(s, c)
183 }
184 }
185 return s
186}
187
188// Debugging support
189func (s *scope) String() string {
190 var buf bytes.Buffer
191 fmt.Fprintf(&buf, "scope %p {", s)
192 if s != nil && len(s.index) > 0 {
193 fmt.Fprintln(&buf)
194 for name := range s.index {
195 fmt.Fprintf(&buf, "\t%v\n", name)
196 }
197 }
198 fmt.Fprintf(&buf, "}\n")
199 return buf.String()
200}