aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXe <me@christine.website>2022-12-31 13:37:49 -0500
committerXe <me@christine.website>2022-12-31 13:37:49 -0500
commit44273f537aa7da7bfdef141941586018ec36e5b4 (patch)
treee7d46578a6116b401a72dc7d3cefe377faa50dba
parent04fe1cb27f4cab8ae55a2ffec1281fddf8f1bdfe (diff)
downloadx-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.go81
-rw-r--r--cmd/hlang/http.go24
-rw-r--r--cmd/hlang/main.go33
-rw-r--r--cmd/hlang/nguh/compile.go177
-rw-r--r--cmd/hlang/nguh/compile_test.go20
-rw-r--r--cmd/hlang/nguh/disasm.txt48
-rw-r--r--cmd/hlang/nguh/doc.go4
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