Этот коммит содержится в:
andygeiss 2020-01-10 19:50:23 +01:00
родитель 78ad829f70
коммит 24159ea339
14 изменённых файлов: 112 добавлений и 222 удалений

Просмотреть файл

@ -1,6 +0,0 @@
package transpiler
// Transpiler specifies the behaviour of taking Go source code and transforming it into another language.
type Transpiler interface {
Transpile() error
}

Просмотреть файл

@ -1,6 +0,0 @@
package worker
// Mapping specifies the api logic to apply transformation to a specific identifier.
type Mapping interface {
Apply(ident string) string
}

Просмотреть файл

@ -1,6 +0,0 @@
package worker
// Source stores the code which will be transformed into another format.
type Source interface {
String() string
}

Просмотреть файл

@ -1,6 +0,0 @@
package worker
// Target stores the result of the transformation process.
type Target interface {
String() string
}

Просмотреть файл

@ -1,6 +0,0 @@
package worker
// Worker specifies the api logic of transforming a source code format into another target format.
type Worker interface {
Start() error
}

4
go.mod
Просмотреть файл

@ -1,5 +1,3 @@
module github.com/andygeiss/esp32-transpiler
go 1.12
require github.com/andygeiss/assert v0.0.6
go 1.14

2
go.sum
Просмотреть файл

@ -1,2 +0,0 @@
github.com/andygeiss/assert v0.0.6 h1:FyiAIudVwnxp55GIcNOaZ2ABr8n2RI9SXmaXG7+Y53o=
github.com/andygeiss/assert v0.0.6/go.mod h1:ztUvWrfUo43X0zMA1XfX8esn5Uavk6ANSKTT0w2qvAI=

Просмотреть файл

@ -1,30 +0,0 @@
package transpile
import (
"fmt"
"github.com/andygeiss/esp32-transpiler/api/transpiler"
"github.com/andygeiss/esp32-transpiler/api/worker"
)
// Transpiler uses a given worker to main source code.
type Transpiler struct {
w worker.Worker
}
const (
// ErrorTranspilerWorkerIsNil ...
ErrorTranspilerWorkerIsNil = "Worker should not be nil"
)
// NewTranspiler creates a new transpiler and returns its address.
func NewTranspiler(w worker.Worker) transpiler.Transpiler {
return &Transpiler{w}
}
// Transpile invokes the workers
func (c *Transpiler) Transpile() error {
if c.w == nil {
return fmt.Errorf("Error: %v", ErrorTranspilerWorkerIsNil)
}
return c.w.Start()
}

Просмотреть файл

@ -1,35 +0,0 @@
package transpile_test
import (
"bytes"
"github.com/andygeiss/assert"
"github.com/andygeiss/esp32-transpiler/api/worker"
"github.com/andygeiss/esp32-transpiler/impl/transpile"
"io"
"testing"
)
type mockupWorker struct {
in io.Reader
out io.Writer
}
func (w *mockupWorker) Prepare(source []worker.Source) (chan worker.Source, error) {
out := make(chan worker.Source)
return out, nil
}
func (w *mockupWorker) Start() error {
return nil
}
func (w *mockupWorker) Transform(source chan worker.Source) (chan worker.Target, error) {
out := make(chan worker.Target)
return out, nil
}
func TestTranspileErrorShouldBeNil(t *testing.T) {
var in, out bytes.Buffer
worker := &mockupWorker{&in, &out}
trans := transpile.NewTranspiler(worker)
err := trans.Transpile()
assert.That(t, err, nil)
}

Просмотреть файл

@ -1,84 +0,0 @@
package worker
import (
"github.com/andygeiss/esp32-transpiler/api/worker"
)
var (
rules = map[string]string{
"digital.Low": "LOW",
"digital.High": "HIGH",
"digital.ModeInput": "INPUT",
"digital.ModeOutput": "OUTPUT",
"digital.PinMode": "pinMode",
"digital.Write": "digitalWrite",
"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": "void loop",
"Setup": "void setup",
}
)
// Mapping specifies the api logic to apply transformation to a specific Golang identifier by reading simple JSON map.
type Mapping struct{}
// NewMapping creates a new mapping and returns its address.
func NewMapping() worker.Mapping {
return &Mapping{}
}
// Apply checks the Golang identifier and transforms it to a specific representation.
func (*Mapping) Apply(ident string) string {
for wanted := range rules {
if ident == wanted {
ident = rules[ident]
}
}
return ident
}

10
main.go
Просмотреть файл

