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) } constDeclarations = nil typeDeclarations = nil abstractClassDeclarations = nil classDeclarations = 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.printIncludeHeaders() s.printConstDeclarations() s.printTypeDeclarations() s.printAbstractClassDeclarations() s.printClassDeclarations() 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 \n\n" s.out.Write([]byte(h)) } 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) printAbstractClassDeclarations() { dlock.Lock() defer dlock.Unlock() for _, c := range abstractClassDeclarations { 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) 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")) }