From ebd8a28d5647587e00fd518f6c5edb71ad5e0f3d Mon Sep 17 00:00:00 2001 From: Softonik Date: Tue, 20 Feb 2024 17:25:27 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BC=D0=BD=D0=BE=D0=B6=D0=B5=D1=81=D1=82=D0=B2=D0=B0?= =?UTF-8?q?=20=D0=B8=D1=81=D1=85=D0=BE=D0=B4=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/go-tr/main.go | 46 +++++----- pkg/service/features/multi_sources.feature | 24 +++++ pkg/service/init_test.go | 5 +- pkg/service/service.go | 100 ++++++++++++++------- pkg/service/service_test.go | 23 +++-- 5 files changed, 131 insertions(+), 67 deletions(-) create mode 100644 pkg/service/features/multi_sources.feature diff --git a/cmd/go-tr/main.go b/cmd/go-tr/main.go index 0fe57f6..8b1e53e 100644 --- a/cmd/go-tr/main.go +++ b/cmd/go-tr/main.go @@ -3,6 +3,7 @@ package main import ( "flag" "fmt" + "io" "my/go-translator/pkg/service" "my/go-translator/pkg/service_python" "os" @@ -14,30 +15,30 @@ const ( ) func main() { - mode, source, target, header_file, addArduinoH := getFlags() - checkFlagsAreValid(source) - safeTranspile(mode, source, target, header_file, addArduinoH) + mode, target, header_file, addArduinoH, sources := getFlags() + checkFlagsAreValid(sources) + safeTranspile(mode, sources, target, header_file, addArduinoH) } -func getFlags() (int, string, string, string, bool) { +func getFlags() (int, string, string, bool, []string) { pmode := flag.Bool("p", false, "Mode: C++ or Python") + target := flag.String("o", "", "Write to file instead of stdout") header_file := flag.String("h", "", "Write headers to file also") addArduinoH := flag.Bool("a", false, "Add Arduino.h") flag.Parse() - source := flag.Arg(0) - target := flag.Arg(1) + sources := flag.Args() mode := CPP_MODE if *pmode { mode = PYTHON_MODE } - return mode, source, target, *header_file, *addArduinoH + return mode, *target, *header_file, *addArduinoH, sources } -func checkFlagsAreValid(source string) { - if source == "" { +func checkFlagsAreValid(sources []string) { + if len(sources) == 0 { printUsage() os.Exit(1) } @@ -49,21 +50,25 @@ func printUsage() { flag.PrintDefaults() fmt.Print("\n") fmt.Print("Example:\n") - fmt.Printf("\tgo-tr [-a] [-h header.h] [-p] controller.go [output.cpp]\n\n") + fmt.Printf("\tgo-tr [-a] [-h header.h] [-o output.cpp] [-p] \n\n") } -func safeTranspile(mode int, source, target, header_file string, addArduinoH bool) { - // Read the Golang source file. - in, err := os.Open(source) - if err != nil { - fmt.Fprintf(os.Stderr, "Go source file [%s] could not be opened! %v", source, err) - os.Exit(1) +func safeTranspile(mode int, sources []string, target, header_file string, addArduinoH bool) { + files := []io.Reader{} + var err error + + for _, src := range sources { + in, err := os.Open(src) + if err != nil { + fmt.Fprintf(os.Stderr, "Go source file [%s] could not be opened! %v", src, err) + os.Exit(1) + } + defer in.Close() + files = append(files, in) } - defer in.Close() out := os.Stdout if target != "" { - // Create the Arduino sketch file. os.Remove(target) out, err = os.OpenFile(target, os.O_CREATE|os.O_RDWR|os.O_SYNC, 0644) if err != nil { @@ -83,8 +88,7 @@ func safeTranspile(mode int, source, target, header_file string, addArduinoH boo } switch mode { case CPP_MODE: - // Transpiles the Golang source into Arduino sketch. - service := service.NewService(in, out) + service := service.NewService(files, out) service.SetHeaderWriter(header_f) service.AddIncludeArduinoH(addArduinoH) if err := service.Start(); err != nil { @@ -92,7 +96,7 @@ func safeTranspile(mode int, source, target, header_file string, addArduinoH boo os.Exit(1) } case PYTHON_MODE: - service := service_python.NewService(in, out) + service := service_python.NewService(files[0], out) if err := service.Start(); err != nil { fmt.Fprintf(os.Stderr, "%v", err) os.Exit(1) diff --git a/pkg/service/features/multi_sources.feature b/pkg/service/features/multi_sources.feature new file mode 100644 index 0000000..2a10fe5 --- /dev/null +++ b/pkg/service/features/multi_sources.feature @@ -0,0 +1,24 @@ +# Во имя Бога Милостивого, Милосердного!!! +# language: ru +Функциональность: Преобразование в C++: множество исходников + + Сценарий: Два исходника + * Исходник: +``` +package test +import "fmt" +func main() {} +``` + * Исходник: +``` +package test +import "fmt" +func other() {} +``` + * Результат: +``` +void main(); +void other(); +void main() {} +void other() {} +``` diff --git a/pkg/service/init_test.go b/pkg/service/init_test.go index 43b9720..74ca38e 100644 --- a/pkg/service/init_test.go +++ b/pkg/service/init_test.go @@ -24,10 +24,7 @@ func InitializeScenario(ctx *godog.ScenarioContext) { return ctx, nil }) ctx.After(func(ctx context.Context, sc *godog.Scenario, err error) (context.Context, error) { - if err == nil { - afterScenario() - } - + afterScenario(err) return ctx, nil }) InitializeGomegaForGodog(ctx) diff --git a/pkg/service/service.go b/pkg/service/service.go index 58589eb..2851f1e 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -2,13 +2,13 @@ package service import ( "fmt" + "go/ast" "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 @@ -16,9 +16,7 @@ type Service interface { } const ( - // ErrorWorkerReaderIsNil ... ErrorWorkerReaderIsNil = "Reader should not be nil" - // ErrorWorkerWriterIsNil ... ErrorWorkerWriterIsNil = "Writer should not be nil" FunctionGoHelperPrefix = "__GoHelper_" @@ -30,20 +28,23 @@ var ( dlock sync.Mutex ) -// defaultService specifies the api logic of transforming a source code format into another target format. type defaultService struct { - in io.Reader + in []io.Reader out io.Writer header io.Writer + decls []ast.Decl addIncludeArduinoH bool } -// NewService creates a a new transpile and returns its address. -func NewService(in io.Reader, out io.Writer) Service { - return &defaultService{ +func NewService(in []io.Reader, out io.Writer) Service { + s := &defaultService{ in: in, out: out, } + + s.initResults() + + return s } func (s *defaultService) SetHeaderWriter(w io.Writer) error { @@ -59,7 +60,6 @@ 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) @@ -67,25 +67,31 @@ func (s *defaultService) Start() error { 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) + + err := s.parseFiles() 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) + return err } + return s.generate() +} + +func (s *defaultService) generate() error { + dst := s.generateStructs() + + return s.printResult(dst) +} + +func (s *defaultService) parseFiles() error { + for _, in := range s.in { + err := s.parseFile(in) + if err != nil { + return err + } + } + return nil +} +func (*defaultService) initResults() { includeDeclarations = nil constDeclarations = nil typeDeclarations = nil @@ -93,20 +99,49 @@ func (s *defaultService) Start() error { variableDeclarations = nil funcDeclarations = nil helpersForDeclarations = nil +} +func (s *defaultService) parseFile(in io.Reader) error { + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, "virt_input.go", in, 0) + if err != nil { + return nil + } + + // Debug the AST + // ast.Fprint(os.Stderr, fset, file, nil) + + if file.Decls == nil { + fmt.Fprint(s.out, "void loop() {}void setup() {}") + return nil + } + + s.decls = append(s.decls, file.Decls...) + + return nil +} + +func (s *defaultService) generateStructs() []chan string { + count := len(s.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. go func() { - for i, decl := range file.Decls { + for i, decl := range s.decls { handleDecl(i, decl, dst[i], done) } }() - // Wait for all workers are done. + for i := 0; i < count; i++ { select { case <-done: } } - + return dst +} +func (s *defaultService) printResult(buf []chan string) error { s.printIncludeAdruinoHeader() s.printIncludes() s.printConstDeclarations() @@ -117,17 +152,14 @@ func (s *defaultService) Start() error { s.printMethodImplementations() s.printGoHelperDeclarations() - // Print the ordered result. - for i := 0; i < count; i++ { - for content := range dst[i] { + for i := 0; i < len(s.decls); i++ { + for content := range buf[i] { s.out.Write([]byte(content)) } } s.out.Write([]byte("\n")) - // Print the AST. - // ast.Fprint(os.Stderr, fset, file, nil) return nil } diff --git a/pkg/service/service_test.go b/pkg/service/service_test.go index 8af0cb8..6b3abc5 100644 --- a/pkg/service/service_test.go +++ b/pkg/service/service_test.go @@ -2,6 +2,7 @@ package service import ( "bytes" + "io" "strings" . "my/go-translator/pkg/testgodoglib" @@ -11,7 +12,7 @@ import ( ) type testData struct { - src string + src []string } var ( @@ -31,22 +32,28 @@ func afterSuite() { func beforeScenario() { resetTestData() } -func afterScenario() { +func afterScenario(result error) { } // ----------------------- func исходник(src *godog.DocString) { - t.src = src.Content + t.src = append(t.src, src.Content) } func результат(out *godog.DocString) { Compare_new(t.src, out.Content) } -func Compare_new(source, expected string) { - var in, out bytes.Buffer - in.WriteString(source) - service := NewService(&in, &out) +func Compare_new(source []string, expected string) { + var in []io.Reader + var out bytes.Buffer + + for _, s := range source { + b := &bytes.Buffer{} + b.WriteString(s) + in = append(in, b) + } + service := NewService(in, &out) err := service.Start() got := out.String() tgot, texpected := trim_new(got), trim_new(expected) @@ -64,7 +71,7 @@ func trim_new(s string) string { func Compare(source, expected string) { var in, out bytes.Buffer in.WriteString(source) - service := NewService(&in, &out) + service := NewService([]io.Reader{&in}, &out) err := service.Start() got := out.String() tgot, texpected := Trim(got), Trim(expected)