diff options
| author | Xe <me@christine.website> | 2022-12-31 13:37:49 -0500 |
|---|---|---|
| committer | Xe <me@christine.website> | 2022-12-31 13:37:49 -0500 |
| commit | 44273f537aa7da7bfdef141941586018ec36e5b4 (patch) | |
| tree | e7d46578a6116b401a72dc7d3cefe377faa50dba | |
| parent | 04fe1cb27f4cab8ae55a2ffec1281fddf8f1bdfe (diff) | |
| download | x-44273f537aa7da7bfdef141941586018ec36e5b4.tar.xz x-44273f537aa7da7bfdef141941586018ec36e5b4.zip | |
cmd/hlang: fix compilation forever until there's a bug
Signed-off-by: Xe <me@christine.website>
| -rw-r--r-- | cmd/hlang/compile.go | 81 | ||||
| -rw-r--r-- | cmd/hlang/http.go | 24 | ||||
| -rw-r--r-- | cmd/hlang/main.go | 33 | ||||
| -rw-r--r-- | cmd/hlang/nguh/compile.go | 177 | ||||
| -rw-r--r-- | cmd/hlang/nguh/compile_test.go | 20 | ||||
| -rw-r--r-- | cmd/hlang/nguh/disasm.txt | 48 | ||||
| -rw-r--r-- | cmd/hlang/nguh/doc.go | 4 |
7 files changed, 267 insertions, 120 deletions
diff --git a/cmd/hlang/compile.go b/cmd/hlang/compile.go index 46e7239..b1455b7 100644 --- a/cmd/hlang/compile.go +++ b/cmd/hlang/compile.go @@ -1,41 +1,18 @@ package main import ( - "bytes" - "context" - "io/ioutil" - "os" - "os/exec" - "path/filepath" "strings" - "text/template" - "time" "github.com/eaburns/peggy/peg" "within.website/x/cmd/hlang/h" + "within.website/x/cmd/hlang/nguh" ) -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"` - AST string `json:"ast"` + Source string `json:"src"` + Binary []byte `json:"bin"` + AST string `json:"ast"` } func compile(source string) (*CompiledProgram, error) { @@ -46,60 +23,20 @@ func compile(source string) (*CompiledProgram, error) { var sb strings.Builder err = peg.PrettyWrite(&sb, tree) - - result := CompiledProgram{ - Source: source, - AST: sb.String(), - } - - 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)) + wasmBytes, err := nguh.Compile(tree) 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 := CompiledProgram{ + Source: source, + AST: sb.String(), + Binary: wasmBytes, } - result.Binary = data - return &result, nil } diff --git a/cmd/hlang/http.go b/cmd/hlang/http.go index f096e3f..d4b9aba 100644 --- a/cmd/hlang/http.go +++ b/cmd/hlang/http.go @@ -203,7 +203,7 @@ const indexTemplate = `<html> <hr /> <footer> - <center><p><i>From <a href="https://christine.website">Within</a></i></p></center> + <center><p><i>From <a href="https://xeiaso.net">Within</a></i></p></center> </footer> </main> </body> @@ -242,7 +242,7 @@ const docsTemplate = `<html> <hr /> <footer> - <center><p><i>From <a href="https://christine.website">Within</a></i></p></center> + <center><p><i>From <a href="https://xeiaso.net">Within</a></i></p></center> </footer> </main> </body> @@ -279,7 +279,7 @@ const faqTemplate = `<html> <p>With any computer running <a href="https://golang.org">Go</a> 1.11 or higher:</p> - <code><pre>go get -u -v within.website/x/cmd/h</pre></code> + <code><pre>go get -u -v within.website/x/cmd/hlang</pre></code> Usage is simple: @@ -309,7 +309,7 @@ const faqTemplate = `<html> <h2>What version is h?</h2> - <p>Version 1.0, this will hopefully be the only release.</p> + <p>Version 1.0.1, this will hopefully be the only release.</p> <h2>What is the h koan?</h2> @@ -328,18 +328,18 @@ const faqTemplate = `<html> <p>That's a good question. The following blogposts may help you understand this more:</p> <ul> - <li><a href="https://christine.website/blog/the-origin-of-h-2015-12-14">The Origin of h</a></li> - <li><a href="https://christine.website/blog/formal-grammar-of-h-2019-05-19">A Formal Grammar of h</a></li> + <li><a href="https://xeiaso.net/blog/the-origin-of-h-2015-12-14">The Origin of h</a></li> + <li><a href="https://xeiaso.net/blog/formal-grammar-of-h-2019-05-19">A Formal Grammar of h</a></li> </ul> <h2>Who wrote h?</h2> - <p><a href="https://christine.website">Within</a></p> + <p><a href="https://xeiaso.net">Within</a></p> <hr /> <footer> - <center><p><i>From <a href="https://christine.website">Within</a></i></p></center> + <center><p><i>From <a href="https://xeiaso.net">Within</a></i></p></center> </footer> </main> </body> @@ -382,16 +382,11 @@ const playgroundTemplate = `<html> <p>Gas used: <span id="gas_used"></span></p> <p>Execution time (nanoseconds): <span id="exec_time"></span></p> - <h4>AST</h4> - - <code><pre id="ast_box"></pre></code> - <script> function runProgram() { const programData = document.getElementById("program").value; const output = document.getElementById("output"); const watBox = document.getElementById("wat_box"); - const astBox = document.getElementById("ast_box"); const gasUsed = document.getElementById("gas_used"); const execTime = document.getElementById("exec_time"); const status = document.getElementById("status"); @@ -407,7 +402,6 @@ const playgroundTemplate = `<html> status.innerHTML = "success"; watBox.innerHTML = data.prog.wat; - astBox.innerHTML = data.prog.ast; output.innerHTML = data.res.out; gasUsed.innerHTML = data.res.gas; execTime.innerHTML = data.res.exec_duration; @@ -435,7 +429,7 @@ const playgroundTemplate = `<html> <hr /> <footer> - <center><p><i>From <a href="https://christine.website">Within</a></i></p></center> + <center><p><i>From <a href="https://xeiaso.net">Within</a></i></p></center> </footer> </main> </body> diff --git a/cmd/hlang/main.go b/cmd/hlang/main.go index faa2779..c17b5e7 100644 --- a/cmd/hlang/main.go +++ b/cmd/hlang/main.go @@ -15,7 +15,6 @@ var _ = func() error { log.SetFlags(log.LstdFlags | log.Llongfile); return nil } var ( 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") sockpath = flag.String("sockpath", "", "Unix domain socket to listen on") writeTao = flag.Bool("koan", false, "if true, print the h koan and then exit") @@ -68,15 +67,6 @@ func oneOff() error { 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 } @@ -109,26 +99,3 @@ func main() { return } } - -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 39)) - {{ 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 "h" (func $h_main)) -)` diff --git a/cmd/hlang/nguh/compile.go b/cmd/hlang/nguh/compile.go new file mode 100644 index 0000000..d268d77 --- /dev/null +++ b/cmd/hlang/nguh/compile.go @@ -0,0 +1,177 @@ +package nguh + +import ( + "bytes" + "fmt" + "io" + + "github.com/eaburns/peggy/peg" + "github.com/go-interpreter/wagon/wasm/leb128" +) + +// Section is a WebAssembly binary section. +type Section struct { + Kind uint32 + Len uint32 + Data bytes.Buffer +} + +// WebAssembly section types +const ( + SectionTypeType = 0x01 + SectionTypeImport = 0x02 + SectionTypeFunc = 0x03 + SectionTypeExport = 0x07 + SectionTypeCode = 0x0a +) + +// Commit turns a section into WebAssembly bytecode +func (s *Section) Commit(out io.Writer) error { + if s.Len != uint32(s.Data.Len()) { + s.Len = uint32(s.Data.Len()) + } + + if _, err := leb128.WriteVarUint32(out, s.Kind); err != nil { + return err + } + if _, err := leb128.WriteVarUint32(out, s.Len); err != nil { + return err + } + + if _, err := io.Copy(out, &s.Data); err != nil { + return err + } + + return nil +} + +const ( + ExportFunc = 0x60 + WASMi32Type = 0x7f +) + +func Compile(tree *peg.Node) ([]byte, error) { + out := bytes.NewBuffer([]byte{ + // WebAssembly binary header / magic numbers + 0x00, 0x61, 0x73, 0x6d, // \0asm wasm magic number + 0x01, 0x00, 0x00, 0x00, // version 1 + }) + + typeS := &Section{Kind: SectionTypeType} + typeS.Data.Write([]byte{ + 0x02, // 2 entries + ExportFunc, 0x01, WASMi32Type, 0x00, // function type 0, 1 i32 param, 0 return + ExportFunc, 0x00, 0x00, // function type 1, 0 param, 0 return + }) + + if err := typeS.Commit(out); err != nil { + return nil, fmt.Errorf("can't write type section: %v", err) + } + + importS := &Section{Kind: SectionTypeImport} + importS.Data.Write([]byte{ + 0x01, // 1 entry + 0x01, 0x68, // module h + 0x01, 0x68, // name h + 0x00, // type index + 0x00, // function number + }) + + if err := importS.Commit(out); err != nil { + return nil, fmt.Errorf("can't write import section: %v", err) + } + + funcS := &Section{Kind: SectionTypeFunc} + funcS.Data.Write([]byte{ + 0x01, // function 1 + 0x01, // type 1 + }) + + if err := funcS.Commit(out); err != nil { + return nil, fmt.Errorf("can't write func section: %v", err) + } + + exportS := &Section{Kind: SectionTypeExport} + exportS.Data.Write([]byte{ + 0x01, // 1 entry + 0x01, 0x68, // "h" + 0x00, 0x01, // function 1 + }) + + if err := exportS.Commit(out); err != nil { + return nil, fmt.Errorf("can't write export section: %v", err) + } + + codeS := &Section{Kind: SectionTypeCode} + codeS.Data.Write([]byte{ + 0x01, // 1 entry + }) + + funcBuf := bytes.NewBuffer([]byte{ + 0x01, // 1 local declaration + 0x03, 0x7f, // 3 i32 values - (local i32 i32 i32) + 0x41, 0x0a, // i32.const 10 '\n' + 0x21, 0x00, // local.set 0 + 0x41, 0xe8, 0x00, // i32.const 104 - 'h' + 0x21, 0x01, // local.set 1 + 0x41, 0x27, // i32.const 39 ' + 0x21, 0x02, // local.set 2 + }) + + // compile AST to wasm + if len(tree.Kids) == 0 { + if err := compileOneNode(funcBuf, tree); err != nil { + return nil, err + } + } else { + for _, node := range tree.Kids { + if err := compileOneNode(funcBuf, node); err != nil { + return nil, err + } + } + } + + // finally print newline + funcBuf.Write([]byte{ + 0x20, 0x00, // local.get 0 + 0x10, 0x00, // call 0 + 0x0b, // end of function + }) + + if _, err := leb128.WriteVarUint32(&codeS.Data, uint32(funcBuf.Len())); err != nil { + return nil, err + } + + if _, err := io.Copy(&codeS.Data, funcBuf); err != nil { + return nil, err + } + + if err := codeS.Commit(out); err != nil { + return nil, fmt.Errorf("can't write code section: %v", err) + } + + return out.Bytes(), nil +} + +func compileOneNode(out io.Writer, node *peg.Node) error { + switch node.Text { + case "h": + if _, err := out.Write([]byte{ + 0x20, 0x01, // local.get 1 + 0x10, 0x00, // call 0 + }); err != nil { + return err + } + case "'": + if _, err := out.Write([]byte{ + 0x20, 0x02, // local.get 2 + 0x10, 0x00, // call 0 + }); err != nil { + return err + } + default: + fmt.Errorf("h: le vi lerfu zo %q cu gentoldra", node.Text) + } + + return nil +} diff --git a/cmd/hlang/nguh/compile_test.go b/cmd/hlang/nguh/compile_test.go new file mode 100644 index 0000000..2b130f9 --- /dev/null +++ b/cmd/hlang/nguh/compile_test.go @@ -0,0 +1,20 @@ +package nguh + +import ( + "encoding/json" + "os" + "testing" + + "github.com/eaburns/peggy/peg" +) + +func TestCompile(t *testing.T) { + inp := &peg.Node{Text: "h"} + + result, err := Compile(inp) + if err != nil { + t.Fatal(err) + } + + json.NewEncoder(os.Stdout).Encode(result) +} diff --git a/cmd/hlang/nguh/disasm.txt b/cmd/hlang/nguh/disasm.txt new file mode 100644 index 0000000..689b307 --- /dev/null +++ b/cmd/hlang/nguh/disasm.txt @@ -0,0 +1,48 @@ +unsigned char __simple_wasm[] = { + 0x00, 0x61, 0x73, 0x6d, // \0asm wasm magic number + 0x01, 0x00, 0x00, 0x00, // version 1 + + 0x01, // type section + 0x08, // 8 bytes long + 0x02, // 2 entries + 0x60, 0x01, 0x7f, 0x00, // function type 0, 1 i32 param, 0 return + 0x60, 0x00, 0x00, // function type 1, 0 param, 0 return + + 0x02, // import section + 0x07, // 7 bytes long + 0x01, // 1 entry + 0x01, 0x68, // module h + 0x01, 0x68, // name h + 0x00, // type index + 0x00, // function number + + 0x03, // func section + 0x02, // 2 bytes long + 0x01, // function 1 + 0x01, // type 1 + + 0x07, // export section + 0x05, // 5 bytes long + 0x01, // 1 entry + 0x01, 0x68, // "h" + 0x00, 0x01, // function 1 + + 0x0a, // code section + 0x1b, // 27 bytes long + 0x01, // 1 entry + 0x19, // 25 bytes long + 0x01, // 1 local declaration + 0x03, 0x7f, // 3 i32 values - (local i32 i32 i32) + 0x41, 0x0a, // i32.const 10 + 0x21, 0x00, // local.set 0 + 0x41, 0xe8, 0x00, // i32.const 104 + 0x21, 0x01, // local.set 1 + 0x41, 0x27, // i32.const 39 + 0x21, 0x02, // local.set 2 + 0x20, 0x01, // local.get 1 + 0x10, 0x00, // call 0 + 0x20, 0x00, // local.get 0 + 0x10, 0x00, // call 0 + 0x0b // end of function +}; +unsigned int __simple_wasm_len = 67; diff --git a/cmd/hlang/nguh/doc.go b/cmd/hlang/nguh/doc.go new file mode 100644 index 0000000..d31d28d --- /dev/null +++ b/cmd/hlang/nguh/doc.go @@ -0,0 +1,4 @@ +// Package nguh is the Next Generation Universal Hlang compiler. nguh gives u hlang. +// +// nguh outputs WebAssembly directly instead of shelling out to wat2wasm. It is not known to have any bugs that are not features. +package nguh |
