andygeiss/esp32-transpiler

View on GitHub
transpile/service.go

Summary

Maintainability
A
0 mins
Test Coverage
package transpile

import (
    "fmt"
    "github.com/andygeiss/esp32-transpiler/transpile/handlers"
    "go/parser"
    "go/token"
    "io"
)

// Service specifies the api logic of transforming a source code format into another target format.
type Service interface {
    Start() error
}

const (
    // ErrorWorkerReaderIsNil ...
    ErrorWorkerReaderIsNil = "Reader should not be nil"
    // ErrorWorkerWriterIsNil ...
    ErrorWorkerWriterIsNil = "Writer should not be nil"
)

// defaultService specifies the api logic of transforming a source code format into another target format.
type defaultService struct {
    in  io.Reader
    out io.Writer
}

// 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,
    }
}

// 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.
    fs := token.NewFileSet()
    file, err := parser.ParseFile(fs, "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)
    }
    // Start a transpile with an individual channel for each declaration in the source file.
    for i, decl := range file.Decls {
        go handlers.HandleDecl(decl, dst[i], done)
    }
    // Wait for all workers are done.
    for i := 0; i < count; i++ {
        select {
        case <-done:
        }
    }
    // Print the ordered result.
    for i := 0; i < count; i++ {
        for content := range dst[i] {
            _, _ = s.out.Write([]byte(content))
        }
    }
    return nil
}