| // Copyright 2018 The CUE Authors |
| // |
| // 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 parser |
| |
| import ( |
| "fmt" |
| |
| "cuelang.org/go/cue/ast" |
| ) |
| |
| // TODO: use ast.Walk or adopt that version to allow visitors. |
| |
| // A visitor's before method is invoked for each node encountered by Walk. |
| // If the result visitor w is not nil, Walk visits each of the children |
| // of node with the visitor w, followed by a call of w.After. |
| type visitor interface { |
| Before(node ast.Node) (w visitor) |
| After(node ast.Node) |
| } |
| |
| // Helper functions for common node lists. They may be empty. |
| |
| func walkExprList(v visitor, list []ast.Expr) { |
| for _, x := range list { |
| walk(v, x) |
| } |
| } |
| |
| func walkDeclList(v visitor, list []ast.Decl) { |
| for _, x := range list { |
| walk(v, x) |
| } |
| } |
| |
| // walk traverses an AST in depth-first order: It starts by calling |
| // v.Visit(node); node must not be nil. If the visitor w returned by |
| // v.Visit(node) is not nil, walk is invoked recursively with visitor |
| // w for each of the non-nil children of node, followed by a call of |
| // w.Visit(nil). |
| // |
| func walk(v visitor, node ast.Node) { |
| if v = v.Before(node); v == nil { |
| return |
| } |
| |
| // TODO: record the comment groups and interleave with the values like for |
| // parsing and printing? |
| for _, c := range node.Comments() { |
| walk(v, c) |
| } |
| |
| // walk children |
| // (the order of the cases matches the order |
| // of the corresponding node types in go) |
| switch n := node.(type) { |
| // Comments and fields |
| case *ast.Comment: |
| // nothing to do |
| |
| case *ast.CommentGroup: |
| for _, c := range n.List { |
| walk(v, c) |
| } |
| |
| case *ast.Attribute: |
| // nothing to do |
| |
| case *ast.Field: |
| walk(v, n.Label) |
| if n.Value != nil { |
| walk(v, n.Value) |
| } |
| for _, a := range n.Attrs { |
| walk(v, a) |
| } |
| |
| case *ast.StructLit: |
| for _, f := range n.Elts { |
| walk(v, f) |
| } |
| |
| // Expressions |
| case *ast.BottomLit, *ast.BadExpr, *ast.Ident, *ast.BasicLit: |
| // nothing to do |
| |
| case *ast.TemplateLabel: |
| walk(v, n.Ident) |
| |
| case *ast.Interpolation: |
| for _, e := range n.Elts { |
| walk(v, e) |
| } |
| |
| case *ast.ListLit: |
| walkExprList(v, n.Elts) |
| if n.Type != nil { |
| walk(v, n.Type) |
| } |
| |
| case *ast.ParenExpr: |
| walk(v, n.X) |
| |
| case *ast.SelectorExpr: |
| walk(v, n.X) |
| walk(v, n.Sel) |
| |
| case *ast.IndexExpr: |
| walk(v, n.X) |
| walk(v, n.Index) |
| |
| case *ast.SliceExpr: |
| walk(v, n.X) |
| if n.Low != nil { |
| walk(v, n.Low) |
| } |
| if n.High != nil { |
| walk(v, n.High) |
| } |
| |
| case *ast.CallExpr: |
| walk(v, n.Fun) |
| walkExprList(v, n.Args) |
| |
| case *ast.UnaryExpr: |
| walk(v, n.X) |
| |
| case *ast.BinaryExpr: |
| walk(v, n.X) |
| walk(v, n.Y) |
| |
| // Declarations |
| case *ast.ImportSpec: |
| if n.Name != nil { |
| walk(v, n.Name) |
| } |
| walk(v, n.Path) |
| |
| case *ast.BadDecl: |
| // nothing to do |
| |
| case *ast.ImportDecl: |
| for _, s := range n.Specs { |
| walk(v, s) |
| } |
| |
| case *ast.EmitDecl: |
| walk(v, n.Expr) |
| |
| case *ast.Alias: |
| walk(v, n.Ident) |
| walk(v, n.Expr) |
| |
| case *ast.ComprehensionDecl: |
| walk(v, n.Field) |
| for _, c := range n.Clauses { |
| walk(v, c) |
| } |
| |
| // Files and packages |
| case *ast.File: |
| if n.Name != nil { |
| walk(v, n.Name) |
| } |
| walkDeclList(v, n.Decls) |
| |
| case *ast.ListComprehension: |
| walk(v, n.Expr) |
| for _, c := range n.Clauses { |
| walk(v, c) |
| } |
| |
| case *ast.ForClause: |
| if n.Key != nil { |
| walk(v, n.Key) |
| } |
| walk(v, n.Value) |
| walk(v, n.Source) |
| |
| case *ast.IfClause: |
| walk(v, n.Condition) |
| |
| default: |
| panic(fmt.Sprintf("Walk: unexpected node type %T", n)) |
| } |
| |
| v.After(node) |
| } |