this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cue/ast: add package

Change-Id: I587028ef8ce9e2e57eb75b8db5667b786dce9213

+1054
+707
cue/ast/ast.go
··· 1 + // 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 + // Package ast declares the types used to represent syntax trees for CUE 16 + // packages. 17 + package ast // import "cuelang.org/go/cue/ast" 18 + 19 + import ( 20 + "strconv" 21 + "strings" 22 + 23 + "cuelang.org/go/cue/token" 24 + ) 25 + 26 + // ---------------------------------------------------------------------------- 27 + // Interfaces 28 + // 29 + // There are two main classes of nodes: expressions, clauses, and declaration 30 + // nodes. The node names usually match the corresponding CUE spec production 31 + // names to which they correspond. The node fields correspond to the individual 32 + // parts of the respective productions. 33 + // 34 + // All nodes contain position information marking the beginning of the 35 + // corresponding source text segment; it is accessible via the Pos accessor 36 + // method. Nodes may contain additional position info for language constructs 37 + // where comments may be found between parts of the construct (typically any 38 + // larger, parenthesized subpart). That position information is needed to 39 + // properly position comments when printing the construct. 40 + 41 + // A Node represents any node in the abstract syntax tree. 42 + type Node interface { 43 + Pos() token.Pos // position of first character belonging to the node 44 + End() token.Pos // position of first character immediately after the node 45 + 46 + // TODO: SetPos(p token.RelPos) 47 + 48 + Comments() []*CommentGroup 49 + AddComment(*CommentGroup) 50 + } 51 + 52 + // An Expr is implemented by all expression nodes. 53 + type Expr interface { 54 + Node 55 + exprNode() 56 + } 57 + 58 + func (*BadExpr) exprNode() {} 59 + func (*Ident) exprNode() {} 60 + func (*Ellipsis) exprNode() {} 61 + func (*BasicLit) exprNode() {} 62 + func (*Interpolation) exprNode() {} 63 + func (*StructLit) exprNode() {} 64 + func (*ListLit) exprNode() {} 65 + func (*LambdaExpr) exprNode() {} 66 + 67 + // func (*StructComprehension) exprNode() {} 68 + func (*ListComprehension) exprNode() {} 69 + func (*ParenExpr) exprNode() {} 70 + func (*SelectorExpr) exprNode() {} 71 + func (*IndexExpr) exprNode() {} 72 + func (*SliceExpr) exprNode() {} 73 + func (*CallExpr) exprNode() {} 74 + func (*UnaryExpr) exprNode() {} 75 + func (*BinaryExpr) exprNode() {} 76 + func (*BottomLit) exprNode() {} 77 + 78 + // A Decl node is implemented by all declarations. 79 + type Decl interface { 80 + Node 81 + declNode() 82 + } 83 + 84 + func (*Field) declNode() {} 85 + func (*ComprehensionDecl) declNode() {} 86 + func (*ImportDecl) declNode() {} 87 + func (*BadDecl) declNode() {} 88 + func (*EmitDecl) declNode() {} 89 + func (*Alias) declNode() {} 90 + 91 + // A Label is any prduction that can be used as a LHS label. 92 + type Label interface { 93 + Node 94 + labelName() (name string, isIdent bool) 95 + } 96 + 97 + func (n *Ident) labelName() (string, bool) { 98 + return n.Name, true 99 + } 100 + 101 + func (n *BasicLit) labelName() (string, bool) { 102 + switch n.Kind { 103 + case token.STRING: 104 + // Use strconv to only allow double-quoted, single-line strings. 105 + if str, err := strconv.Unquote(n.Value); err == nil { 106 + return str, true 107 + } 108 + case token.NULL, token.TRUE, token.FALSE: 109 + return n.Value, true 110 + 111 + // TODO: allow numbers to be fields? 112 + } 113 + return "", false 114 + } 115 + 116 + func (n *TemplateLabel) labelName() (string, bool) { 117 + return n.Ident.Name, false 118 + } 119 + 120 + func (n *Interpolation) labelName() (string, bool) { 121 + return "", false 122 + } 123 + func (n *ExprLabel) labelName() (string, bool) { 124 + return "", false 125 + } 126 + 127 + // LabelName reports the name of a label, if known, and whether it is valid. 128 + func LabelName(x Label) (name string, ok bool) { 129 + return x.labelName() 130 + } 131 + 132 + // As for now, a ExprLabel is not a label, as it can only use used in 133 + // struct comprehensions. 134 + 135 + // Clause nodes are part of comprehensions. 136 + type Clause interface { 137 + Node 138 + clauseNode() 139 + } 140 + 141 + func (x *ForClause) clauseNode() {} 142 + func (x *IfClause) clauseNode() {} 143 + func (x *Alias) clauseNode() {} 144 + 145 + // Comments 146 + 147 + type comments struct { 148 + groups *[]*CommentGroup 149 + } 150 + 151 + func (c *comments) Comments() []*CommentGroup { 152 + if c.groups == nil { 153 + return []*CommentGroup{} 154 + } 155 + return *c.groups 156 + } 157 + 158 + // // AddComment adds the given comments to the fields. 159 + // // If line is true the comment is inserted at the preceding token. 160 + 161 + func (c *comments) AddComment(cg *CommentGroup) { 162 + if cg == nil { 163 + return 164 + } 165 + if c.groups == nil { 166 + a := []*CommentGroup{cg} 167 + c.groups = &a 168 + return 169 + } 170 + *c.groups = append(*c.groups, cg) 171 + } 172 + 173 + // A Comment node represents a single //-style or /*-style comment. 174 + type Comment struct { 175 + Slash token.Pos // position of "/" starting the comment 176 + Text string // comment text (excluding '\n' for //-style comments) 177 + } 178 + 179 + func (g *Comment) Comments() []*CommentGroup { return nil } 180 + func (g *Comment) AddComment(*CommentGroup) {} 181 + 182 + func (c *Comment) Pos() token.Pos { return c.Slash } 183 + func (c *Comment) End() token.Pos { return c.Slash.Add(len(c.Text)) } 184 + 185 + // A CommentGroup represents a sequence of comments 186 + // with no other tokens and no empty lines between. 187 + type CommentGroup struct { 188 + // TODO: remove and use the token position of the first commment. 189 + Doc bool 190 + Line bool // true if it is on the same line as the node's end pos. 191 + 192 + // Position indicates where a comment should be attached if a node has 193 + // multiple tokens. 0 means before the first token, 1 means before the 194 + // second, etc. For instance, for a field, the positions are: 195 + // <0> Label <1> ":" <2> Expr <3> "," <4> 196 + Position int8 197 + List []*Comment // len(List) > 0 198 + } 199 + 200 + func (g *CommentGroup) Pos() token.Pos { return g.List[0].Pos() } 201 + func (g *CommentGroup) End() token.Pos { return g.List[len(g.List)-1].End() } 202 + 203 + func (g *CommentGroup) Comments() []*CommentGroup { return nil } 204 + func (g *CommentGroup) AddComment(*CommentGroup) {} 205 + 206 + func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' } 207 + 208 + func stripTrailingWhitespace(s string) string { 209 + i := len(s) 210 + for i > 0 && isWhitespace(s[i-1]) { 211 + i-- 212 + } 213 + return s[0:i] 214 + } 215 + 216 + // Text returns the text of the comment. 217 + // Comment markers (//, /*, and */), the first space of a line comment, and 218 + // leading and trailing empty lines are removed. Multiple empty lines are 219 + // reduced to one, and trailing space on lines is trimmed. Unless the result 220 + // is empty, it is newline-terminated. 221 + func (g *CommentGroup) Text() string { 222 + if g == nil { 223 + return "" 224 + } 225 + comments := make([]string, len(g.List)) 226 + for i, c := range g.List { 227 + comments[i] = c.Text 228 + } 229 + 230 + lines := make([]string, 0, 10) // most comments are less than 10 lines 231 + for _, c := range comments { 232 + // Remove comment markers. 233 + // The parser has given us exactly the comment text. 234 + switch c[1] { 235 + case '/': 236 + //-style comment (no newline at the end) 237 + c = c[2:] 238 + // strip first space - required for Example tests 239 + if len(c) > 0 && c[0] == ' ' { 240 + c = c[1:] 241 + } 242 + case '*': 243 + /*-style comment */ 244 + c = c[2 : len(c)-2] 245 + } 246 + 247 + // Split on newlines. 248 + cl := strings.Split(c, "\n") 249 + 250 + // Walk lines, stripping trailing white space and adding to list. 251 + for _, l := range cl { 252 + lines = append(lines, stripTrailingWhitespace(l)) 253 + } 254 + } 255 + 256 + // Remove leading blank lines; convert runs of 257 + // interior blank lines to a single blank line. 258 + n := 0 259 + for _, line := range lines { 260 + if line != "" || n > 0 && lines[n-1] != "" { 261 + lines[n] = line 262 + n++ 263 + } 264 + } 265 + lines = lines[0:n] 266 + 267 + // Add final "" entry to get trailing newline from Join. 268 + if n > 0 && lines[n-1] != "" { 269 + lines = append(lines, "") 270 + } 271 + 272 + return strings.Join(lines, "\n") 273 + } 274 + 275 + // A Field represents a field declaration in a struct. 276 + type Field struct { 277 + comments 278 + Label Label // must have at least one element. 279 + 280 + // No colon: Value must be an StructLit with one field or a 281 + // LambdaExpr. 282 + Colon token.Pos 283 + Value Expr // the value associated with this field. 284 + } 285 + 286 + func (d *Field) Pos() token.Pos { return d.Label.Pos() } 287 + func (d *Field) End() token.Pos { return d.Value.End() } 288 + 289 + // An Alias binds another field to the alias name in the current struct. 290 + type Alias struct { 291 + comments 292 + Ident *Ident // field name, always an Ident 293 + Equal token.Pos // position of "=" 294 + Expr Expr // An Ident or SelectorExpr 295 + } 296 + 297 + func (a *Alias) Pos() token.Pos { return a.Ident.Pos() } 298 + func (a *Alias) End() token.Pos { return a.Expr.End() } 299 + 300 + // A ComprehensionDecl node represents a field comprehension. 301 + type ComprehensionDecl struct { 302 + comments 303 + Field *Field 304 + Select token.Pos 305 + Clauses []Clause 306 + } 307 + 308 + func (x *ComprehensionDecl) Pos() token.Pos { return x.Field.Pos() } 309 + func (x *ComprehensionDecl) End() token.Pos { 310 + if len(x.Clauses) > 0 { 311 + return x.Clauses[len(x.Clauses)-1].End() 312 + } 313 + return x.Select 314 + } 315 + 316 + // ---------------------------------------------------------------------------- 317 + // Expressions and types 318 + // 319 + // An expression is represented by a tree consisting of one 320 + // or more of the following concrete expression nodes. 321 + 322 + // A LambdaExpr defines a function expression. 323 + // 324 + // Lambdas are only used internally under controlled conditions. Although 325 + // the implementation of lambdas is fully functional, enabling them will 326 + // cause the language to be Turing-complete (if not otherwise limited). 327 + // Also, lambdas would provide yet another way to create structure, and one 328 + // that is known to not work well for declarative configuration languages. 329 + type LambdaExpr struct { 330 + comments 331 + Lparen token.Pos // position of "(" 332 + Params []*Field // parameters with possible initializers 333 + Rparen token.Pos // position of ")" 334 + Expr Expr 335 + } 336 + 337 + func (t *LambdaExpr) Pos() token.Pos { return t.Lparen } 338 + func (t *LambdaExpr) End() token.Pos { return t.Rparen } 339 + 340 + // A BadExpr node is a placeholder for expressions containing 341 + // syntax errors for which no correct expression nodes can be 342 + // created. This is different from an ErrorExpr which represents 343 + // an explicitly marked error in the source. 344 + type BadExpr struct { 345 + comments 346 + From, To token.Pos // position range of bad expression 347 + } 348 + 349 + // A BottomLit indicates an error. 350 + type BottomLit struct { 351 + comments 352 + Bottom token.Pos 353 + } 354 + 355 + // An Ident node represents an left-hand side identifier. 356 + type Ident struct { 357 + comments 358 + NamePos token.Pos // identifier position 359 + 360 + // This LHS path element may be an identifier. Possible forms: 361 + // foo: a normal identifier 362 + // "foo": JSON compatible 363 + // <foo>: a template shorthand 364 + Name string 365 + 366 + Scope Node // scope in which node was found or nil if referring directly 367 + Node Node 368 + } 369 + 370 + // A TemplateLabel represents a field template declaration in a struct. 371 + type TemplateLabel struct { 372 + comments 373 + Langle token.Pos 374 + Ident *Ident 375 + Rangle token.Pos 376 + } 377 + 378 + // An ExprLabel is an expression to create a label in struct comprehensions. 379 + type ExprLabel struct { 380 + comments 381 + Lbrack token.Pos 382 + Label Expr 383 + Rbrack token.Pos 384 + } 385 + 386 + // An Ellipsis node stands for the "..." type in a 387 + // parameter list or the "..." length in an array type. 388 + type Ellipsis struct { 389 + comments 390 + Ellipsis token.Pos // position of "..." 391 + Elt Expr // ellipsis element type (parameter lists only); or nil 392 + } 393 + 394 + // A BasicLit node represents a literal of basic type. 395 + type BasicLit struct { 396 + comments 397 + ValuePos token.Pos // literal position 398 + Kind token.Token // INT, FLOAT, DURATION, or STRING 399 + Value string // literal string; e.g. 42, 0x7f, 3.14, 1_234_567, 1e-9, 2.4i, 'a', '\x7f', "foo", or '\m\n\o' 400 + } 401 + 402 + // A Interpolation node represents a string or bytes interpolation. 403 + type Interpolation struct { // TODO: rename to TemplateLit 404 + comments 405 + Elts []Expr // interleaving of strings and expressions. 406 + } 407 + 408 + // A StructLit node represents a literal struct. 409 + type StructLit struct { 410 + comments 411 + Lbrace token.Pos // position of "{" 412 + Elts []Decl // list of elements; or nil 413 + Rbrace token.Pos // position of "}" 414 + } 415 + 416 + // A ListLit node represents a literal list. 417 + type ListLit struct { 418 + comments 419 + Lbrack token.Pos // position of "[" 420 + Elts []Expr // list of composite elements; or nil 421 + Ellipsis token.Pos // open list if set 422 + Type Expr // type for the remaining elements 423 + Rbrack token.Pos // position of "]" 424 + } 425 + 426 + // A ListComprehension node represents as list comprehension. 427 + type ListComprehension struct { 428 + comments 429 + Lbrack token.Pos // position of "[" 430 + Expr Expr 431 + Clauses []Clause // Feed or Guard (TODO let) 432 + Rbrack token.Pos // position of "]" 433 + } 434 + 435 + // A ForClause node represents a for clause in a comprehension. 436 + type ForClause struct { 437 + comments 438 + For token.Pos 439 + Key *Ident // allow pattern matching? 440 + Colon token.Pos 441 + Value *Ident // allow pattern matching? 442 + In token.Pos 443 + Source Expr 444 + } 445 + 446 + // A IfClause node represents an if guard clause in a comprehension. 447 + type IfClause struct { 448 + comments 449 + If token.Pos 450 + Condition Expr 451 + } 452 + 453 + // A ParenExpr node represents a parenthesized expression. 454 + type ParenExpr struct { 455 + comments 456 + Lparen token.Pos // position of "(" 457 + X Expr // parenthesized expression 458 + Rparen token.Pos // position of ")" 459 + } 460 + 461 + // A SelectorExpr node represents an expression followed by a selector. 462 + type SelectorExpr struct { 463 + comments 464 + X Expr // expression 465 + Sel *Ident // field selector 466 + } 467 + 468 + // An IndexExpr node represents an expression followed by an index. 469 + type IndexExpr struct { 470 + comments 471 + X Expr // expression 472 + Lbrack token.Pos // position of "[" 473 + Index Expr // index expression 474 + Rbrack token.Pos // position of "]" 475 + } 476 + 477 + // An SliceExpr node represents an expression followed by slice indices. 478 + type SliceExpr struct { 479 + comments 480 + X Expr // expression 481 + Lbrack token.Pos // position of "[" 482 + Low Expr // begin of slice range; or nil 483 + High Expr // end of slice range; or nil 484 + Rbrack token.Pos // position of "]" 485 + } 486 + 487 + // A CallExpr node represents an expression followed by an argument list. 488 + type CallExpr struct { 489 + comments 490 + Fun Expr // function expression 491 + Lparen token.Pos // position of "(" 492 + Args []Expr // function arguments; or nil 493 + Rparen token.Pos // position of ")" 494 + } 495 + 496 + // A UnaryExpr node represents a unary expression. 497 + type UnaryExpr struct { 498 + comments 499 + OpPos token.Pos // position of Op 500 + Op token.Token // operator 501 + X Expr // operand 502 + } 503 + 504 + // A BinaryExpr node represents a binary expression. 505 + type BinaryExpr struct { 506 + comments 507 + X Expr // left operand 508 + OpPos token.Pos // position of Op 509 + Op token.Token // operator 510 + Y Expr // right operand 511 + } 512 + 513 + // token.Pos and End implementations for expression/type nodes. 514 + 515 + func (x *BadExpr) Pos() token.Pos { return x.From } 516 + func (x *Ident) Pos() token.Pos { return x.NamePos } 517 + func (x *TemplateLabel) Pos() token.Pos { return x.Langle } 518 + func (x *ExprLabel) Pos() token.Pos { return x.Lbrack } 519 + func (x *Ellipsis) Pos() token.Pos { return x.Ellipsis } 520 + func (x *BasicLit) Pos() token.Pos { return x.ValuePos } 521 + func (x *Interpolation) Pos() token.Pos { return x.Elts[0].Pos() } 522 + func (x *StructLit) Pos() token.Pos { 523 + if x.Lbrace == token.NoPos && len(x.Elts) > 0 { 524 + return x.Elts[0].Pos() 525 + } 526 + return x.Lbrace 527 + } 528 + 529 + func (x *ListLit) Pos() token.Pos { return x.Lbrack } 530 + func (x *ListComprehension) Pos() token.Pos { return x.Lbrack } 531 + func (x *ForClause) Pos() token.Pos { return x.For } 532 + func (x *IfClause) Pos() token.Pos { return x.If } 533 + func (x *ParenExpr) Pos() token.Pos { return x.Lparen } 534 + func (x *SelectorExpr) Pos() token.Pos { return x.X.Pos() } 535 + func (x *IndexExpr) Pos() token.Pos { return x.X.Pos() } 536 + func (x *SliceExpr) Pos() token.Pos { return x.X.Pos() } 537 + func (x *CallExpr) Pos() token.Pos { return x.Fun.Pos() } 538 + func (x *UnaryExpr) Pos() token.Pos { return x.OpPos } 539 + func (x *BinaryExpr) Pos() token.Pos { return x.X.Pos() } 540 + func (x *BottomLit) Pos() token.Pos { return x.Bottom } 541 + 542 + func (x *BadExpr) End() token.Pos { return x.To } 543 + func (x *Ident) End() token.Pos { 544 + return x.NamePos.Add(len(x.Name)) 545 + } 546 + func (x *TemplateLabel) End() token.Pos { return x.Rangle } 547 + func (x *ExprLabel) End() token.Pos { return x.Rbrack } 548 + func (x *Ellipsis) End() token.Pos { 549 + if x.Elt != nil { 550 + return x.Elt.End() 551 + } 552 + return x.Ellipsis + 3 // len("...") 553 + } 554 + func (x *BasicLit) End() token.Pos { return token.Pos(int(x.ValuePos) + len(x.Value)) } 555 + func (x *Interpolation) End() token.Pos { return x.Elts[len(x.Elts)-1].Pos() } 556 + func (x *StructLit) End() token.Pos { 557 + if x.Rbrace == token.NoPos && len(x.Elts) > 0 { 558 + return x.Elts[len(x.Elts)-1].Pos() 559 + } 560 + return x.Rbrace.Add(1) 561 + } 562 + func (x *ListLit) End() token.Pos { return x.Rbrack.Add(1) } 563 + func (x *ListComprehension) End() token.Pos { return x.Rbrack } 564 + func (x *ForClause) End() token.Pos { return x.Source.End() } 565 + func (x *IfClause) End() token.Pos { return x.Condition.End() } 566 + func (x *ParenExpr) End() token.Pos { return x.Rparen.Add(1) } 567 + func (x *SelectorExpr) End() token.Pos { return x.Sel.End() } 568 + func (x *IndexExpr) End() token.Pos { return x.Rbrack.Add(1) } 569 + func (x *SliceExpr) End() token.Pos { return x.Rbrack.Add(1) } 570 + func (x *CallExpr) End() token.Pos { return x.Rparen.Add(1) } 571 + func (x *UnaryExpr) End() token.Pos { return x.X.End() } 572 + func (x *BinaryExpr) End() token.Pos { return x.Y.End() } 573 + func (x *BottomLit) End() token.Pos { return x.Bottom.Add(1) } 574 + 575 + // ---------------------------------------------------------------------------- 576 + // Convenience functions for Idents 577 + 578 + // NewIdent creates a new Ident without position. 579 + // Useful for ASTs generated by code other than the Go 580 + func NewIdent(name string) *Ident { 581 + return &Ident{comments{}, token.NoPos, name, nil, nil} 582 + } 583 + 584 + func (id *Ident) String() string { 585 + if id != nil { 586 + return id.Name 587 + } 588 + return "<nil>" 589 + } 590 + 591 + // ---------------------------------------------------------------------------- 592 + // Declarations 593 + 594 + // An ImportSpec node represents a single package import. 595 + type ImportSpec struct { 596 + comments 597 + Name *Ident // local package name (including "."); or nil 598 + Path *BasicLit // import path 599 + EndPos token.Pos // end of spec (overrides Path.Pos if nonzero) 600 + } 601 + 602 + // Pos and End implementations for spec nodes. 603 + 604 + func (s *ImportSpec) Pos() token.Pos { 605 + if s.Name != nil { 606 + return s.Name.Pos() 607 + } 608 + return s.Path.Pos() 609 + } 610 + 611 + // func (s *AliasSpec) Pos() token.Pos { return s.Name.Pos() } 612 + // func (s *ValueSpec) Pos() token.Pos { return s.Names[0].Pos() } 613 + // func (s *TypeSpec) Pos() token.Pos { return s.Name.Pos() } 614 + 615 + func (s *ImportSpec) End() token.Pos { 616 + if s.EndPos != 0 { 617 + return s.EndPos 618 + } 619 + return s.Path.End() 620 + } 621 + 622 + // specNode() ensures that only spec nodes can be assigned to a Spec. 623 + func (*ImportSpec) specNode() {} 624 + 625 + // A declaration is represented by one of the following declaration nodes. 626 + type ( 627 + // A BadDecl node is a placeholder for declarations containing 628 + // syntax errors for which no correct declaration nodes can be 629 + // created. 630 + BadDecl struct { 631 + comments 632 + From, To token.Pos // position range of bad declaration 633 + } 634 + 635 + // A ImportDecl node represents a series of import declarations. A valid 636 + // Lparen position (Lparen.Line > 0) indicates a parenthesized declaration. 637 + ImportDecl struct { 638 + comments 639 + Import token.Pos 640 + Lparen token.Pos // position of '(', if any 641 + Specs []*ImportSpec 642 + Rparen token.Pos // position of ')', if any 643 + } 644 + 645 + // An EmitDecl node represents a single expression used as a declaration. 646 + // The expressions in this declaration is what will be emitted as 647 + // configuration output. 648 + // 649 + // An EmitDecl may only appear at the top level. 650 + EmitDecl struct { 651 + comments 652 + Expr Expr 653 + } 654 + ) 655 + 656 + // Pos and End implementations for declaration nodes. 657 + 658 + func (d *BadDecl) Pos() token.Pos { return d.From } 659 + func (d *ImportDecl) Pos() token.Pos { return d.Import } 660 + func (d *EmitDecl) Pos() token.Pos { return d.Expr.Pos() } 661 + 662 + func (d *BadDecl) End() token.Pos { return d.To } 663 + func (d *ImportDecl) End() token.Pos { 664 + if d.Rparen.IsValid() { 665 + return d.Rparen.Add(1) 666 + } 667 + return d.Specs[0].End() 668 + } 669 + func (d *EmitDecl) End() token.Pos { return d.Expr.End() } 670 + 671 + // ---------------------------------------------------------------------------- 672 + // Files and packages 673 + 674 + // A File node represents a Go source file. 675 + // 676 + // The Comments list contains all comments in the source file in order of 677 + // appearance, including the comments that are pointed to from other nodes 678 + // via Doc and Comment fields. 679 + type File struct { 680 + Filename string 681 + comments 682 + Package token.Pos // position of "package" pseudo-keyword 683 + Name *Ident // package names 684 + // TODO: Change Expr to Decl? 685 + Imports []*ImportSpec // imports in this file 686 + Decls []Decl // top-level declarations; or nil 687 + Unresolved []*Ident // unresolved identifiers in this file 688 + } 689 + 690 + func (f *File) Pos() token.Pos { 691 + if f.Package != token.NoPos { 692 + return f.Package 693 + } 694 + if len(f.Decls) > 0 { 695 + return f.Decls[0].Pos() 696 + } 697 + return token.NoPos 698 + } 699 + func (f *File) End() token.Pos { 700 + if n := len(f.Decls); n > 0 { 701 + return f.Decls[n-1].End() 702 + } 703 + if f.Name != nil { 704 + return f.Name.End() 705 + } 706 + return token.NoPos 707 + }
+60
cue/ast/ast_test.go
··· 1 + // 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 + package ast 16 + 17 + import ( 18 + "testing" 19 + ) 20 + 21 + func TestCommentText(t *testing.T) { 22 + testCases := []struct { 23 + list []string 24 + text string 25 + }{ 26 + {[]string{"//"}, ""}, 27 + {[]string{"// "}, ""}, 28 + {[]string{"//", "//", "// "}, ""}, 29 + {[]string{"// foo "}, "foo\n"}, 30 + {[]string{"//", "//", "// foo"}, "foo\n"}, 31 + {[]string{"// foo bar "}, "foo bar\n"}, 32 + {[]string{"// foo", "// bar"}, "foo\nbar\n"}, 33 + {[]string{"// foo", "//", "//", "//", "// bar"}, "foo\n\nbar\n"}, 34 + {[]string{"// foo", "/* bar */"}, "foo\n bar\n"}, 35 + {[]string{"//", "//", "//", "// foo", "//", "//", "//"}, "foo\n"}, 36 + 37 + {[]string{"/**/"}, ""}, 38 + {[]string{"/* */"}, ""}, 39 + {[]string{"/**/", "/**/", "/* */"}, ""}, 40 + {[]string{"/* Foo */"}, " Foo\n"}, 41 + {[]string{"/* Foo Bar */"}, " Foo Bar\n"}, 42 + {[]string{"/* Foo*/", "/* Bar*/"}, " Foo\n Bar\n"}, 43 + {[]string{"/* Foo*/", "/**/", "/**/", "/**/", "// Bar"}, " Foo\n\nBar\n"}, 44 + {[]string{"/* Foo*/", "/*\n*/", "//", "/*\n*/", "// Bar"}, " Foo\n\nBar\n"}, 45 + {[]string{"/* Foo*/", "// Bar"}, " Foo\nBar\n"}, 46 + {[]string{"/* Foo\n Bar*/"}, " Foo\n Bar\n"}, 47 + } 48 + 49 + for i, c := range testCases { 50 + list := make([]*Comment, len(c.list)) 51 + for i, s := range c.list { 52 + list[i] = &Comment{Text: s} 53 + } 54 + 55 + text := (&CommentGroup{List: list}).Text() 56 + if text != c.text { 57 + t.Errorf("case %d: got %q; expected %q", i, text, c.text) 58 + } 59 + } 60 + }
+287
cue/ast/walk.go
··· 1 + // 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 + package ast 16 + 17 + import ( 18 + "fmt" 19 + 20 + "cuelang.org/go/cue/token" 21 + ) 22 + 23 + // Walk traverses an AST in depth-first order: It starts by calling f(node); 24 + // node must not be nil. If before returns true, Walk invokes f recursively for 25 + // each of the non-nil children of node, followed by a call of after. Both 26 + // functions may be nil. If before is nil, it is assumed to always return true. 27 + // 28 + func Walk(node Node, before func(Node) bool, after func(Node)) { 29 + walk(&inspector{before: before, after: after}, node) 30 + } 31 + 32 + // A visitor's before method is invoked for each node encountered by Walk. 33 + // If the result visitor w is not nil, Walk visits each of the children 34 + // of node with the visitor w, followed by a call of w.After. 35 + type visitor interface { 36 + Before(node Node) (w visitor) 37 + After(node Node) 38 + } 39 + 40 + // Helper functions for common node lists. They may be empty. 41 + 42 + func walkIdentList(v visitor, list []*Ident) { 43 + for _, x := range list { 44 + walk(v, x) 45 + } 46 + } 47 + 48 + func walkExprList(v visitor, list []Expr) { 49 + for _, x := range list { 50 + walk(v, x) 51 + } 52 + } 53 + 54 + func walkDeclList(v visitor, list []Decl) { 55 + for _, x := range list { 56 + walk(v, x) 57 + } 58 + } 59 + 60 + // walk traverses an AST in depth-first order: It starts by calling 61 + // v.Visit(node); node must not be nil. If the visitor w returned by 62 + // v.Visit(node) is not nil, walk is invoked recursively with visitor 63 + // w for each of the non-nil children of node, followed by a call of 64 + // w.Visit(nil). 65 + // 66 + func walk(v visitor, node Node) { 67 + if v = v.Before(node); v == nil { 68 + return 69 + } 70 + 71 + // TODO: record the comment groups and interleave with the values like for 72 + // parsing and printing? 73 + for _, c := range node.Comments() { 74 + walk(v, c) 75 + } 76 + 77 + // walk children 78 + // (the order of the cases matches the order 79 + // of the corresponding node types in go) 80 + switch n := node.(type) { 81 + // Comments and fields 82 + case *Comment: 83 + // nothing to do 84 + 85 + case *CommentGroup: 86 + for _, c := range n.List { 87 + walk(v, c) 88 + } 89 + 90 + case *Field: 91 + walk(v, n.Label) 92 + if n.Value != nil { 93 + walk(v, n.Value) 94 + } 95 + 96 + case *LambdaExpr: 97 + for _, p := range n.Params { 98 + walk(v, p) 99 + } 100 + walk(v, n.Expr) 101 + 102 + case *StructLit: 103 + for _, f := range n.Elts { 104 + walk(v, f) 105 + } 106 + 107 + // Expressions 108 + case *BottomLit, *BadExpr, *Ident, *BasicLit: 109 + // nothing to do 110 + 111 + case *ExprLabel: 112 + walk(v, n.Label) 113 + 114 + case *TemplateLabel: 115 + walk(v, n.Ident) 116 + 117 + case *Interpolation: 118 + for _, e := range n.Elts { 119 + walk(v, e) 120 + } 121 + 122 + case *Ellipsis: 123 + if n.Elt != nil { 124 + walk(v, n.Elt) 125 + } 126 + 127 + case *ListLit: 128 + walkExprList(v, n.Elts) 129 + if n.Type != nil { 130 + walk(v, n.Type) 131 + } 132 + 133 + case *ParenExpr: 134 + walk(v, n.X) 135 + 136 + case *SelectorExpr: 137 + walk(v, n.X) 138 + walk(v, n.Sel) 139 + 140 + case *IndexExpr: 141 + walk(v, n.X) 142 + walk(v, n.Index) 143 + 144 + case *SliceExpr: 145 + walk(v, n.X) 146 + if n.Low != nil { 147 + walk(v, n.Low) 148 + } 149 + if n.High != nil { 150 + walk(v, n.High) 151 + } 152 + 153 + case *CallExpr: 154 + walk(v, n.Fun) 155 + walkExprList(v, n.Args) 156 + 157 + case *UnaryExpr: 158 + walk(v, n.X) 159 + 160 + case *BinaryExpr: 161 + walk(v, n.X) 162 + walk(v, n.Y) 163 + 164 + // Declarations 165 + case *ImportSpec: 166 + if n.Name != nil { 167 + walk(v, n.Name) 168 + } 169 + walk(v, n.Path) 170 + 171 + case *BadDecl: 172 + // nothing to do 173 + 174 + case *ImportDecl: 175 + for _, s := range n.Specs { 176 + walk(v, s) 177 + } 178 + 179 + case *EmitDecl: 180 + walk(v, n.Expr) 181 + 182 + case *Alias: 183 + walk(v, n.Ident) 184 + walk(v, n.Expr) 185 + 186 + case *ComprehensionDecl: 187 + walk(v, n.Field) 188 + for _, c := range n.Clauses { 189 + walk(v, c) 190 + } 191 + 192 + // Files and packages 193 + case *File: 194 + if n.Name != nil { 195 + walk(v, n.Name) 196 + } 197 + walkDeclList(v, n.Decls) 198 + // don't walk n.Comments - they have been 199 + // visited already through the individual 200 + // nodes 201 + 202 + case *ListComprehension: 203 + walk(v, n.Expr) 204 + for _, c := range n.Clauses { 205 + walk(v, c) 206 + } 207 + 208 + case *ForClause: 209 + if n.Key != nil { 210 + walk(v, n.Key) 211 + } 212 + walk(v, n.Value) 213 + walk(v, n.Source) 214 + 215 + case *IfClause: 216 + walk(v, n.Condition) 217 + 218 + default: 219 + panic(fmt.Sprintf("Walk: unexpected node type %T", n)) 220 + } 221 + 222 + v.After(node) 223 + } 224 + 225 + type inspector struct { 226 + before func(Node) bool 227 + after func(Node) 228 + 229 + commentStack []commentFrame 230 + current commentFrame 231 + } 232 + 233 + type commentFrame struct { 234 + cg []*CommentGroup 235 + pos int8 236 + } 237 + 238 + func (f *inspector) Before(node Node) visitor { 239 + if f.before == nil || f.before(node) { 240 + f.commentStack = append(f.commentStack, f.current) 241 + f.current = commentFrame{cg: node.Comments()} 242 + f.visitComments(f.current.pos) 243 + return f 244 + } 245 + return nil 246 + } 247 + 248 + func (f *inspector) After(node Node) { 249 + f.visitComments(127) 250 + p := len(f.commentStack) - 1 251 + f.current = f.commentStack[p] 252 + f.commentStack = f.commentStack[:p] 253 + f.current.pos++ 254 + if f.after != nil { 255 + f.after(node) 256 + } 257 + } 258 + 259 + func (f *inspector) Token(t token.Token) { 260 + f.current.pos++ 261 + } 262 + 263 + func (f *inspector) setPos(i int8) { 264 + f.current.pos = i 265 + } 266 + 267 + func (f *inspector) visitComments(pos int8) { 268 + c := &f.current 269 + for ; len(c.cg) > 0; c.cg = c.cg[1:] { 270 + cg := c.cg[0] 271 + if cg.Position == pos { 272 + continue 273 + } 274 + if f.before == nil || f.before(cg) { 275 + for _, c := range cg.List { 276 + if f.before == nil || f.before(c) { 277 + if f.after != nil { 278 + f.after(c) 279 + } 280 + } 281 + } 282 + if f.after != nil { 283 + f.after(cg) 284 + } 285 + } 286 + } 287 + }