@ -1,10 +1,9 @@
package main
import (
"esp32-transpiler/transpile"
"flag"
"fmt"
"github.com/andygeiss/esp32-transpiler/impl/transpile"
"github.com/andygeiss/esp32-transpiler/impl/worker"
"os"
)
@ -34,7 +33,7 @@ func printUsage() {
flag.PrintDefaults()
fmt.Print("\n")
fmt.Print("Example:\n")
fmt.Printf("\tesp32 -source impl/blink/controller.go -target impl/blink/controller.worker\n\n")
fmt.Printf("\tesp32 -source impl/blink/controller.go -target impl/blink/controller.transpile\n\n")
}
func safeTranspile(source, target string) {
@ -53,9 +52,8 @@ func safeTranspile(source, target string) {
os.Exit(1)
}
// Transpiles the Golang source into Arduino sketch.
wrk := worker.NewWorker(in, out, worker.NewMapping())
trans := transpile.NewTranspiler(wrk)
if err := trans.Transpile(); err != nil {
service := transpile.NewService(in, out)
if err := service.Start(); err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
os.Exit(1)
}

60
transpile/mapping.go Обычный файл
Просмотреть файл

@ -0,0 +1,60 @@
package transpile
var mapping = map[string]string{
"digital.Low": "LOW",
"digital.High": "HIGH",
"digital.ModeInput": "INPUT",
"digital.ModeOutput": "OUTPUT",
"digital.PinMode": "pinMode",
"digital.Write": "digitalWrite",
"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": "void loop",
"Setup": "void setup",
}

Просмотреть файл

@ -1,8 +1,7 @@
package worker
package transpile
import (
"fmt"
"github.com/andygeiss/esp32-transpiler/api/worker"
"go/ast"
"go/parser"
"go/token"
@ -10,6 +9,11 @@ import (
"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"
@ -17,37 +21,37 @@ const (
ErrorWorkerWriterIsNil = "Writer should not be nil"
)
var mapping worker.Mapping
// Worker specifies the api logic of transforming a source code format into another target format.
type Worker struct {
// defaultService specifies the api logic of transforming a source code format into another target format.
type defaultService struct {
in io.Reader
out io.Writer
}
// NewWorker creates a a new worker and returns its address.
func NewWorker(in io.Reader, out io.Writer, m worker.Mapping) worker.Worker {
mapping = m
return &Worker{in, out}
// 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 (w *Worker) Start() error {
if w.in == nil {
func (s *defaultService) Start() error {
if s.in == nil {
return fmt.Errorf("Error: %s", ErrorWorkerReaderIsNil)
}
if w.out == nil {
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", w.in, 0)
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(w.out, "void loop() {} void setup() {}")
fmt.Fprint(s.out, "void loop() {} void setup() {}")
return nil
}
// Use Goroutines to work concurrently.
@ -57,7 +61,7 @@ func (w *Worker) Start() error {
for i := 0; i < count; i++ {
dst[i] = make(chan string, 1)
}
// Start a worker with an individual channel for each declaration in the source file.
// 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)
}
@ -70,7 +74,7 @@ func (w *Worker) Start() error {
// Print the ordered result.
for i := 0; i < count; i++ {
for content := range dst[i] {
w.out.Write([]byte(content))
s.out.Write([]byte(content))
}
}
// Print the AST.
@ -160,7 +164,7 @@ func handleExpr(expr ast.Expr) string {
return code
}
func handleParenExpr (stmt *ast.ParenExpr) string {
func handleParenExpr(stmt *ast.ParenExpr) string {
code := ""
code += handleExpr(stmt.X)
return code
@ -249,7 +253,9 @@ func handleFuncDeclName(ident *ast.Ident) string {
return code
}
code += ident.Name
code = mapping.Apply(code)
if val, ok := mapping[code]; ok {
code = val
}
return code
}
@ -302,7 +308,9 @@ func handleImportSpec(spec ast.Spec) string {
code := ""
if s.Name != nil {
name := handleIdent(s.Name)
name = mapping.Apply(name)
if val, ok := mapping[name]; ok {
name = val
}
if name != "" {
if name != "controller" {
code = "#include <" + name + ".h>\n"
@ -321,7 +329,9 @@ func handleSelectorExpr(expr ast.Expr) string {
}
code += "."
code += handleIdent(s.Sel)
code = mapping.Apply(code)
if val, ok := mapping[code]; ok {
code = val
}
return code
}
@ -370,9 +380,9 @@ func handleForStmt(stmt *ast.ForStmt) string {
} else {
code += "for"
}
code += "(" // stmt.Init
code += "(" // stmt.Init
code += handleBinaryExpr(stmt.Cond) // stmt.Cond
code += "" // stmt.Post
code += "" // stmt.Post
code += ") {"
code += handleBlockStmt(stmt.Body) // stmt.Body
code += "}"

Просмотреть файл

@ -1,9 +1,9 @@
package worker_test
package transpile_test
import (
"bytes"
"github.com/andygeiss/assert"
"github.com/andygeiss/esp32-transpiler/impl/worker"
"esp32-transpiler/transpile"
"reflect"
"strings"
"testing"
)
@ -18,15 +18,20 @@ func Trim(s string) string {
}
// Validate the content of a given source with an expected outcome by using a string compare.
// The Worker will be started and used to transform the source into an Arduino sketch format.
// The defaultService will be started and used to transform the source into an Arduino sketch format.
func Validate(source, expected string, t *testing.T) {
var in, out bytes.Buffer
in.WriteString(source)
wrk := worker.NewWorker(&in, &out, worker.NewMapping())
assert.That(t, wrk.Start(), nil)
code := out.String()
tcode, texpected := Trim(code), Trim(expected)
assert.That(t, tcode, texpected)
service := transpile.NewService(&in, &out)
err := service.Start()
got := out.String()
tgot, texpected := Trim(got), Trim(expected)
if !reflect.DeepEqual(err, nil) {
t.Fatalf("got %v, but expected %v", err, nil)
}
if !reflect.DeepEqual(err, nil) {
t.Fatalf("got %v, but expected %v", tgot, texpected)
}
}
func Test_Empty_Package(t *testing.T) {