go-translator/pkg/service/service.go

153 строки
3,3 КиБ
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)
}
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.printIncludeHeaders()
s.printFunctionDeclarations()
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) printIncludeHeaders() {
if !s.addIncludeArduinoH {
return
}
h := "#include <Arduino.h>\n\n"
s.out.Write([]byte(h))
}
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) 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"))
}