aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorChristine Dodrill <me@christine.website>2019-06-25 22:59:06 +0000
committerChristine Dodrill <me@christine.website>2019-06-25 22:59:06 +0000
commit96a073ecddf7eee051050347a09786dc38b3a8ac (patch)
tree44644398eaf655e5be51cdbd8bab25330bc0bd58 /cmd
parentab2f1f58d4de28aa708cdbc1602c5ae1a941fce6 (diff)
downloadx-96a073ecddf7eee051050347a09786dc38b3a8ac.tar.xz
x-96a073ecddf7eee051050347a09786dc38b3a8ac.zip
h
Diffstat (limited to 'cmd')
-rw-r--r--cmd/h/compile.go98
-rw-r--r--cmd/h/main.go59
-rw-r--r--cmd/h/run.go76
3 files changed, 233 insertions, 0 deletions
diff --git a/cmd/h/compile.go b/cmd/h/compile.go
new file mode 100644
index 0000000..7b4d6e3
--- /dev/null
+++ b/cmd/h/compile.go
@@ -0,0 +1,98 @@
+package main
+
+import (
+ "bytes"
+ "context"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "text/template"
+ "time"
+
+ "within.website/x/h"
+)
+
+var (
+ wat2wasmLoc string
+ wasmTemplateObj *template.Template
+)
+
+func init() {
+ loc, err := exec.LookPath("wat2wasm")
+ if err != nil {
+ panic(err)
+ }
+
+ wat2wasmLoc = loc
+ wasmTemplateObj = template.Must(template.New("h.wast").Parse(wasmTemplate))
+}
+
+// CompiledProgram is a fully parsed and compiled h program.
+type CompiledProgram struct {
+ Source string `json:"src"`
+ WebAssemblyText string `json:"wat"`
+ Binary []byte `json:"bin"`
+}
+
+func compile(source string) (*CompiledProgram, error) {
+ tree, err := h.Parse(source)
+ if err != nil {
+ return nil, err
+ }
+
+ result := CompiledProgram{
+ Source: source,
+ }
+
+ dir, err := ioutil.TempDir("", "h")
+ if err != nil {
+ return nil, err
+ }
+ defer os.RemoveAll(dir)
+
+ fout, err := os.Create(filepath.Join(dir, "h.wast"))
+ if err != nil {
+ return nil, err
+ }
+
+ buf := bytes.NewBuffer(nil)
+
+ err = wasmTemplateObj.Execute(buf, []byte(tree.Text))
+ if err != nil {
+ return nil, err
+ }
+
+ result.WebAssemblyText = buf.String()
+ _, err = fout.WriteString(result.WebAssemblyText)
+ if err != nil {
+ return nil, err
+ }
+
+ fname := fout.Name()
+
+ err = fout.Close()
+ if err != nil {
+ return nil, err
+ }
+
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+ defer cancel()
+
+ cmd := exec.CommandContext(ctx, wat2wasmLoc, fname, "-o", filepath.Join(dir, "h.wasm"))
+ cmd.Dir = dir
+
+ err = cmd.Run()
+ if err != nil {
+ return nil, err
+ }
+
+ data, err := ioutil.ReadFile(filepath.Join(dir, "h.wasm"))
+ if err != nil {
+ return nil, err
+ }
+
+ result.Binary = data
+
+ return &result, nil
+}
diff --git a/cmd/h/main.go b/cmd/h/main.go
new file mode 100644
index 0000000..f7e064e
--- /dev/null
+++ b/cmd/h/main.go
@@ -0,0 +1,59 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+
+ "within.website/x/internal"
+)
+
+var (
+ program = flag.String("p", "h", "h program to compile/run")
+)
+
+func main() {
+ internal.HandleStartup()
+
+ log.Println("compiling...")
+ comp, err := compile(*program)
+ if err != nil {
+ panic(err)
+ }
+
+ log.Println("running...")
+ er, err := run(*comp)
+ if err != nil {
+ panic(err)
+ }
+
+ log.Println("success!")
+
+ log.Printf("gas used:\t%d", er.GasUsed)
+ log.Printf("exec time:\t%s", er.ExecTime)
+ log.Println("output:")
+ fmt.Println(er.Output)
+}
+
+const wasmTemplate = `(module
+ (import "h" "h" (func $h (param i32)))
+ (func $h_main
+ (local i32 i32 i32)
+ (local.set 0 (i32.const 10))
+ (local.set 1 (i32.const 104))
+ (local.set 2 (i32.const 37))
+ {{ range . -}}
+ {{ if eq . 32 -}}
+ (call $h (get_local 0))
+ {{ end -}}
+ {{ if eq . 104 -}}
+ (call $h (get_local 1))
+ {{ end -}}
+ {{ if eq . 39 -}}
+ (call $h (get_local 2))
+ {{ end -}}
+ {{ end -}}
+ (call $h (get_local 0))
+ )
+ (export "main" (func $h_main))
+)`
diff --git a/cmd/h/run.go b/cmd/h/run.go
new file mode 100644
index 0000000..d07e51f
--- /dev/null
+++ b/cmd/h/run.go
@@ -0,0 +1,76 @@
+package main
+
+import (
+ "errors"
+ "time"
+
+ "github.com/perlin-network/life/compiler"
+ "github.com/perlin-network/life/exec"
+)
+
+type Process struct {
+ Source CompiledProgram
+ Output []byte
+}
+
+// ResolveGlobal does nothing, currently.
+func (p *Process) ResolveGlobal(module, field string) int64 { return 0 }
+
+// ResolveFunc resolves h's ABI and importable function.
+func (p *Process) ResolveFunc(module, field string) exec.FunctionImport {
+ switch module {
+ case "h":
+ switch field {
+ case "h":
+ return func(vm *exec.VirtualMachine) int64 {
+ frame := vm.GetCurrentFrame()
+ data := frame.Locals[0]
+ p.Output = append(p.Output, byte(data))
+
+ return 0
+ }
+
+ default:
+ panic("impossible state")
+ }
+
+ default:
+ panic("impossible state")
+ }
+}
+
+type ExecResult struct {
+ Output string
+ GasUsed uint64
+ ExecTime time.Duration
+}
+
+func run(cp CompiledProgram) (*ExecResult, error) {
+ p := &Process{
+ Source: cp,
+ }
+
+ var cfg exec.VMConfig
+ gp := &compiler.SimpleGasPolicy{GasPerInstruction: 1}
+ vm, err := exec.NewVirtualMachine(cp.Binary, cfg, p, gp)
+ if err != nil {
+ return nil, err
+ }
+
+ mainFunc, ok := vm.GetFunctionExport("main")
+ if !ok {
+ return nil, errors.New("impossible state: no main function exposed")
+ }
+
+ t0 := time.Now()
+ _, err = vm.Run(mainFunc)
+ if err != nil {
+ return nil, err
+ }
+
+ return &ExecResult{
+ Output: string(p.Output),
+ GasUsed: vm.Gas,
+ ExecTime: time.Since(t0),
+ }, nil
+}