-
Notifications
You must be signed in to change notification settings - Fork 5
/
java2go.go
196 lines (159 loc) · 4.73 KB
/
java2go.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package main
import (
"flag"
"fmt"
"go/ast"
"go/printer"
"go/token"
"io"
"os"
"path/filepath"
"strings"
"sync"
"github.com/NickyBoy89/java2go/parsing"
"github.com/NickyBoy89/java2go/symbol"
log "github.com/sirupsen/logrus"
)
// Stores a global list of Java annotations to exclude from the generated code
var excludedAnnotations = make(map[string]bool)
// Command-line arguments
var (
writeFiles bool
dryRun bool
displayAST bool
symbolAware bool
parseFilesSynchronously bool
)
var (
outputDirectory string
ignoredAnnotations string
)
func main() {
flag.BoolVar(&writeFiles, "w", false, "Whether to write the files to disk instead of stdout")
flag.BoolVar(&dryRun, "q", false, "Don't write to stdout on successful parse")
flag.BoolVar(&displayAST, "ast", false, "Print out go's pretty-printed ast, instead of source code")
flag.BoolVar(&parseFilesSynchronously, "sync", false, "Parse the files one by one, instead of in parallel")
flag.BoolVar(&symbolAware, "symbols", true, `Whether the program is aware of the symbols of the parsed code
Results in better code generation, but can be disabled for a more direct translation
or to fix crashes with the symbol handling`,
)
flag.StringVar(&outputDirectory, "output", ".", "Specify a directory for the generated files")
flag.StringVar(&ignoredAnnotations, "exclude-annotations", "", "A comma-separated list of annotations to exclude from the final code generation")
flag.Parse()
for _, annotation := range strings.Split(ignoredAnnotations, ",") {
excludedAnnotations[annotation] = true
}
// All the files to parse
var files []parsing.SourceFile
log.Info("Collecting files...")
// Collect all the files and read them into memory
for _, dirName := range flag.Args() {
sources, err := parsing.ReadSourcesInDir(dirName)
if err != nil {
log.WithField("error", err).Fatal("Error reading directory")
}
files = append(files, sources...)
}
if len(files) == 0 {
log.Warn("No files specified to convert")
}
// Parse the ASTs of all the files
log.Info("Parsing ASTs...")
var wg sync.WaitGroup
wg.Add(len(files))
for index := range files {
parseFunc := func(ind int) {
if err := files[ind].ParseAST(); err != nil {
log.WithField("error", err).Error("Error parsing AST")
}
wg.Done()
}
if parseFilesSynchronously {
parseFunc(index)
} else {
go parseFunc(index)
}
}
// We might still have some parsing jobs, so wait on them
wg.Wait()
for _, file := range files {
if file.Ast == nil {
panic("Not all files have asts")
}
}
// Generate the symbol tables for the files
if symbolAware {
log.Info("Generating symbol tables...")
for index, file := range files {
if file.Ast.HasError() {
log.WithFields(log.Fields{
"fileName": file.Name,
}).Warn("AST parse error in file, skipping file")
continue
}
symbols := files[index].ParseSymbols()
// Add the symbols to the global symbol table
symbol.AddSymbolsToPackage(symbols)
}
// Go back through the symbol tables and fill in anything that could not be resolved
log.Info("Resolving symbols...")
for _, file := range files {
if !file.Ast.HasError() {
ResolveFile(file)
}
}
}
// Transpile the files
log.Info("Converting files...")
for _, file := range files {
if dryRun {
log.Infof("Not converting file \"%s\"", file.Name)
continue
}
log.Infof("Converting file \"%s\"", file.Name)
// Write to stdout by default
var output io.Writer = os.Stdout
if writeFiles {
// Write to a `.go` file in the same directory
outputFile := fmt.Sprintf("%s/%s",
outputDirectory,
strings.TrimSuffix(file.Name, filepath.Ext(file.Name))+".go",
)
err := os.MkdirAll(outputDirectory, 0755)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"path": outputFile,
}).Panic("Error creating output directory")
}
// Write the output to a file
output, err = os.Create(outputFile)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"file": outputFile,
}).Panic("Error creating output file")
}
}
// The converted AST, in Go's AST representation
var initialContext Ctx
if symbolAware {
initialContext.currentFile = file.Symbols
initialContext.currentClass = file.Symbols.BaseClass
}
parsed := ParseNode(file.Ast, file.Source, initialContext).(ast.Node)
// Print the generated AST
if displayAST {
ast.Print(token.NewFileSet(), parsed)
}
// Output the parsed AST, into the source specified earlier
if err := printer.Fprint(output, token.NewFileSet(), parsed); err != nil {
log.WithFields(log.Fields{
"error": err,
}).Panic("Error printing generated code")
}
if writeFiles {
output.(*os.File).Close()
}
}
}