blob: 27a288279911d222fc67f7135d536f6d479d2d99 [file]
// This file contains functions to convert from one AST to the other.
// Input: AST from github.com/google/skylark/syntax
// Output: AST from github.com/bazelbuild/buildtools/build
package convertast
import (
"log"
"strconv"
"strings"
"github.com/bazelbuild/buildtools/build"
"github.com/google/skylark/syntax"
)
func ConvFile(f *syntax.File) *build.File {
stmts := []build.Expr{}
for _, stmt := range f.Stmts {
stmts = append(stmts, convStmt(stmt))
}
return &build.File{
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.LiteralExpr{
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.BinaryExpr{
Op: stmt.Op.String(),
X: convExpr(stmt.LHS),
Y: 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.Function.Params),
Body: convStmts(stmt.Function.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:
log.Fatal("float not yet supported")
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()
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.Expr{}
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()),
}
}
panic("other expr")
}