| // Copyright 2017 The Bazel Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Package syntax provides a Starlark parser and abstract syntax tree. |
| package syntax // import "go.starlark.net/syntax" |
| |
| // A Node is a node in a Starlark syntax tree. |
| type Node interface { |
| // Span returns the start and end position of the expression. |
| Span() (start, end Position) |
| |
| // Comments returns the comments associated with this node. |
| // It returns nil if RetainComments was not specified during parsing, |
| // or if AllocComments was not called. |
| Comments() *Comments |
| |
| // AllocComments allocates a new Comments node if there was none. |
| // This makes possible to add new comments using Comments() method. |
| AllocComments() |
| } |
| |
| // A Comment represents a single # comment. |
| type Comment struct { |
| Start Position |
| Text string // without trailing newline |
| } |
| |
| // Comments collects the comments associated with an expression. |
| type Comments struct { |
| Before []Comment // whole-line comments before this expression |
| Suffix []Comment // end-of-line comments after this expression (up to 1) |
| |
| // For top-level expressions only, After lists whole-line |
| // comments following the expression. |
| After []Comment |
| } |
| |
| // A commentsRef is a possibly-nil reference to a set of comments. |
| // A commentsRef is embedded in each type of syntax node, |
| // and provides its Comments and AllocComments methods. |
| type commentsRef struct{ ref *Comments } |
| |
| // Comments returns the comments associated with a syntax node, |
| // or nil if AllocComments has not yet been called. |
| func (cr commentsRef) Comments() *Comments { return cr.ref } |
| |
| // AllocComments enables comments to be associated with a syntax node. |
| func (cr *commentsRef) AllocComments() { |
| if cr.ref == nil { |
| cr.ref = new(Comments) |
| } |
| } |
| |
| // Start returns the start position of the expression. |
| func Start(n Node) Position { |
| start, _ := n.Span() |
| return start |
| } |
| |
| // End returns the end position of the expression. |
| func End(n Node) Position { |
| _, end := n.Span() |
| return end |
| } |
| |
| // A File represents a Starlark file. |
| type File struct { |
| commentsRef |
| Path string |
| Stmts []Stmt |
| |
| Module interface{} // a *resolve.Module, set by resolver |
| } |
| |
| func (x *File) Span() (start, end Position) { |
| if len(x.Stmts) == 0 { |
| return |
| } |
| start, _ = x.Stmts[0].Span() |
| _, end = x.Stmts[len(x.Stmts)-1].Span() |
| return start, end |
| } |
| |
| // A Stmt is a Starlark statement. |
| type Stmt interface { |
| Node |
| stmt() |
| } |
| |
| func (*AssignStmt) stmt() {} |
| func (*BranchStmt) stmt() {} |
| func (*DefStmt) stmt() {} |
| func (*ExprStmt) stmt() {} |
| func (*ForStmt) stmt() {} |
| func (*WhileStmt) stmt() {} |
| func (*IfStmt) stmt() {} |
| func (*LoadStmt) stmt() {} |
| func (*ReturnStmt) stmt() {} |
| |
| // An AssignStmt represents an assignment: |
| // x = 0 |
| // x, y = y, x |
| // x += 1 |
| type AssignStmt struct { |
| commentsRef |
| OpPos Position |
| Op Token // = EQ | {PLUS,MINUS,STAR,PERCENT}_EQ |
| LHS Expr |
| RHS Expr |
| } |
| |
| func (x *AssignStmt) Span() (start, end Position) { |
| start, _ = x.LHS.Span() |
| _, end = x.RHS.Span() |
| return |
| } |
| |
| // A DefStmt represents a function definition. |
| type DefStmt struct { |
| commentsRef |
| Def Position |
| Name *Ident |
| Params []Expr // param = ident | ident=expr | * | *ident | **ident |
| Body []Stmt |
| |
| Function interface{} // a *resolve.Function, set by resolver |
| } |
| |
| func (x *DefStmt) Span() (start, end Position) { |
| _, end = x.Body[len(x.Body)-1].Span() |
| return x.Def, end |
| } |
| |
| // An ExprStmt is an expression evaluated for side effects. |
| type ExprStmt struct { |
| commentsRef |
| X Expr |
| } |
| |
| func (x *ExprStmt) Span() (start, end Position) { |
| return x.X.Span() |
| } |
| |
| // An IfStmt is a conditional: If Cond: True; else: False. |
| // 'elseif' is desugared into a chain of IfStmts. |
| type IfStmt struct { |
| commentsRef |
| If Position // IF or ELIF |
| Cond Expr |
| True []Stmt |
| ElsePos Position // ELSE or ELIF |
| False []Stmt // optional |
| } |
| |
| func (x *IfStmt) Span() (start, end Position) { |
| body := x.False |
| if body == nil { |
| body = x.True |
| } |
| _, end = body[len(body)-1].Span() |
| return x.If, end |
| } |
| |
| // A LoadStmt loads another module and binds names from it: |
| // load(Module, "x", y="foo"). |
| // |
| // The AST is slightly unfaithful to the concrete syntax here because |
| // Starlark's load statement, so that it can be implemented in Python, |
| // binds some names (like y above) with an identifier and some (like x) |
| // without. For consistency we create fake identifiers for all the |
| // strings. |
| type LoadStmt struct { |
| commentsRef |
| Load Position |
| Module *Literal // a string |
| From []*Ident // name defined in loading module |
| To []*Ident // name in loaded module |
| Rparen Position |
| } |
| |
| func (x *LoadStmt) Span() (start, end Position) { |
| return x.Load, x.Rparen |
| } |
| |
| // ModuleName returns the name of the module loaded by this statement. |
| func (x *LoadStmt) ModuleName() string { return x.Module.Value.(string) } |
| |
| // A BranchStmt changes the flow of control: break, continue, pass. |
| type BranchStmt struct { |
| commentsRef |
| Token Token // = BREAK | CONTINUE | PASS |
| TokenPos Position |
| } |
| |
| func (x *BranchStmt) Span() (start, end Position) { |
| return x.TokenPos, x.TokenPos.add(x.Token.String()) |
| } |
| |
| // A ReturnStmt returns from a function. |
| type ReturnStmt struct { |
| commentsRef |
| Return Position |
| Result Expr // may be nil |
| } |
| |
| func (x *ReturnStmt) Span() (start, end Position) { |
| if x.Result == nil { |
| return x.Return, x.Return.add("return") |
| } |
| _, end = x.Result.Span() |
| return x.Return, end |
| } |
| |
| // An Expr is a Starlark expression. |
| type Expr interface { |
| Node |
| expr() |
| } |
| |
| func (*BinaryExpr) expr() {} |
| func (*CallExpr) expr() {} |
| func (*Comprehension) expr() {} |
| func (*CondExpr) expr() {} |
| func (*DictEntry) expr() {} |
| func (*DictExpr) expr() {} |
| func (*DotExpr) expr() {} |
| func (*Ident) expr() {} |
| func (*IndexExpr) expr() {} |
| func (*LambdaExpr) expr() {} |
| func (*ListExpr) expr() {} |
| func (*Literal) expr() {} |
| func (*ParenExpr) expr() {} |
| func (*SliceExpr) expr() {} |
| func (*TupleExpr) expr() {} |
| func (*UnaryExpr) expr() {} |
| |
| // An Ident represents an identifier. |
| type Ident struct { |
| commentsRef |
| NamePos Position |
| Name string |
| |
| Binding interface{} // a *resolver.Binding, set by resolver |
| } |
| |
| func (x *Ident) Span() (start, end Position) { |
| return x.NamePos, x.NamePos.add(x.Name) |
| } |
| |
| // A Literal represents a literal string or number. |
| type Literal struct { |
| commentsRef |
| Token Token // = STRING | INT |
| TokenPos Position |
| Raw string // uninterpreted text |
| Value interface{} // = string | int64 | *big.Int |
| } |
| |
| func (x *Literal) Span() (start, end Position) { |
| return x.TokenPos, x.TokenPos.add(x.Raw) |
| } |
| |
| // A ParenExpr represents a parenthesized expression: (X). |
| type ParenExpr struct { |
| commentsRef |
| Lparen Position |
| X Expr |
| Rparen Position |
| } |
| |
| func (x *ParenExpr) Span() (start, end Position) { |
| return x.Lparen, x.Rparen.add(")") |
| } |
| |
| // A CallExpr represents a function call expression: Fn(Args). |
| type CallExpr struct { |
| commentsRef |
| Fn Expr |
| Lparen Position |
| Args []Expr // arg = expr | ident=expr | *expr | **expr |
| Rparen Position |
| } |
| |
| func (x *CallExpr) Span() (start, end Position) { |
| start, _ = x.Fn.Span() |
| return start, x.Rparen.add(")") |
| } |
| |
| // A DotExpr represents a field or method selector: X.Name. |
| type DotExpr struct { |
| commentsRef |
| X Expr |
| Dot Position |
| NamePos Position |
| Name *Ident |
| } |
| |
| func (x *DotExpr) Span() (start, end Position) { |
| start, _ = x.X.Span() |
| _, end = x.Name.Span() |
| return |
| } |
| |
| // A Comprehension represents a list or dict comprehension: |
| // [Body for ... if ...] or {Body for ... if ...} |
| type Comprehension struct { |
| commentsRef |
| Curly bool // {x:y for ...} or {x for ...}, not [x for ...] |
| Lbrack Position |
| Body Expr |
| Clauses []Node // = *ForClause | *IfClause |
| Rbrack Position |
| } |
| |
| func (x *Comprehension) Span() (start, end Position) { |
| return x.Lbrack, x.Rbrack.add("]") |
| } |
| |
| // A ForStmt represents a loop: for Vars in X: Body. |
| type ForStmt struct { |
| commentsRef |
| For Position |
| Vars Expr // name, or tuple of names |
| X Expr |
| Body []Stmt |
| } |
| |
| func (x *ForStmt) Span() (start, end Position) { |
| _, end = x.Body[len(x.Body)-1].Span() |
| return x.For, end |
| } |
| |
| // A WhileStmt represents a while loop: while X: Body. |
| type WhileStmt struct { |
| commentsRef |
| While Position |
| Cond Expr |
| Body []Stmt |
| } |
| |
| func (x *WhileStmt) Span() (start, end Position) { |
| _, end = x.Body[len(x.Body)-1].Span() |
| return x.While, end |
| } |
| |
| // A ForClause represents a for clause in a list comprehension: for Vars in X. |
| type ForClause struct { |
| commentsRef |
| For Position |
| Vars Expr // name, or tuple of names |
| In Position |
| X Expr |
| } |
| |
| func (x *ForClause) Span() (start, end Position) { |
| _, end = x.X.Span() |
| return x.For, end |
| } |
| |
| // An IfClause represents an if clause in a list comprehension: if Cond. |
| type IfClause struct { |
| commentsRef |
| If Position |
| Cond Expr |
| } |
| |
| func (x *IfClause) Span() (start, end Position) { |
| _, end = x.Cond.Span() |
| return x.If, end |
| } |
| |
| // A DictExpr represents a dictionary literal: { List }. |
| type DictExpr struct { |
| commentsRef |
| Lbrace Position |
| List []Expr // all *DictEntrys |
| Rbrace Position |
| } |
| |
| func (x *DictExpr) Span() (start, end Position) { |
| return x.Lbrace, x.Rbrace.add("}") |
| } |
| |
| // A DictEntry represents a dictionary entry: Key: Value. |
| // Used only within a DictExpr. |
| type DictEntry struct { |
| commentsRef |
| Key Expr |
| Colon Position |
| Value Expr |
| } |
| |
| func (x *DictEntry) Span() (start, end Position) { |
| start, _ = x.Key.Span() |
| _, end = x.Value.Span() |
| return start, end |
| } |
| |
| // A LambdaExpr represents an inline function abstraction. |
| // |
| // Although they may be added in future, lambda expressions are not |
| // currently part of the Starlark spec, so their use is controlled by the |
| // resolver.AllowLambda flag. |
| type LambdaExpr struct { |
| commentsRef |
| Lambda Position |
| Params []Expr // param = ident | ident=expr | * | *ident | **ident |
| Body Expr |
| |
| Function interface{} // a *resolve.Function, set by resolver |
| } |
| |
| func (x *LambdaExpr) Span() (start, end Position) { |
| _, end = x.Body.Span() |
| return x.Lambda, end |
| } |
| |
| // A ListExpr represents a list literal: [ List ]. |
| type ListExpr struct { |
| commentsRef |
| Lbrack Position |
| List []Expr |
| Rbrack Position |
| } |
| |
| func (x *ListExpr) Span() (start, end Position) { |
| return x.Lbrack, x.Rbrack.add("]") |
| } |
| |
| // CondExpr represents the conditional: X if COND else ELSE. |
| type CondExpr struct { |
| commentsRef |
| If Position |
| Cond Expr |
| True Expr |
| ElsePos Position |
| False Expr |
| } |
| |
| func (x *CondExpr) Span() (start, end Position) { |
| start, _ = x.True.Span() |
| _, end = x.False.Span() |
| return start, end |
| } |
| |
| // A TupleExpr represents a tuple literal: (List). |
| type TupleExpr struct { |
| commentsRef |
| Lparen Position // optional (e.g. in x, y = 0, 1), but required if List is empty |
| List []Expr |
| Rparen Position |
| } |
| |
| func (x *TupleExpr) Span() (start, end Position) { |
| if x.Lparen.IsValid() { |
| return x.Lparen, x.Rparen |
| } else { |
| return Start(x.List[0]), End(x.List[len(x.List)-1]) |
| } |
| } |
| |
| // A UnaryExpr represents a unary expression: Op X. |
| // |
| // As a special case, UnaryOp{Op:Star} may also represent |
| // the star parameter in def f(*args) or def f(*, x). |
| type UnaryExpr struct { |
| commentsRef |
| OpPos Position |
| Op Token |
| X Expr // may be nil if Op==STAR |
| } |
| |
| func (x *UnaryExpr) Span() (start, end Position) { |
| if x.X != nil { |
| _, end = x.X.Span() |
| } else { |
| end = x.OpPos.add("*") |
| } |
| return x.OpPos, end |
| } |
| |
| // A BinaryExpr represents a binary expression: X Op Y. |
| // |
| // As a special case, BinaryExpr{Op:EQ} may also |
| // represent a named argument in a call f(k=v) |
| // or a named parameter in a function declaration |
| // def f(param=default). |
| type BinaryExpr struct { |
| commentsRef |
| X Expr |
| OpPos Position |
| Op Token |
| Y Expr |
| } |
| |
| func (x *BinaryExpr) Span() (start, end Position) { |
| start, _ = x.X.Span() |
| _, end = x.Y.Span() |
| return start, end |
| } |
| |
| // A SliceExpr represents a slice or substring expression: X[Lo:Hi:Step]. |
| type SliceExpr struct { |
| commentsRef |
| X Expr |
| Lbrack Position |
| Lo, Hi, Step Expr // all optional |
| Rbrack Position |
| } |
| |
| func (x *SliceExpr) Span() (start, end Position) { |
| start, _ = x.X.Span() |
| return start, x.Rbrack |
| } |
| |
| // An IndexExpr represents an index expression: X[Y]. |
| type IndexExpr struct { |
| commentsRef |
| X Expr |
| Lbrack Position |
| Y Expr |
| Rbrack Position |
| } |
| |
| func (x *IndexExpr) Span() (start, end Position) { |
| start, _ = x.X.Span() |
| return start, x.Rbrack |
| } |