| /* |
| Copyright 2020 Google LLC |
| |
| 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 |
| |
| https://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. |
| */ |
| |
| // This file contains functions to convert from one AST to the other. |
| // Input: AST from go.starlark.net/syntax |
| // Output: AST from github.com/bazelbuild/buildtools/build |
| |
| package convertast |
| |
| import ( |
| "fmt" |
| "strconv" |
| "strings" |
| |
| "github.com/bazelbuild/buildtools/build" |
| "go.starlark.net/syntax" |
| ) |
| |
| func ConvFile(f *syntax.File) *build.File { |
| stmts := []build.Expr{} |
| for _, stmt := range f.Stmts { |
| stmts = append(stmts, convStmt(stmt)) |
| } |
| |
| return &build.File{ |
| Type: build.TypeDefault, |
| Stmt: stmts, |
| Comments: convComments(f.Comments()), |
| } |
| } |
| |
| func convStmt(stmt syntax.Stmt) build.Expr { |
| switch stmt := stmt.(type) { |
| case *syntax.ExprStmt: |
| s := convExpr(stmt.X) |
| *s.Comment() = convComments(stmt.Comments()) |
| return s |
| case *syntax.BranchStmt: |
| return &build.BranchStmt{ |
| Token: stmt.Token.String(), |
| Comments: convComments(stmt.Comments()), |
| } |
| case *syntax.LoadStmt: |
| load := &build.LoadStmt{ |
| Module: convExpr(stmt.Module).(*build.StringExpr), |
| ForceCompact: singleLine(stmt), |
| } |
| for _, ident := range stmt.From { |
| load.From = append(load.From, convExpr(ident).(*build.Ident)) |
| } |
| for _, ident := range stmt.To { |
| load.To = append(load.To, convExpr(ident).(*build.Ident)) |
| } |
| return load |
| case *syntax.AssignStmt: |
| return &build.AssignExpr{ |
| Op: stmt.Op.String(), |
| LHS: convExpr(stmt.LHS), |
| RHS: convExpr(stmt.RHS), |
| Comments: convComments(stmt.Comments()), |
| } |
| case *syntax.IfStmt: |
| return &build.IfStmt{ |
| Cond: convExpr(stmt.Cond), |
| True: convStmts(stmt.True), |
| False: convStmts(stmt.False), |
| Comments: convComments(stmt.Comments()), |
| } |
| case *syntax.DefStmt: |
| return &build.DefStmt{ |
| Name: stmt.Name.Name, |
| Comments: convComments(stmt.Comments()), |
| Function: build.Function{ |
| Params: convExprs(stmt.Params), |
| Body: convStmts(stmt.Body), |
| }, |
| } |
| case *syntax.ForStmt: |
| return &build.ForStmt{ |
| Vars: convExpr(stmt.Vars), |
| X: convExpr(stmt.X), |
| Comments: convComments(stmt.Comments()), |
| Body: convStmts(stmt.Body), |
| } |
| case *syntax.ReturnStmt: |
| return &build.ReturnStmt{ |
| Comments: convComments(stmt.Comments()), |
| Result: convExpr(stmt.Result), |
| } |
| } |
| panic("unreachable") |
| } |
| |
| func convStmts(list []syntax.Stmt) []build.Expr { |
| res := []build.Expr{} |
| for _, i := range list { |
| res = append(res, convStmt(i)) |
| } |
| return res |
| } |
| |
| func convExprs(list []syntax.Expr) []build.Expr { |
| res := []build.Expr{} |
| for _, i := range list { |
| res = append(res, convExpr(i)) |
| } |
| return res |
| } |
| |
| func convCommentList(list []syntax.Comment, txt string) []build.Comment { |
| res := []build.Comment{} |
| for _, c := range list { |
| res = append(res, build.Comment{Token: c.Text}) |
| } |
| return res |
| } |
| |
| func convComments(c *syntax.Comments) build.Comments { |
| if c == nil { |
| return build.Comments{} |
| } |
| return build.Comments{ |
| Before: convCommentList(c.Before, "before"), |
| Suffix: convCommentList(c.Suffix, "suffix"), |
| After: convCommentList(c.After, "after"), |
| } |
| } |
| |
| // singleLine returns true if the node fits on a single line. |
| func singleLine(n syntax.Node) bool { |
| start, end := n.Span() |
| return start.Line == end.Line |
| } |
| |
| func convClauses(list []syntax.Node) []build.Expr { |
| res := []build.Expr{} |
| for _, c := range list { |
| switch stmt := c.(type) { |
| case *syntax.ForClause: |
| res = append(res, &build.ForClause{ |
| Vars: convExpr(stmt.Vars), |
| X: convExpr(stmt.X), |
| }) |
| case *syntax.IfClause: |
| res = append(res, &build.IfClause{ |
| Cond: convExpr(stmt.Cond), |
| }) |
| } |
| } |
| return res |
| } |
| |
| func convExpr(e syntax.Expr) build.Expr { |
| if e == nil { |
| return nil |
| } |
| switch e := e.(type) { |
| case *syntax.Literal: |
| switch e.Token { |
| case syntax.INT: |
| return &build.LiteralExpr{ |
| Token: strconv.FormatInt(e.Value.(int64), 10), |
| Comments: convComments(e.Comments())} |
| case syntax.FLOAT: |
| return &build.LiteralExpr{ |
| Token: e.Raw, |
| Comments: convComments(e.Comments())} |
| case syntax.STRING: |
| return &build.StringExpr{ |
| Value: e.Value.(string), |
| TripleQuote: strings.HasPrefix(e.Raw, "\"\"\""), |
| Comments: convComments(e.Comments())} |
| } |
| case *syntax.Ident: |
| return &build.Ident{Name: e.Name, Comments: convComments(e.Comments())} |
| case *syntax.BinaryExpr: |
| _, lhsEnd := e.X.Span() |
| rhsBegin, _ := e.Y.Span() |
| if e.Op.String() == "=" { |
| return &build.AssignExpr{ |
| LHS: convExpr(e.X), |
| RHS: convExpr(e.Y), |
| Op: e.Op.String(), |
| LineBreak: lhsEnd.Line != rhsBegin.Line, |
| Comments: convComments(e.Comments())} |
| } |
| return &build.BinaryExpr{ |
| X: convExpr(e.X), |
| Y: convExpr(e.Y), |
| Op: e.Op.String(), |
| LineBreak: lhsEnd.Line != rhsBegin.Line, |
| Comments: convComments(e.Comments())} |
| case *syntax.UnaryExpr: |
| return &build.UnaryExpr{Op: e.Op.String(), X: convExpr(e.X)} |
| case *syntax.SliceExpr: |
| return &build.SliceExpr{X: convExpr(e.X), From: convExpr(e.Lo), To: convExpr(e.Hi), Step: convExpr(e.Step)} |
| case *syntax.DotExpr: |
| return &build.DotExpr{X: convExpr(e.X), Name: e.Name.Name} |
| case *syntax.CallExpr: |
| args := []build.Expr{} |
| for _, a := range e.Args { |
| args = append(args, convExpr(a)) |
| } |
| return &build.CallExpr{ |
| X: convExpr(e.Fn), |
| List: args, |
| ForceCompact: singleLine(e), |
| } |
| case *syntax.ListExpr: |
| list := []build.Expr{} |
| for _, i := range e.List { |
| list = append(list, convExpr(i)) |
| } |
| return &build.ListExpr{List: list, Comments: convComments(e.Comments())} |
| case *syntax.DictExpr: |
| list := []*build.KeyValueExpr{} |
| for i := range e.List { |
| entry := e.List[i].(*syntax.DictEntry) |
| list = append(list, &build.KeyValueExpr{ |
| Key: convExpr(entry.Key), |
| Value: convExpr(entry.Value), |
| Comments: convComments(entry.Comments()), |
| }) |
| } |
| return &build.DictExpr{List: list, Comments: convComments(e.Comments())} |
| case *syntax.CondExpr: |
| return &build.ConditionalExpr{ |
| Then: convExpr(e.True), |
| Test: convExpr(e.Cond), |
| Else: convExpr(e.False), |
| Comments: convComments(e.Comments()), |
| } |
| case *syntax.Comprehension: |
| return &build.Comprehension{ |
| Body: convExpr(e.Body), |
| Clauses: convClauses(e.Clauses), |
| Comments: convComments(e.Comments()), |
| Curly: e.Curly, |
| } |
| case *syntax.ParenExpr: |
| return &build.ParenExpr{ |
| X: convExpr(e.X), |
| Comments: convComments(e.Comments()), |
| } |
| case *syntax.TupleExpr: |
| return &build.TupleExpr{ |
| List: convExprs(e.List), |
| NoBrackets: !e.Lparen.IsValid(), |
| Comments: convComments(e.Comments()), |
| ForceCompact: singleLine(e), |
| } |
| case *syntax.IndexExpr: |
| return &build.IndexExpr{ |
| X: convExpr(e.X), |
| Y: convExpr(e.Y), |
| Comments: convComments(e.Comments()), |
| } |
| case *syntax.LambdaExpr: |
| return &build.LambdaExpr{ |
| Comments: convComments(e.Comments()), |
| Function: build.Function{ |
| Params: convExprs(e.Params), |
| Body: []build.Expr{convExpr(e.Body)}, |
| }, |
| } |
| default: |
| panic(fmt.Sprintf("other expr: %T %+v", e, e)) |
| } |
| panic("unreachable") |
| } |