diff --git a/cmd/go-tr/main.go b/cmd/go-tr/main.go index fef9767..19b1f67 100644 --- a/cmd/go-tr/main.go +++ b/cmd/go-tr/main.go @@ -4,13 +4,19 @@ import ( "flag" "fmt" "my/go-translator/transpile" + "my/go-translator/transpile_python" "os" ) +const ( + CPP_MODE = iota + PYTHON_MODE +) + func main() { - source, target := getFlags() + mode, source, target := getFlags() checkFlagsAreValid(source, target) - safeTranspile(source, target) + safeTranspile(mode, source, target) } func checkFlagsAreValid(source, target string) { @@ -20,25 +26,33 @@ func checkFlagsAreValid(source, target string) { } } -func getFlags() (string, string) { +func getFlags() (int, string, string) { // source := flag.String("source", "", "Golang source file") // target := flag.String("target", "", "Arduino sketch file") flag.Parse() + mode := CPP_MODE source := flag.Arg(0) target := flag.Arg(1) - return source, target + + if source == "-p" { + mode = PYTHON_MODE + source = flag.Arg(1) + target = flag.Arg(2) + } + + return mode, source, target } func printUsage() { - fmt.Print("This program transpiles Golang source into corresponding C code.\n\n") - fmt.Print("Options:\n") + fmt.Print("This program transpiles Golang source into corresponding C++/python code.\n\n") + fmt.Printf("Options:\n by default C++ mode\n -p - python mode\n") flag.PrintDefaults() fmt.Print("\n") fmt.Print("Example:\n") - fmt.Printf("\tgo-tr controller.go controller.ino\n\n") + fmt.Printf("\tgo-tr [-p] controller.go controller.ino\n\n") } -func safeTranspile(source, target string) { +func safeTranspile(mode int, source, target string) { // Read the Golang source file. in, err := os.Open(source) if err != nil { @@ -53,10 +67,19 @@ func safeTranspile(source, target string) { fmt.Fprintf(os.Stderr, "Arduino sketch file [%s] could not be opened! %v", target, err) os.Exit(1) } - // Transpiles the Golang source into Arduino sketch. - service := transpile.NewService(in, out) - if err := service.Start(); err != nil { - fmt.Fprintf(os.Stderr, "%v", err) - os.Exit(1) + switch mode { + case CPP_MODE: + // Transpiles the Golang source into Arduino sketch. + service := transpile.NewService(in, out) + if err := service.Start(); err != nil { + fmt.Fprintf(os.Stderr, "%v", err) + os.Exit(1) + } + case PYTHON_MODE: + service := transpile_python.NewService(in, out) + if err := service.Start(); err != nil { + fmt.Fprintf(os.Stderr, "%v", err) + os.Exit(1) + } } } diff --git a/transpile_python/mapping.go b/transpile_python/mapping.go new file mode 100644 index 0000000..0d18a99 --- /dev/null +++ b/transpile_python/mapping.go @@ -0,0 +1,97 @@ +package transpile_python + +var mapping = map[string]string{ + "digital.Low": "LOW", + "digital.High": "HIGH", + "digital.ModeInput": "INPUT", + "digital.ModeOutput": "OUTPUT", + "digital.PinMode": "pinMode", + "digital.Read": "digitalRead", + "digital.Write": "digitalWrite", + "analog.Read": "analogRead", + "analog.Write": "analogWrite", + "random.Num": "random", + "random.NumBetween": "random", + "random.Seed": "randomSeed", + "serial.Available": "Serial.available", + "serial.BaudRate300": "300", + "serial.BaudRate600": "600", + "serial.BaudRate1200": "1200", + "serial.BaudRate2400": "2400", + "serial.BaudRate4800": "4800", + "serial.BaudRate9600": "9600", + "serial.BaudRate14400": "14400", + "serial.BaudRate28800": "28800", + "serial.BaudRate38400": "38400", + "serial.BaudRate57600": "57600", + "serial.BaudRate115200": "115200", + "serial.Begin": "Serial.begin", + "serial.Print": "Serial.print", + "serial.Println": "Serial.println", + "timer.Delay": "delay", + "wifi": "WiFi", + "wifi.Client": "WiFiClient", + "client.Connect": "client.connect", + "client.Println": "client.println", + "client.Write": "client.write", + "wifi.Begin": "WiFi.begin", + "wifi.BeginEncrypted": "WiFi.begin", + "wifi.BSSID": "WiFi.BSSID", + "wifi.Disconnect": "WiFi.disconnect", + "wifi.EncryptionType": "WiFi.encryptionType", + "wifi.EncryptionTypeAuto": "8", + "wifi.EncryptionTypeCCMP": "4", + "wifi.EncryptionTypeNone": "7", + "wifi.EncryptionTypeTKIP": "2", + "wifi.EncryptionTypeWEP": "5", + "wifi.LocalIP": "WiFi.localIP", + "wifi.RSSI": "WiFi.RSSI", + "wifi.ScanNetworks": "WiFi.scanNetworks", + "wifi.SetDNS": "WiFi.setDNS", + "wifi.SSID": "WiFi.SSID", + "wifi.Status": "WiFi.status", + "wifi.StatusConnected": "WL_CONNECTED", + "wifi.StatusConnectionLost": "WL_CONNECTION_LOST", + "wifi.StatusConnectFailed": "WL_CONNECT_FAILED", + "wifi.StatusDisconnected": "WL_DISCONNECTED", + "wifi.StatusIdle": "WL_IDLE_STATUS", + "wifi.StatusNoShield": "WL_NO_SHIELD", + "wifi.StatusNoSSIDAvailable": "WL_NO_SSID_AVAIL", + "wifi.StatusScanCompleted": "WL_SCAN_COMPLETED", + "Loop": "loop", + "Setup": "setup", + "arduino.A0": "A0", + "arduino.A1": "A1", + "arduino.A2": "A2", + "arduino.A3": "A3", + "arduino.A4": "A4", + "arduino.A5": "A5", + "arduino.A6": "A6", + "arduino.A7": "A7", + "arduino.D0": "0", + "arduino.D1": "1", + "arduino.D2": "2", + "arduino.D3": "3", + "arduino.D4": "4", + "arduino.D5": "5", + "arduino.D6": "6", + "arduino.D7": "7", + "arduino.D8": "8", + "arduino.D9": "9", + "arduino.D10": "10", + "arduino.D11": "11", + "arduino.D12": "12", + "arduino.D13": "13", + "arduino.D14": "14", + "arduino.CIPO": "14", + "arduino.MISO": "14", + "arduino.D15": "15", + "arduino.SCK": "15", + "arduino.D16": "16", + "arduino.COPI": "16", + "arduino.MOSI": "16", + "arduino.D17": "17", + "arduino.RXLED": "17", + "arduino.D30": "30", + "arduino.TXLED": "30", +} diff --git a/transpile_python/service.go b/transpile_python/service.go new file mode 100644 index 0000000..d64f652 --- /dev/null +++ b/transpile_python/service.go @@ -0,0 +1,513 @@ +package transpile_python + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "io" + "strings" +) + +// 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. + 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) + } + // Start a transpile with an individual channel for each declaration in the source file. + for i, decl := range file.Decls { + go handleDecl(i, 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)) + } + } + + s.out.Write([]byte("\n")) + + // Print the AST. + // ast.Fprint(os.Stderr, fset, file, nil) + return nil +} + +func handleAssignStmt(as *ast.AssignStmt) string { + code := handleAssignStmtExpr(as.Lhs) + code += as.Tok.String() + code += handleAssignStmtExpr(as.Rhs) + return code +} + +func handleAssignStmtExpr(e []ast.Expr) string { + ops := make([]string, 0) + code := "" + for _, op := range e { + ops = append(ops, handleExpr(op)) + } + code += strings.Join(ops, ",") + return code +} + +func handleBasicLit(bl *ast.BasicLit) string { + return bl.Value +} + +func handleUnaryExpr(expr *ast.UnaryExpr) string { + code := expr.Op.String() + code += handleExpr(expr.X) + return code +} + +func handleBinaryExpr(expr ast.Expr) string { + be := expr.(*ast.BinaryExpr) + code := handleExpr(be.X) + code += be.Op.String() + code += handleExpr(be.Y) + return code +} + +func handleCallExpr(expr *ast.CallExpr) string { + code := handleExpr(expr.Fun) + code += "(" + args := make([]string, 0) + for _, arg := range expr.Args { + args = append(args, handleExpr(arg)) + } + code += strings.Join(args, ",") + code += ")" + return code +} + +func handleDecl(id int, decl ast.Decl, dst chan<- string, done chan<- bool) { + code := "" + switch d := decl.(type) { + case *ast.FuncDecl: + code += handleFuncDecl(d) + case *ast.GenDecl: + code += handleGenDecl(d) + } + dst <- code + close(dst) + done <- true +} + +func handleDeclStmt(stmt *ast.DeclStmt) string { + code := "" + switch decl := stmt.Decl.(type) { + case *ast.GenDecl: + code += handleGenDecl(decl) + } + return code +} + +func handleExpr(expr ast.Expr) string { + code := "" + switch e := expr.(type) { + case *ast.BasicLit: + code += handleBasicLit(e) + case *ast.UnaryExpr: + code += handleUnaryExpr(e) + case *ast.BinaryExpr: + code += handleBinaryExpr(e) + case *ast.CallExpr: + code += handleCallExpr(e) + case *ast.Ident: + code += handleIdent(e) + case *ast.ParenExpr: + code += handleParenExpr(e) + case *ast.SelectorExpr: + code += handleSelectorExpr(e) + } + return code +} + +func handleParenExpr(stmt *ast.ParenExpr) string { + code := "" + code += handleExpr(stmt.X) + return code +} + +func handleExprStmt(stmt *ast.ExprStmt) string { + code := "" + switch x := stmt.X.(type) { + case *ast.CallExpr: + code += handleCallExpr(x) + } + return code +} + +func handleFuncDecl(decl ast.Decl) string { + fd := decl.(*ast.FuncDecl) + if shouldSkipFunction(fd.Type) { + return "" + } + code := "" + name := "" + code += handleFuncDeclType(fd.Type) + code += " " + name = handleFuncDeclName(fd.Name) + if name == "NewController" { + return "" + } + code += name + code += "(" + code += handleFuncDeclParams(fd.Type) + code += ") {" + code += handleBlockStmt(fd.Body) + code += "}" + return code +} +func shouldSkipFunction(t *ast.FuncType) (res bool) { + r := t.Results + if r == nil { + return + } + + l := r.List + if len(l) != 1 { + return + } + + p := l[0] + if p == nil { + return + } + + pt := p.Type + if pt == nil { + return + } + + i, ok := pt.(*ast.Ident) + if !ok { + return + } + + if i.Name == "ShouldBeSkipped" { + return true + } + return +} + +func handleFuncDeclParams(t *ast.FuncType) string { + code := "" + if t.Params == nil || t.Params.List == nil { + return code + } + values := make([]string, 0) + for _, field := range t.Params.List { + ftype := "" + switch ft := field.Type.(type) { + case *ast.Ident: + ftype = handleIdent(ft) + } + for _, names := range field.Names { + values = append(values, ftype+" "+names.Name) + } + } + code += strings.Join(values, ",") + return code +} + +func handleBlockStmt(body *ast.BlockStmt) string { + code := "" + if body == nil { + return code + } + for _, stmt := range body.List { + code += handleStmt(stmt, false) + } + return code +} + +func handleBranchStmt(stmt *ast.BranchStmt) string { + return "break;" +} + +func handleCaseClause(cc *ast.CaseClause) string { + code := "case " + clauses := make([]string, 0) + for _, clause := range cc.List { + clauses = append(clauses, handleExpr(clause)) + } + code += strings.Join(clauses, ",") + code += ":" + for _, body := range cc.Body { + code += handleStmt(body, false) + } + return code +} + +func handleFuncDeclName(ident *ast.Ident) string { + code := "" + if ident == nil { + return code + } + code += ident.Name + if val, ok := mapping[code]; ok { + code = val + } + return code +} + +func handleFuncDeclType(t *ast.FuncType) string { + code := "" + if t.Results == nil { + code = "void" + } + return code +} + +func handleGenDecl(decl ast.Decl) string { + gd := decl.(*ast.GenDecl) + code := "" + switch gd.Tok { + case token.CONST: + code += "const " + case token.VAR: + code += "" + } + code += handleSpecs(gd.Specs) + return code +} + +func handleIdent(expr ast.Expr) string { + ident := expr.(*ast.Ident) + code := "" + switch ident.Name { + case "string": + code += "char*" + default: + code += ident.Name + } + return code +} + +func handleIfStmt(stmt *ast.IfStmt) string { + cond := handleExpr(stmt.Cond) + body := handleBlockStmt(stmt.Body) + code := fmt.Sprintf(`if (%s) { %s }`, cond, body) + if stmt.Else != nil { + tail := handleBlockStmt(stmt.Else.(*ast.BlockStmt)) + code += fmt.Sprintf(" else { %s }", tail) + } + return code +} + +func handleImportSpec(spec ast.Spec) string { + s := spec.(*ast.ImportSpec) + code := "" + if s.Name != nil { + name := handleIdent(s.Name) + if val, ok := mapping[name]; ok { + name = val + } + if name != "" { + if name != "controller" { + code = "#include <" + name + ".h>\n" + } + } + } + return code +} + +func handleSelectorExpr(expr ast.Expr) string { + s := expr.(*ast.SelectorExpr) + code := "" + switch x := s.X.(type) { + case *ast.Ident: + code += handleIdent(x) + } + code += "." + code += handleIdent(s.Sel) + if val, ok := mapping[code]; ok { + code = val + } + return code +} + +func handleSpecs(specs []ast.Spec) string { + code := "" + for _, spec := range specs { + switch spec.(type) { + case *ast.ImportSpec: + code += handleImportSpec(spec) + case *ast.ValueSpec: + code += handleValueSpec(spec) + ";" + } + } + return code +} + +func handleStmt(stmt ast.Stmt, standaloneAssignment bool) string { + code := "" + switch s := stmt.(type) { + case *ast.AssignStmt: + code += handleAssignStmt(s) + if !standaloneAssignment { + code += ";" + } + case *ast.BranchStmt: + code += handleBranchStmt(s) + case *ast.CaseClause: + code += handleCaseClause(s) + case *ast.DeclStmt: + code += handleDeclStmt(s) + case *ast.ExprStmt: + code += handleExprStmt(s) + code += ";" + case *ast.ForStmt: + code += handleForStmt(s) + case *ast.IfStmt: + code += handleIfStmt(s) + case *ast.SwitchStmt: + code += handleSwitchStmt(s) + case *ast.ReturnStmt: + code += handleReturnStmt(s) + } + return code +} + +func handleForStmt(stmt *ast.ForStmt) string { + code := "" + is_while := false + if stmt.Init == nil && stmt.Post == nil { + is_while = true + code += "while" + } else { + code += "for" + } + code += "(" + if !is_while { + code += handleStmt(stmt.Init, true) + code += ";" + } + code += handleBinaryExpr(stmt.Cond) // stmt.Cond + if !is_while { + code += ";" + code += handleStmt(stmt.Post, true) + } + code += ") {" + code += handleBlockStmt(stmt.Body) // stmt.Body + code += "}" + return code +} + +func handleSwitchStmt(stmt *ast.SwitchStmt) string { + code := "switch (" + code += handleExpr(stmt.Tag) + code += "){" + code += handleBlockStmt(stmt.Body) + code += "}" + return code +} + +func handleReturnStmt(stmt *ast.ReturnStmt) string { + code := "return " + if len(stmt.Results) > 0 { + code += handleExpr(stmt.Results[0]) + } + code += ";" + return code +} + +func handleValueSpec(spec ast.Spec) string { + s := spec.(*ast.ValueSpec) + code := "" + code += handleValueSpecType(s.Type) + code += " " + code += handleValueSpecNames(s.Names) + if s.Values != nil { + code += " = " + code += handleValueSpecValues(s.Values) + } + return code +} + +func handleValueSpecNames(names []*ast.Ident) string { + code := "" + for _, name := range names { + code += handleIdent(name) + } + return code +} + +func handleValueSpecType(expr ast.Expr) string { + code := "" + switch t := expr.(type) { + case *ast.SelectorExpr: + code += handleSelectorExpr(t) + case *ast.Ident: + code += handleIdent(t) + } + return code +} + +func handleValueSpecValues(values []ast.Expr) string { + code := "" + for _, value := range values { + switch v := value.(type) { + case *ast.BasicLit: + code += handleBasicLit(v) + case *ast.SelectorExpr: + code += handleSelectorExpr(value) + case *ast.CallExpr: + code += handleCallExpr(v) + } + } + return code +} diff --git a/transpile_python/service_test.go b/transpile_python/service_test.go new file mode 100644 index 0000000..e26856a --- /dev/null +++ b/transpile_python/service_test.go @@ -0,0 +1,674 @@ +package transpile_python + +import ( + "bytes" + "strings" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestUtils(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Go Translator") +} + +var _ = Describe("Go Translator", func() { + Describe("All", func() { + It("Empty_Package", func() { + source := `package test` + expected := `void loop(){} + void setup(){}` + Compare(source, expected) + }) + + It("Function_Declaration", func() { + source := `package test + func foo() {} + func bar() {} + ` + expected := `void foo(){} + void bar() {}` + Compare(source, expected) + }) + + It("Function_Declaration_With_Args", func() { + source := `package test + func foo(x int) {} + func bar(y int) {} + ` + expected := `void foo(int x){} + void bar(int y) {}` + Compare(source, expected) + }) + + It("Const_String_Declaration", func() { + source := `package test + const foo string = "bar" + ` + expected := ` + const char* foo = "bar"; + ` + Compare(source, expected) + }) + + It("Var_String_Declaration", func() { + source := `package test + var client wifi.Client + ` + expected := ` + WiFiClient client; + ` + Compare(source, expected) + }) + + It("Function_With_Const_String_Declaration", func() { + source := `package test + func foo() { + const foo string = "bar" + } + ` + expected := ` + void foo() { + const char* foo = "bar"; + } + ` + Compare(source, expected) + }) + + It("Function_With_Var_String_Declaration", func() { + source := `package test + func foo() { + var foo string = "bar" + } + ` + expected := ` + void foo() { + char* foo = "bar"; + } + ` + Compare(source, expected) + }) + + It("Function_With_Function_Call", func() { + source := `package test + func foo() { + bar() + } + ` + expected := ` + void foo() { + bar(); + } + ` + Compare(source, expected) + }) + + It("Function_With_Function_Call_With_Args", func() { + source := `package test + func foo() { + bar(1,2,3) + } + ` + expected := ` + void foo() { + bar(1,2,3); + } + ` + Compare(source, expected) + }) + + It("Function_With_Function_Call_With_String", func() { + source := `package test + func foo() { + bar("foo") + } + ` + expected := ` + void foo() { + bar("foo"); + } + ` + Compare(source, expected) + }) + + It("Function_With_Package_Function_Call", func() { + source := `package test + func foo() { + foo.Bar(1,"2") + } + ` + expected := ` + void foo() { + foo.Bar(1,"2"); + } + ` + Compare(source, expected) + }) + + It("Function_With_Assignments", func() { + source := `package test + func foo() { + x = 1 + y = 2 + z = x + y + } + ` + expected := ` + void foo() { + x = 1; + y = 2; + z = x + y; + } + ` + Compare(source, expected) + }) + + It("Function_With_Package_Selector_Assignments", func() { + source := `package test + func foo() { + x = bar() + y = pkg.Bar() + z = x + y + } + ` + expected := ` + void foo() { + x = bar(); + y = pkg.Bar(); + z = x + y; + } + ` + Compare(source, expected) + }) + + It("Function_Ident_Mapping", func() { + source := `package test + func foo() { + serial.Begin() + } + ` + expected := ` + void foo() { + Serial.begin(); + } + ` + Compare(source, expected) + }) + + It("Function_With_Ident_Param", func() { + source := `package test + func foo() { + foo.Bar(1,"2",digital.Low) + } + ` + expected := ` + void foo() { + foo.Bar(1,"2",LOW); + } + ` + Compare(source, expected) + }) + + It("Function_With_Function_Param", func() { + source := `package test + func foo() { + serial.Println(wifi.LocalIP()) + } + ` + expected := ` + void foo() { + Serial.println(WiFi.localIP()); + } + ` + Compare(source, expected) + }) + + It("Function_Should_Be_Skipped", func() { + source := `package test + func foo() (s ShouldBeSkipped) { + return; + } + ` + expected := `` + Compare(source, expected) + }) + + It("Package_Import", func() { + source := `package test + import "github.com/andygeiss/esp32-mqtt/api/controller" + import "github.com/andygeiss/esp32-mqtt/api/controller/serial" + import "github.com/andygeiss/esp32/api/controller/timer" + import wifi "github.com/andygeiss/esp32/api/controller/wifi" + ` + expected := ` + #include + ` + Compare(source, expected) + }) + + It("Package_Import_But_Ignore_Controller", func() { + source := `package test + import controller "my/go-controller" + import "github.com/andygeiss/esp32-mqtt/api/controller/serial" + import "github.com/andygeiss/esp32/api/controller/timer" + import wifi "github.com/andygeiss/esp32/api/controller/wifi" + ` + expected := ` + #include + ` + Compare(source, expected) + }) + + It("IfStmt_With_Condition_BasicLit_And_BasicLit", func() { + source := `package test + func Setup() {} + func Loop() { + if 1 == 1 { + serial.Println("1") + } + } + ` + expected := ` + void setup() {} + void loop() { + if (1 == 1) { + Serial.println("1"); + } + } + ` + Compare(source, expected) + }) + + It("IfStmt_With_Condition_Ident_And_BasicLit", func() { + source := `package test + func Setup() {} + func Loop() { + if x == 1 { + serial.Println("1") + } + } + ` + expected := ` + void setup() {} + void loop() { + if (x == 1) { + Serial.println("1"); + } + } + ` + Compare(source, expected) + }) + + It("IfStmt_With_Condition_CallExpr_And_BasicLit", func() { + source := `package test + func Setup() {} + func Loop() { + if x() == 1 { + serial.Println("1") + } + } + ` + expected := ` + void setup() {} + void loop() { + if (x() == 1) { + Serial.println("1"); + } + } + ` + Compare(source, expected) + }) + + It("IfStmt_With_Condition_Const_And_BasicLit", func() { + source := `package test + const maxX = 1 + func Setup() {} + func Loop() { + if x == maxX { + serial.Println("1") + } + } + ` + expected := ` + const maxX = 1; + void setup() {} + void loop() { + if (x == maxX) { + Serial.println("1"); + } + } + ` + Compare(source, expected) + }) + + It("IfStmt_With_Else", func() { + source := `package test + const maxX = 1 + func Setup() {} + func Loop() { + if x == maxX { + serial.Println("1") + } else { + serial.Println("2") + } + } + ` + expected := ` + const maxX = 1; + void setup() {} + void loop() { + if (x == maxX) { + Serial.println("1"); + } else { + Serial.println("2"); + } + } + ` + Compare(source, expected) + }) + + It("SwitchStmt_With_Ident_And_BasicLit", func() { + source := `package test + func Setup() {} + func Loop() { + switch x { + case 1: + serial.Println("1") + } + } + ` + expected := ` + void setup() {} + void loop() { + switch (x) { + case 1: + Serial.println("1"); + } + } + ` + Compare(source, expected) + }) + + It("SwitchStmt_With_Break", func() { + source := `package test + func Setup() {} + func Loop() { + switch x { + case 1: + serial.Println("1") + break + case 2: + serial.Println("1") + } + } + ` + expected := ` + void setup() {} + void loop() { + switch (x) { + case 1: + Serial.println("1"); + break; + case 2: + Serial.println("1"); + } + } + ` + Compare(source, expected) + }) + + It("ForLoop_WithoutInit_And_Post_Transpiles_To_While", func() { + source := `package test + import wifi "github.com/andygeiss/esp32/api/controller/wifi" + func Setup() { + serial.Begin(serial.BaudRate115200) + wifi.BeginEncrypted("SSID", "PASS") + for wifi.Status() != wifi.StatusConnected { + serial.Println("Connecting ...") + } + serial.Println("Connected!") + } + func Loop() {} + ` + expected := ` + #include + void setup() { + Serial.begin(115200); + WiFi.begin("SSID","PASS"); + while(WiFi.status()!=WL_CONNECTED){ + Serial.println("Connecting..."); + } + Serial.println("Connected!"); + } + void loop() {} + ` + Compare(source, expected) + }) + + It("WiFiWebClient", func() { + source := `package test + import wifi "github.com/andygeiss/esp32/api/controller/wifi" + var client wifi.Client + func Setup() {} + func Loop() { + serial.Print("Connecting to ") + serial.Println(host) + serial.Print(" ...") + if (client.Connect(host, 443) == true) { + serial.Println(" Connected!") + } else { + serial.Println(" Failed!") + } + } + ` + expected := `#include + WiFiClient client; + voidsetup(){} + voidloop(){ + Serial.print("Connecting to"); + Serial.println(host); + Serial.print(" ..."); + if(client.connect(host, 443) == true){ + Serial.println(" Connected!"); + } else { + Serial.println(" Failed!"); + } + }` + Compare(source, expected) + }) + }) + + Describe("Arduino", func() { + It("Maps constants", func() { + source := `package test + var analogInPin int = arduino.A0 + func Setup() {} + func Loop() {} + ` + expected := ` + int analogInPin = A0; + void setup() {} + void loop() {} + ` + Compare(source, expected) + }) + }) + + Describe("Функции", func() { + It("Объявление void функции", func() { + source := `package test + func Setup() {} + func Loop() { + } + + func MyFunction() { + } + ` + expected := ` + void setup() {} + void loop() { + } + void MyFunction() { + } + ` + Compare(source, expected) + }) + It("Объявление void функции с return", func() { + source := `package test + func Setup() {} + func Loop() { + } + + func MyFunction() { + return + } + ` + expected := ` + void setup() {} + void loop() { + } + void MyFunction() { + return; + } + ` + Compare(source, expected) + }) + It("Объявление int функции", func() { + source := `package test + func Setup() {} + func Loop() { + } + + func MyFunction() int { + } + ` + expected := ` + void setup() {} + void loop() { + } + MyFunction() { + } + ` + Compare(source, expected) + }) + It("Объявление int функции с return 0", func() { + source := `package test + func Setup() {} + func Loop() { + } + + func MyFunction() int { + return 0 + } + ` + expected := ` + void setup() {} + void loop() { + } + MyFunction() { + return 0; + } + ` + Compare(source, expected) + }) + It("Объявление int функции с return -1", func() { + source := `package test + func Setup() {} + func Loop() { + } + + func MyFunction() int { + return -1 + } + ` + expected := ` + void setup() {} + void loop() { + } + MyFunction() { + return -1; + } + ` + Compare(source, expected) + }) + It("Объявляет и вызывает функцию", func() { + source := `package test + func Setup() {} + func Loop() { + var x int = MyFunction() + } + + func MyFunction() int { + return 0 + } + ` + expected := ` + void setup() {} + void loop() { + int x = MyFunction(); + } + MyFunction() { + return 0; + } + ` + Compare(source, expected) + }) + }) + + Describe("Циклы", func() { + It("for i=0; i<10; i+=1", func() { + source := `package test + func Setup() {} + func Loop() { + var i int + for i=0; i<10; i+=1 { + i = i + } + } + ` + expected := ` + void setup() {} + void loop() { + int i; + for (i=0; i<10; i+=1) { + i = i; + } + } + ` + Compare(source, expected) + }) + }) +}) + +func Compare(source, expected string) { + var in, out bytes.Buffer + in.WriteString(source) + service := NewService(&in, &out) + err := service.Start() + got := out.String() + tgot, texpected := Trim(got), Trim(expected) + // spew.Dump(tgot) + ExpectWithOffset(1, err).NotTo(HaveOccurred()) + ExpectWithOffset(1, tgot).To(Be(texpected)) +} + +func Trim(s string) string { + s = strings.Replace(s, " ", "", -1) + s = strings.Replace(s, "\n", "", -1) + s = strings.Replace(s, "\r", "", -1) + s = strings.Replace(s, "\t", "", -1) + return s +} + +func NDescribe(s string, i func()) int { return 0 } +func NIt(s string, i func()) int { return 0 } + +var Be = Equal + +func NoErr(err error) { + ExpectWithOffset(1, err).NotTo(HaveOccurred()) +} + +var Ok = NoErr