...

Source file src/github.com/bazelbuild/buildtools/convertast/convert_ast.go

Documentation: github.com/bazelbuild/buildtools/convertast

     1  /*
     2  Copyright 2020 Google LLC
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      https://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // This file contains functions to convert from one AST to the other.
    18  // Input: AST from go.starlark.net/syntax
    19  // Output: AST from github.com/bazelbuild/buildtools/build
    20  
    21  package convertast
    22  
    23  import (
    24  	"fmt"
    25  	"strconv"
    26  	"strings"
    27  
    28  	"github.com/bazelbuild/buildtools/build"
    29  	"go.starlark.net/syntax"
    30  )
    31  
    32  func ConvFile(f *syntax.File) *build.File {
    33  	stmts := []build.Expr{}
    34  	for _, stmt := range f.Stmts {
    35  		stmts = append(stmts, convStmt(stmt))
    36  	}
    37  
    38  	return &build.File{
    39  		Type:     build.TypeDefault,
    40  		Stmt:     stmts,
    41  		Comments: convComments(f.Comments()),
    42  	}
    43  }
    44  
    45  func convStmt(stmt syntax.Stmt) build.Expr {
    46  	switch stmt := stmt.(type) {
    47  	case *syntax.ExprStmt:
    48  		s := convExpr(stmt.X)
    49  		*s.Comment() = convComments(stmt.Comments())
    50  		return s
    51  	case *syntax.BranchStmt:
    52  		return &build.BranchStmt{
    53  			Token:    stmt.Token.String(),
    54  			Comments: convComments(stmt.Comments()),
    55  		}
    56  	case *syntax.LoadStmt:
    57  		load := &build.LoadStmt{
    58  			Module:       convExpr(stmt.Module).(*build.StringExpr),
    59  			ForceCompact: singleLine(stmt),
    60  		}
    61  		for _, ident := range stmt.From {
    62  			load.From = append(load.From, convExpr(ident).(*build.Ident))
    63  		}
    64  		for _, ident := range stmt.To {
    65  			load.To = append(load.To, convExpr(ident).(*build.Ident))
    66  		}
    67  		return load
    68  	case *syntax.AssignStmt:
    69  		return &build.AssignExpr{
    70  			Op:       stmt.Op.String(),
    71  			LHS:      convExpr(stmt.LHS),
    72  			RHS:      convExpr(stmt.RHS),
    73  			Comments: convComments(stmt.Comments()),
    74  		}
    75  	case *syntax.IfStmt:
    76  		return &build.IfStmt{
    77  			Cond:     convExpr(stmt.Cond),
    78  			True:     convStmts(stmt.True),
    79  			False:    convStmts(stmt.False),
    80  			Comments: convComments(stmt.Comments()),
    81  		}
    82  	case *syntax.DefStmt:
    83  		return &build.DefStmt{
    84  			Name:     stmt.Name.Name,
    85  			Comments: convComments(stmt.Comments()),
    86  			Function: build.Function{
    87  				Params: convExprs(stmt.Params),
    88  				Body:   convStmts(stmt.Body),
    89  			},
    90  		}
    91  	case *syntax.ForStmt:
    92  		return &build.ForStmt{
    93  			Vars:     convExpr(stmt.Vars),
    94  			X:        convExpr(stmt.X),
    95  			Comments: convComments(stmt.Comments()),
    96  			Body:     convStmts(stmt.Body),
    97  		}
    98  	case *syntax.ReturnStmt:
    99  		return &build.ReturnStmt{
   100  			Comments: convComments(stmt.Comments()),
   101  			Result:   convExpr(stmt.Result),
   102  		}
   103  	}
   104  	panic("unreachable")
   105  }
   106  
   107  func convStmts(list []syntax.Stmt) []build.Expr {
   108  	res := []build.Expr{}
   109  	for _, i := range list {
   110  		res = append(res, convStmt(i))
   111  	}
   112  	return res
   113  }
   114  
   115  func convExprs(list []syntax.Expr) []build.Expr {
   116  	res := []build.Expr{}
   117  	for _, i := range list {
   118  		res = append(res, convExpr(i))
   119  	}
   120  	return res
   121  }
   122  
   123  func convCommentList(list []syntax.Comment, txt string) []build.Comment {
   124  	res := []build.Comment{}
   125  	for _, c := range list {
   126  		res = append(res, build.Comment{Token: c.Text})
   127  	}
   128  	return res
   129  }
   130  
   131  func convComments(c *syntax.Comments) build.Comments {
   132  	if c == nil {
   133  		return build.Comments{}
   134  	}
   135  	return build.Comments{
   136  		Before: convCommentList(c.Before, "before"),
   137  		Suffix: convCommentList(c.Suffix, "suffix"),
   138  		After:  convCommentList(c.After, "after"),
   139  	}
   140  }
   141  
   142  // singleLine returns true if the node fits on a single line.
   143  func singleLine(n syntax.Node) bool {
   144  	start, end := n.Span()
   145  	return start.Line == end.Line
   146  }
   147  
   148  func convClauses(list []syntax.Node) []build.Expr {
   149  	res := []build.Expr{}
   150  	for _, c := range list {
   151  		switch stmt := c.(type) {
   152  		case *syntax.ForClause:
   153  			res = append(res, &build.ForClause{
   154  				Vars: convExpr(stmt.Vars),
   155  				X:    convExpr(stmt.X),
   156  			})
   157  		case *syntax.IfClause:
   158  			res = append(res, &build.IfClause{
   159  				Cond: convExpr(stmt.Cond),
   160  			})
   161  		}
   162  	}
   163  	return res
   164  }
   165  
   166  func convExpr(e syntax.Expr) build.Expr {
   167  	if e == nil {
   168  		return nil
   169  	}
   170  	switch e := e.(type) {
   171  	case *syntax.Literal:
   172  		switch e.Token {
   173  		case syntax.INT:
   174  			return &build.LiteralExpr{
   175  				Token:    strconv.FormatInt(e.Value.(int64), 10),
   176  				Comments: convComments(e.Comments())}
   177  		case syntax.FLOAT:
   178  			return &build.LiteralExpr{
   179  				Token:    e.Raw,
   180  				Comments: convComments(e.Comments())}
   181  		case syntax.STRING:
   182  			return &build.StringExpr{
   183  				Value:       e.Value.(string),
   184  				TripleQuote: strings.HasPrefix(e.Raw, "\"\"\""),
   185  				Comments:    convComments(e.Comments())}
   186  		}
   187  	case *syntax.Ident:
   188  		return &build.Ident{Name: e.Name, Comments: convComments(e.Comments())}
   189  	case *syntax.BinaryExpr:
   190  		_, lhsEnd := e.X.Span()
   191  		rhsBegin, _ := e.Y.Span()
   192  		if e.Op.String() == "=" {
   193  			return &build.AssignExpr{
   194  				LHS:       convExpr(e.X),
   195  				RHS:       convExpr(e.Y),
   196  				Op:        e.Op.String(),
   197  				LineBreak: lhsEnd.Line != rhsBegin.Line,
   198  				Comments:  convComments(e.Comments())}
   199  		}
   200  		return &build.BinaryExpr{
   201  			X:         convExpr(e.X),
   202  			Y:         convExpr(e.Y),
   203  			Op:        e.Op.String(),
   204  			LineBreak: lhsEnd.Line != rhsBegin.Line,
   205  			Comments:  convComments(e.Comments())}
   206  	case *syntax.UnaryExpr:
   207  		return &build.UnaryExpr{Op: e.Op.String(), X: convExpr(e.X)}
   208  	case *syntax.SliceExpr:
   209  		return &build.SliceExpr{X: convExpr(e.X), From: convExpr(e.Lo), To: convExpr(e.Hi), Step: convExpr(e.Step)}
   210  	case *syntax.DotExpr:
   211  		return &build.DotExpr{X: convExpr(e.X), Name: e.Name.Name}
   212  	case *syntax.CallExpr:
   213  		args := []build.Expr{}
   214  		for _, a := range e.Args {
   215  			args = append(args, convExpr(a))
   216  		}
   217  		return &build.CallExpr{
   218  			X:            convExpr(e.Fn),
   219  			List:         args,
   220  			ForceCompact: singleLine(e),
   221  		}
   222  	case *syntax.ListExpr:
   223  		list := []build.Expr{}
   224  		for _, i := range e.List {
   225  			list = append(list, convExpr(i))
   226  		}
   227  		return &build.ListExpr{List: list, Comments: convComments(e.Comments())}
   228  	case *syntax.DictExpr:
   229  		list := []*build.KeyValueExpr{}
   230  		for i := range e.List {
   231  			entry := e.List[i].(*syntax.DictEntry)
   232  			list = append(list, &build.KeyValueExpr{
   233  				Key:      convExpr(entry.Key),
   234  				Value:    convExpr(entry.Value),
   235  				Comments: convComments(entry.Comments()),
   236  			})
   237  		}
   238  		return &build.DictExpr{List: list, Comments: convComments(e.Comments())}
   239  	case *syntax.CondExpr:
   240  		return &build.ConditionalExpr{
   241  			Then:     convExpr(e.True),
   242  			Test:     convExpr(e.Cond),
   243  			Else:     convExpr(e.False),
   244  			Comments: convComments(e.Comments()),
   245  		}
   246  	case *syntax.Comprehension:
   247  		return &build.Comprehension{
   248  			Body:     convExpr(e.Body),
   249  			Clauses:  convClauses(e.Clauses),
   250  			Comments: convComments(e.Comments()),
   251  			Curly:    e.Curly,
   252  		}
   253  	case *syntax.ParenExpr:
   254  		return &build.ParenExpr{
   255  			X:        convExpr(e.X),
   256  			Comments: convComments(e.Comments()),
   257  		}
   258  	case *syntax.TupleExpr:
   259  		return &build.TupleExpr{
   260  			List:         convExprs(e.List),
   261  			NoBrackets:   !e.Lparen.IsValid(),
   262  			Comments:     convComments(e.Comments()),
   263  			ForceCompact: singleLine(e),
   264  		}
   265  	case *syntax.IndexExpr:
   266  		return &build.IndexExpr{
   267  			X:        convExpr(e.X),
   268  			Y:        convExpr(e.Y),
   269  			Comments: convComments(e.Comments()),
   270  		}
   271  	case *syntax.LambdaExpr:
   272  		return &build.LambdaExpr{
   273  			Comments: convComments(e.Comments()),
   274  			Function: build.Function{
   275  				Params: convExprs(e.Params),
   276  				Body:   []build.Expr{convExpr(e.Body)},
   277  			},
   278  		}
   279  	default:
   280  		panic(fmt.Sprintf("other expr: %T %+v", e, e))
   281  	}
   282  	panic("unreachable")
   283  }
   284  

View as plain text