242 строки
5,2 КиБ
Go
242 строки
5,2 КиБ
Go
package service
|
|
|
|
import (
|
|
"fmt"
|
|
"go/parser"
|
|
"go/token"
|
|
"io"
|
|
"sync"
|
|
)
|
|
|
|
// Service specifies the api logic of transforming a source code format into another target format.
|
|
type Service interface {
|
|
Start() error
|
|
SetHeaderWriter(io.Writer) error
|
|
AddIncludeArduinoH(bool)
|
|
}
|
|
|
|
const (
|
|
// ErrorWorkerReaderIsNil ...
|
|
ErrorWorkerReaderIsNil = "Reader should not be nil"
|
|
// ErrorWorkerWriterIsNil ...
|
|
ErrorWorkerWriterIsNil = "Writer should not be nil"
|
|
|
|
FunctionGoHelperPrefix = "__GoHelper_"
|
|
)
|
|
|
|
var (
|
|
funcDeclarations []string
|
|
helpersForDeclarations []string
|
|
dlock sync.Mutex
|
|
)
|
|
|
|
// defaultService specifies the api logic of transforming a source code format into another target format.
|
|
type defaultService struct {
|
|
in io.Reader
|
|
out io.Writer
|
|
header io.Writer
|
|
addIncludeArduinoH bool
|
|
}
|
|
|
|
// NewService creates a a new transpile and returns its address.
|
|
func NewService(in io.Reader, out io.Writer) Service {
|
|
return &defaultService{
|
|
in: in,
|
|
out: out,
|
|
}
|
|
}
|
|
|
|
func (s *defaultService) SetHeaderWriter(w io.Writer) error {
|
|
if w == nil {
|
|
return fmt.Errorf("Empty")
|
|
}
|
|
|
|
s.header = w
|
|
return nil
|
|
}
|
|
|
|
func (s *defaultService) AddIncludeArduinoH(add bool) {
|
|
s.addIncludeArduinoH = add
|
|
}
|
|
|
|
// Start ...
|
|
func (s *defaultService) Start() error {
|
|
if s.in == nil {
|
|
return fmt.Errorf("Error: %s", ErrorWorkerReaderIsNil)
|
|
}
|
|
if s.out == nil {
|
|
return fmt.Errorf("Error: %s", ErrorWorkerWriterIsNil)
|
|
}
|
|
// Read tokens from file by using Go's parser.
|
|
fset := token.NewFileSet()
|
|
file, err := parser.ParseFile(fset, "source.go", s.in, 0)
|
|
if err != nil {
|
|
return fmt.Errorf("ParseFile failed! %v", err)
|
|
}
|
|
// If source has no declarations then main it to an empty for loop.
|
|
if file.Decls == nil {
|
|
fmt.Fprint(s.out, "void loop() {}void setup() {}")
|
|
return nil
|
|
}
|
|
// Use Goroutines to work concurrently.
|
|
count := len(file.Decls)
|
|
done := make(chan bool, count)
|
|
dst := make([]chan string, count)
|
|
for i := 0; i < count; i++ {
|
|
dst[i] = make(chan string, 1)
|
|
}
|
|
|
|
includeDeclarations = nil
|
|
constDeclarations = nil
|
|
typeDeclarations = nil
|
|
classDeclarations = nil
|
|
variableDeclarations = nil
|
|
funcDeclarations = nil
|
|
helpersForDeclarations = nil
|
|
|
|
// Start a transpile with an individual channel for each declaration in the source file.
|
|
go func() {
|
|
for i, decl := range file.Decls {
|
|
handleDecl(i, decl, dst[i], done)
|
|
}
|
|
}()
|
|
// Wait for all workers are done.
|
|
for i := 0; i < count; i++ {
|
|
select {
|
|
case <-done:
|
|
}
|
|
}
|
|
|
|
s.printIncludeAdruinoHeader()
|
|
s.printIncludes()
|
|
s.printConstDeclarations()
|
|
s.printTypeDeclarations()
|
|
s.printClassDeclarations()
|
|
s.printFunctionDeclarations()
|
|
s.printVariableDeclarations()
|
|
s.printMethodImplementations()
|
|
s.printGoHelperDeclarations()
|
|
|
|
// Print the ordered result.
|
|
for i := 0; i < count; i++ {
|
|
for content := range dst[i] {
|
|
s.out.Write([]byte(content))
|
|
}
|
|
}
|
|
|
|
s.out.Write([]byte("\n"))
|
|
|
|
// Print the AST.
|
|
// ast.Fprint(os.Stderr, fset, file, nil)
|
|
return nil
|
|
}
|
|
|
|
func (s *defaultService) printIncludeAdruinoHeader() {
|
|
if !s.addIncludeArduinoH {
|
|
return
|
|
}
|
|
|
|
h := "#include <Arduino.h>\n\n"
|
|
s.out.Write([]byte(h))
|
|
}
|
|
func (s *defaultService) printIncludes() {
|
|
dlock.Lock()
|
|
defer dlock.Unlock()
|
|
|
|
for _, c := range includeDeclarations {
|
|
d := c.String()
|
|
s.out.Write([]byte(d + "\n"))
|
|
if s.header != nil {
|
|
s.header.Write([]byte(d + "\n"))
|
|
}
|
|
}
|
|
s.out.Write([]byte("\n"))
|
|
}
|
|
func (s *defaultService) printConstDeclarations() {
|
|
dlock.Lock()
|
|
defer dlock.Unlock()
|
|
|
|
for _, c := range constDeclarations {
|
|
d := c.String()
|
|
s.out.Write([]byte(d + "\n"))
|
|
if s.header != nil {
|
|
s.header.Write([]byte(d + "\n"))
|
|
}
|
|
}
|
|
s.out.Write([]byte("\n"))
|
|
}
|
|
func (s *defaultService) printTypeDeclarations() {
|
|
dlock.Lock()
|
|
defer dlock.Unlock()
|
|
|
|
for _, c := range typeDeclarations {
|
|
d := c.String()
|
|
s.out.Write([]byte(d + "\n"))
|
|
if s.header != nil {
|
|
s.header.Write([]byte(d + "\n"))
|
|
}
|
|
}
|
|
s.out.Write([]byte("\n"))
|
|
}
|
|
func (s *defaultService) printClassDeclarations() {
|
|
dlock.Lock()
|
|
defer dlock.Unlock()
|
|
|
|
for _, c := range classDeclarations {
|
|
d := c.String()
|
|
s.out.Write([]byte(d + "\n"))
|
|
if s.header != nil {
|
|
s.header.Write([]byte(d + "\n"))
|
|
}
|
|
}
|
|
s.out.Write([]byte("\n"))
|
|
}
|
|
func (s *defaultService) printFunctionDeclarations() {
|
|
dlock.Lock()
|
|
defer dlock.Unlock()
|
|
|
|
for _, f := range funcDeclarations {
|
|
s.out.Write([]byte(f + "\n"))
|
|
if s.header != nil {
|
|
s.header.Write([]byte(f + "\n"))
|
|
}
|
|
}
|
|
s.out.Write([]byte("\n"))
|
|
}
|
|
func (s *defaultService) printVariableDeclarations() {
|
|
dlock.Lock()
|
|
defer dlock.Unlock()
|
|
|
|
for _, c := range variableDeclarations {
|
|
d := c.String()
|
|
s.out.Write([]byte(d + "\n"))
|
|
if s.header != nil {
|
|
s.header.Write([]byte(d + "\n"))
|
|
}
|
|
}
|
|
s.out.Write([]byte("\n"))
|
|
}
|
|
func (s *defaultService) printMethodImplementations() {
|
|
dlock.Lock()
|
|
defer dlock.Unlock()
|
|
|
|
for _, c := range classDeclarations {
|
|
d := c.MethodsString()
|
|
s.out.Write([]byte(d + "\n"))
|
|
if s.header != nil {
|
|
s.header.Write([]byte(d + "\n"))
|
|
}
|
|
}
|
|
s.out.Write([]byte("\n"))
|
|
}
|
|
func (s *defaultService) printGoHelperDeclarations() {
|
|
dlock.Lock()
|
|
defer dlock.Unlock()
|
|
|
|
for _, f := range helpersForDeclarations {
|
|
helper := fmt.Sprintf(`void %v%v(void*) { %v(); vTaskDelete(NULL); }`,
|
|
FunctionGoHelperPrefix, f, f)
|
|
s.out.Write([]byte(helper + "\n"))
|
|
}
|
|
s.out.Write([]byte("\n"))
|
|
}
|