blob: 29adad70a7dc439bd87b6035bd8e5b49716cf6b5 [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
15package parser
16
17import (
18 "fmt"
19
20 "cuelang.org/go/cue/ast"
21 "cuelang.org/go/cue/token"
22)
23
24// TODO: use ast.Walk or adopt that version to allow visitors.
25
26// A visitor's before method is invoked for each node encountered by Walk.
27// If the result visitor w is not nil, Walk visits each of the children
28// of node with the visitor w, followed by a call of w.After.
29type visitor interface {
30 Before(node ast.Node) (w visitor)
31 After(node ast.Node)
32}
33
34// Helper functions for common node lists. They may be empty.
35
36func walkIdentList(v visitor, list []*ast.Ident) {
37 for _, x := range list {
38 walk(v, x)
39 }
40}
41
42func walkExprList(v visitor, list []ast.Expr) {
43 for _, x := range list {
44 walk(v, x)
45 }
46}
47
48func walkDeclList(v visitor, list []ast.Decl) {
49 for _, x := range list {
50 walk(v, x)
51 }
52}
53
54// walk traverses an AST in depth-first order: It starts by calling
55// v.Visit(node); node must not be nil. If the visitor w returned by
56// v.Visit(node) is not nil, walk is invoked recursively with visitor
57// w for each of the non-nil children of node, followed by a call of
58// w.Visit(nil).
59//
60func walk(v visitor, node ast.Node) {
61 if v = v.Before(node); v == nil {
62 return
63 }
64
65 // TODO: record the comment groups and interleave with the values like for
66 // parsing and printing?
67 for _, c := range node.Comments() {
68 walk(v, c)
69 }
70
71 // walk children
72 // (the order of the cases matches the order
73 // of the corresponding node types in go)
74 switch n := node.(type) {
75 // Comments and fields
76 case *ast.Comment:
77 // nothing to do
78
79 case *ast.CommentGroup:
80 for _, c := range n.List {
81 walk(v, c)
82 }
83
84 case *ast.Field:
85 walk(v, n.Label)
86 if n.Value != nil {
87 walk(v, n.Value)
88 }
89
Marcel van Lohuizend96ad3d2018-12-10 15:30:20 +010090 case *ast.StructLit:
91 for _, f := range n.Elts {
92 walk(v, f)
93 }
94
95 // Expressions
96 case *ast.BottomLit, *ast.BadExpr, *ast.Ident, *ast.BasicLit:
97 // nothing to do
98
Marcel van Lohuizend96ad3d2018-12-10 15:30:20 +010099 case *ast.TemplateLabel:
100 walk(v, n.Ident)
101
102 case *ast.Interpolation:
103 for _, e := range n.Elts {
104 walk(v, e)
105 }
106
107 case *ast.Ellipsis:
108 if n.Elt != nil {
109 walk(v, n.Elt)
110 }
111
112 case *ast.ListLit:
113 walkExprList(v, n.Elts)
114 if n.Type != nil {
115 walk(v, n.Type)
116 }
117
118 case *ast.ParenExpr:
119 walk(v, n.X)
120
121 case *ast.SelectorExpr:
122 walk(v, n.X)
123 walk(v, n.Sel)
124
125 case *ast.IndexExpr:
126 walk(v, n.X)
127 walk(v, n.Index)
128
129 case *ast.SliceExpr:
130 walk(v, n.X)
131 if n.Low != nil {
132 walk(v, n.Low)
133 }
134 if n.High != nil {
135 walk(v, n.High)
136 }
137
138 case *ast.CallExpr:
139 walk(v, n.Fun)
140 walkExprList(v, n.Args)
141
142 case *ast.UnaryExpr:
143 walk(v, n.X)
144
145 case *ast.BinaryExpr:
146 walk(v, n.X)
147 walk(v, n.Y)
148
149 // Declarations
150 case *ast.ImportSpec:
151 if n.Name != nil {
152 walk(v, n.Name)
153 }
154 walk(v, n.Path)
155
156 case *ast.BadDecl:
157 // nothing to do
158
159 case *ast.ImportDecl:
160 for _, s := range n.Specs {
161 walk(v, s)
162 }
163
164 case *ast.EmitDecl:
165 walk(v, n.Expr)
166
167 case *ast.Alias:
168 walk(v, n.Ident)
169 walk(v, n.Expr)
170
171 case *ast.ComprehensionDecl:
172 walk(v, n.Field)
173 for _, c := range n.Clauses {
174 walk(v, c)
175 }
176
177 // Files and packages
178 case *ast.File:
179 if n.Name != nil {
180 walk(v, n.Name)
181 }
182 walkDeclList(v, n.Decls)
183 // don't walk n.Comments - they have been
184 // visited already through the individual
185 // nodes
186
187 case *ast.ListComprehension:
188 walk(v, n.Expr)
189 for _, c := range n.Clauses {
190 walk(v, c)
191 }
192
193 case *ast.ForClause:
194 if n.Key != nil {
195 walk(v, n.Key)
196 }
197 walk(v, n.Value)
198 walk(v, n.Source)
199
200 case *ast.IfClause:
201 walk(v, n.Condition)
202
203 default:
204 panic(fmt.Sprintf("Walk: unexpected node type %T", n))
205 }
206
207 v.After(node)
208}
209
210type inspector struct {
211 before func(ast.Node) bool
212 after func(ast.Node)
213
214 commentStack []commentFrame
215 current commentFrame
216}
217
218type commentFrame struct {
219 cg []*ast.CommentGroup
220 pos int8
221}
222
223func (f *inspector) Before(node ast.Node) visitor {
224 if f.before == nil || f.before(node) {
225 f.commentStack = append(f.commentStack, f.current)
226 f.current = commentFrame{cg: node.Comments()}
227 f.visitComments(f.current.pos)
228 return f
229 }
230 return nil
231}
232
233func (f *inspector) After(node ast.Node) {
234 f.visitComments(127)
235 p := len(f.commentStack) - 1
236 f.current = f.commentStack[p]
237 f.commentStack = f.commentStack[:p]
238 f.current.pos++
239 if f.after != nil {
240 f.after(node)
241 }
242}
243
244func (f *inspector) Token(t token.Token) {
245 f.current.pos++
246}
247
248func (f *inspector) setPos(i int8) {
249 f.current.pos = i
250}
251
252func (f *inspector) visitComments(pos int8) {
253 c := &f.current
254 for ; len(c.cg) > 0; c.cg = c.cg[1:] {
255 cg := c.cg[0]
256 if cg.Position == pos {
257 continue
258 }
259 if f.before == nil || f.before(cg) {
260 for _, c := range cg.List {
261 if f.before == nil || f.before(c) {
262 if f.after != nil {
263 f.after(c)
264 }
265 }
266 }
267 if f.after != nil {
268 f.after(cg)
269 }
270 }
271 }
272}