aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristine Dodrill <me@christine.website>2019-06-26 02:19:50 +0000
committerChristine Dodrill <me@christine.website>2019-06-26 02:19:50 +0000
commitbfea637d7698f5d9b1bcf5fbc43fc1f7ae245fa8 (patch)
tree819d090f6953e9b078f685e76a0001ecb8560138
parent96a073ecddf7eee051050347a09786dc38b3a8ac (diff)
downloadx-bfea637d7698f5d9b1bcf5fbc43fc1f7ae245fa8.tar.xz
x-bfea637d7698f5d9b1bcf5fbc43fc1f7ae245fa8.zip
cmd/h: simplify, add http playground API route
-rw-r--r--cmd/h/http.go55
-rw-r--r--cmd/h/main.go83
-rw-r--r--cmd/h/run.go19
3 files changed, 138 insertions, 19 deletions
diff --git a/cmd/h/http.go b/cmd/h/http.go
new file mode 100644
index 0000000..696cf78
--- /dev/null
+++ b/cmd/h/http.go
@@ -0,0 +1,55 @@
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+)
+
+var (
+ maxBytes = flag.Int64("max-playground-bytes", 75, "how many bytes of data should users be allowed to post to the playground?")
+)
+
+func doHTTP() error {
+ http.HandleFunc("/api/playground", runPlayground)
+
+ return http.ListenAndServe(":"+*port, nil)
+}
+
+func runPlayground(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ http.NotFound(w, r)
+ return
+ }
+
+ rc := http.MaxBytesReader(w, r.Body, *maxBytes)
+ defer rc.Close()
+
+ data, err := ioutil.ReadAll(rc)
+ if err != nil {
+ http.Error(w, "too many bytes sent", http.StatusBadRequest)
+ return
+ }
+
+ comp, err := compile(string(data))
+ if err != nil {
+ http.Error(w, fmt.Sprintf("compilation error: %v", err), http.StatusBadRequest)
+ return
+ }
+
+ er, err := run(comp.Binary)
+ if err != nil {
+ http.Error(w, fmt.Sprintf("runtime error: %v", err), http.StatusInternalServerError)
+ return
+ }
+
+ json.NewEncoder(w).Encode(struct {
+ Program *CompiledProgram `json:"prog"`
+ Results *ExecResult `json:"res"`
+ }{
+ Program: comp,
+ Results: er,
+ })
+}
diff --git a/cmd/h/main.go b/cmd/h/main.go
index f7e064e..cdfbf99 100644
--- a/cmd/h/main.go
+++ b/cmd/h/main.go
@@ -3,28 +3,49 @@ package main
import (
"flag"
"fmt"
+ "io/ioutil"
"log"
+ "os"
"within.website/x/internal"
)
var (
- program = flag.String("p", "h", "h program to compile/run")
+ program = flag.String("p", "", "h program to compile/run")
+ outFname = flag.String("o", "", "if specified, write the webassembly binary created by -p here")
+ watFname = flag.String("o-wat", "", "if specified, write the uncompiled webassembly created by -p here")
+ port = flag.String("port", "", "HTTP port to listen on")
+ writeTao = flag.Bool("koan", false, "if true, print the h koan and then exit")
)
-func main() {
- internal.HandleStartup()
+const koan = `And Jesus said unto the theologians, "Who do you say that I am?".
+
+They replied: "You are the eschatological manifestation of the ground of our
+being, the kerygma of which we find the ultimate meaning in our interpersonal
+relationships."
+
+And Jesus said "...What?"
+
+Some time passed and one of them spoke "h".
+
+Jesus was enlightened.`
+
+func tao() {
+ fmt.Println(koan)
+ os.Exit(0)
+}
+func oneOff() error {
log.Println("compiling...")
comp, err := compile(*program)
if err != nil {
- panic(err)
+ return err
}
log.Println("running...")
- er, err := run(*comp)
+ er, err := run(comp.Binary)
if err != nil {
- panic(err)
+ return err
}
log.Println("success!")
@@ -32,7 +53,53 @@ func main() {
log.Printf("gas used:\t%d", er.GasUsed)
log.Printf("exec time:\t%s", er.ExecTime)
log.Println("output:")
- fmt.Println(er.Output)
+ fmt.Print(er.Output)
+
+ if *outFname != "" {
+ err := ioutil.WriteFile(*outFname, comp.Binary, 0666)
+ if err != nil {
+ return err
+ }
+
+ log.Printf("wrote %d bytes to %s", len(comp.Binary), *outFname)
+ }
+
+ if *watFname != "" {
+ err := ioutil.WriteFile(*watFname, []byte(comp.WebAssemblyText), 0666)
+ if err != nil {
+ return err
+ }
+
+ log.Printf("write %d bytes of source to %s", len(comp.WebAssemblyText), *watFname)
+ }
+
+ return nil
+}
+
+func main() {
+ internal.HandleStartup()
+
+ if *writeTao {
+ tao()
+ }
+
+ if *program != "" {
+ err := oneOff()
+ if err != nil {
+ panic(err)
+ }
+
+ return
+ }
+
+ if *port != "" {
+ err := doHTTP()
+ if err != nil {
+ panic(err)
+ }
+
+ return
+ }
}
const wasmTemplate = `(module
@@ -55,5 +122,5 @@ const wasmTemplate = `(module
{{ end -}}
(call $h (get_local 0))
)
- (export "main" (func $h_main))
+ (export "h" (func $h_main))
)`
diff --git a/cmd/h/run.go b/cmd/h/run.go
index d07e51f..fb55bb7 100644
--- a/cmd/h/run.go
+++ b/cmd/h/run.go
@@ -9,7 +9,6 @@ import (
)
type Process struct {
- Source CompiledProgram
Output []byte
}
@@ -40,26 +39,24 @@ func (p *Process) ResolveFunc(module, field string) exec.FunctionImport {
}
type ExecResult struct {
- Output string
- GasUsed uint64
- ExecTime time.Duration
+ Output string `json:"out"`
+ GasUsed uint64 `json:"gas"`
+ ExecTime time.Duration `json:"exec_duration"`
}
-func run(cp CompiledProgram) (*ExecResult, error) {
- p := &Process{
- Source: cp,
- }
+func run(bin []byte) (*ExecResult, error) {
+ p := &Process{}
var cfg exec.VMConfig
gp := &compiler.SimpleGasPolicy{GasPerInstruction: 1}
- vm, err := exec.NewVirtualMachine(cp.Binary, cfg, p, gp)
+ vm, err := exec.NewVirtualMachine(bin, cfg, p, gp)
if err != nil {
return nil, err
}
- mainFunc, ok := vm.GetFunctionExport("main")
+ mainFunc, ok := vm.GetFunctionExport("h")
if !ok {
- return nil, errors.New("impossible state: no main function exposed")
+ return nil, errors.New("impossible state: no h function exposed")
}
t0 := time.Now()