diff options
| author | Xe <me@christine.website> | 2022-12-30 10:13:56 -0500 |
|---|---|---|
| committer | Xe <me@christine.website> | 2022-12-30 10:13:56 -0500 |
| commit | e3e8ebbcf82f221d026385c5a85d0afde62b3e3d (patch) | |
| tree | 1ab8a5811db5a64ed14d3112e47730d0fe97325a /cmd/hlang | |
| parent | 9bdbd3156177640afaf18ca2c4ad783138004e4a (diff) | |
| download | x-e3e8ebbcf82f221d026385c5a85d0afde62b3e3d.tar.xz x-e3e8ebbcf82f221d026385c5a85d0afde62b3e3d.zip | |
add old hlang draft
Signed-off-by: Xe <me@christine.website>
Diffstat (limited to 'cmd/hlang')
| -rw-r--r-- | cmd/hlang/Dockerfile | 21 | ||||
| -rw-r--r-- | cmd/hlang/LICENSE | 12 | ||||
| -rw-r--r-- | cmd/hlang/README.md | 3 | ||||
| -rw-r--r-- | cmd/hlang/compile.go | 105 | ||||
| -rw-r--r-- | cmd/hlang/h/h.peg | 12 | ||||
| -rw-r--r-- | cmd/hlang/h/h_gen.go | 789 | ||||
| -rw-r--r-- | cmd/hlang/h/parser.go | 42 | ||||
| -rw-r--r-- | cmd/hlang/h/simplify.go | 59 | ||||
| -rw-r--r-- | cmd/hlang/http.go | 442 | ||||
| -rw-r--r-- | cmd/hlang/main.go | 134 | ||||
| -rw-r--r-- | cmd/hlang/run.go | 73 | ||||
| -rw-r--r-- | cmd/hlang/version.go | 13 |
12 files changed, 1705 insertions, 0 deletions
diff --git a/cmd/hlang/Dockerfile b/cmd/hlang/Dockerfile new file mode 100644 index 0000000..86267a9 --- /dev/null +++ b/cmd/hlang/Dockerfile @@ -0,0 +1,21 @@ +FROM xena/go:1.13.3 AS build +WORKDIR /hlang +COPY . . +RUN GOBIN=/usr/local/bin go install . + +FROM xena/alpine AS wasm +WORKDIR /wabt +RUN apk --no-cache add build-base cmake git python \ + && git clone --recursive https://github.com/WebAssembly/wabt /wabt \ + && mkdir build \ + && cd build \ + && cmake .. \ + && make && make install +RUN ldd $(which wat2wasm) + +FROM xena/alpine +COPY --from=wasm /usr/local/bin/wat2wasm /usr/local/bin/wat2wasm +COPY --from=build /usr/local/bin/hlang /usr/local/bin/hlang +ENV PORT 5000 +RUN apk --no-cache add libstdc++ +CMD /usr/local/bin/hlang diff --git a/cmd/hlang/LICENSE b/cmd/hlang/LICENSE new file mode 100644 index 0000000..87df039 --- /dev/null +++ b/cmd/hlang/LICENSE @@ -0,0 +1,12 @@ +Copyright (c) 2019 Christine Dodrill <me@christine.website> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE.
\ No newline at end of file diff --git a/cmd/hlang/README.md b/cmd/hlang/README.md new file mode 100644 index 0000000..843d30a --- /dev/null +++ b/cmd/hlang/README.md @@ -0,0 +1,3 @@ +# h + +https://h.christine.website diff --git a/cmd/hlang/compile.go b/cmd/hlang/compile.go new file mode 100644 index 0000000..46e7239 --- /dev/null +++ b/cmd/hlang/compile.go @@ -0,0 +1,105 @@ +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" +) + +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"` +} + +func compile(source string) (*CompiledProgram, error) { + tree, err := h.Parse(source) + if err != nil { + return nil, err + } + + 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)) + 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/hlang/h/h.peg b/cmd/hlang/h/h.peg new file mode 100644 index 0000000..542a887 --- /dev/null +++ b/cmd/hlang/h/h.peg @@ -0,0 +1,12 @@ +{ +package h + +import ( + "github.com/eaburns/peggy/peg" +) +} + +sep <- space+h +space <- ' ' +h <- 'h' / "'" +H <- h+sep+ / h diff --git a/cmd/hlang/h/h_gen.go b/cmd/hlang/h/h_gen.go new file mode 100644 index 0000000..365b630 --- /dev/null +++ b/cmd/hlang/h/h_gen.go @@ -0,0 +1,789 @@ +package h + +import ( + "github.com/eaburns/peggy/peg" +) + +type _Parser struct { + text string + deltaPos []_Rules + deltaErr []_Rules + node map[_key]*peg.Node + fail map[_key]*peg.Fail + lastFail int + data interface{} +} + +type _key struct { + start int + name string +} + +func _NewParser(text string) *_Parser { + return &_Parser{ + text: text, + deltaPos: make([]_Rules, len(text)+1), + deltaErr: make([]_Rules, len(text)+1), + node: make(map[_key]*peg.Node), + fail: make(map[_key]*peg.Fail), + } +} + +type _Rules struct { + sep int32 + space int32 + h int32 + H int32 +} + +func _max(a, b int) int { + if a > b { + return a + } + return b +} + +func _next(parser *_Parser, pos int) (rune, int) { + r, w := peg.DecodeRuneInString(parser.text[pos:]) + return r, w +} + +func _node(name string) *peg.Node { + return &peg.Node{Name: name} +} + +func _sub(parser *_Parser, start, end int, kids []*peg.Node) *peg.Node { + node := &peg.Node{ + Text: parser.text[start:end], + Kids: make([]*peg.Node, len(kids)), + } + copy(node.Kids, kids) + return node +} + +func _leaf(parser *_Parser, start, end int) *peg.Node { + return &peg.Node{Text: parser.text[start:end]} +} + +func _sepAccepts(parser *_Parser, start int) (deltaPos, deltaErr int) { + if dp := parser.deltaPos[start].sep; dp != 0 { + de := parser.deltaErr[start].sep - 1 + if dp > 0 { + dp-- + } + return int(dp), int(de) + } + pos, perr := start, -1 + // space+ h + // space+ + // space + if dp, de := _spaceAccepts(parser, pos); dp < 0 { + perr = _max(perr, pos+de) + goto fail + } else { + perr = _max(perr, pos+de) + pos += dp + } + for { + pos1 := pos + // space + if dp, de := _spaceAccepts(parser, pos); dp < 0 { + perr = _max(perr, pos+de) + goto fail2 + } else { + perr = _max(perr, pos+de) + pos += dp + } + continue + fail2: + pos = pos1 + break + } + // h + if dp, de := _hAccepts(parser, pos); dp < 0 { + perr = _max(perr, pos+de) + goto fail + } else { + perr = _max(perr, pos+de) + pos += dp + } + parser.deltaPos[start].sep = int32(pos-start) + 1 + parser.deltaErr[start].sep = int32(perr-start) + 1 + parser.lastFail = perr + return pos - start, perr - start +fail: + parser.deltaPos[start].sep = -1 + parser.deltaErr[start].sep = int32(perr-start) + 1 + parser.lastFail = perr + return -1, perr - start +} + +func _sepNode(parser *_Parser, start int) (int, *peg.Node) { + dp := parser.deltaPos[start].sep + if dp < 0 { + return -1, nil + } + key := _key{start: start, name: "sep"} + node := parser.node[key] + if node != nil { + return start + int(dp-1), node + } + pos := start + node = _node("sep") + // space+ h + // space+ + // space + if p, kid := _spaceNode(parser, pos); kid == nil { + goto fail + } else { + node.Kids = append(node.Kids, kid) + pos = p + } + for { + nkids0 := len(node.Kids) + pos1 := pos + // space + if p, kid := _spaceNode(parser, pos); kid == nil { + goto fail2 + } else { + node.Kids = append(node.Kids, kid) + pos = p + } + continue + fail2: + node.Kids = node.Kids[:nkids0] + pos = pos1 + break + } + // h + if p, kid := _hNode(parser, pos); kid == nil { + goto fail + } else { + node.Kids = append(node.Kids, kid) + pos = p + } + node.Text = parser.text[start:pos] + parser.node[key] = node + return pos, node +fail: + return -1, nil +} + +func _sepFail(parser *_Parser, start, errPos int) (int, *peg.Fail) { + if start > parser.lastFail { + return -1, &peg.Fail{} + } + dp := parser.deltaPos[start].sep + de := parser.deltaErr[start].sep + if start+int(de-1) < errPos { + if dp > 0 { + return start + int(dp-1), &peg.Fail{} + } + return -1, &peg.Fail{} + } + key := _key{start: start, name: "sep"} + failure := parser.fail[key] + if dp < 0 && failure != nil { + return -1, failure + } + if dp > 0 && failure != nil { + return start + int(dp-1), failure + } + pos := start + failure = &peg.Fail{ + Name: "sep", + Pos: int(start), + } + // space+ h + // space+ + // space + { + p, kid := _spaceFail(parser, pos, errPos) + if kid.Want != "" || len(kid.Kids) > 0 { + failure.Kids = append(failure.Kids, kid) + } + if p < 0 { + goto fail + } + pos = p + } + for { + pos1 := pos + // space + { + p, kid := _spaceFail(parser, pos, errPos) + if kid.Want != "" || len(kid.Kids) > 0 { + failure.Kids = append(failure.Kids, kid) + } + if p < 0 { + goto fail2 + } + pos = p + } + continue + fail2: + pos = pos1 + break + } + // h + { + p, kid := _hFail(parser, pos, errPos) + if kid.Want != "" || len(kid.Kids) > 0 { + failure.Kids = append(failure.Kids, kid) + } + if p < 0 { + goto fail + } + pos = p + } + parser.fail[key] = failure + return pos, failure +fail: + parser.fail[key] = failure + return -1, failure +} + +func _spaceAccepts(parser *_Parser, start int) (deltaPos, deltaErr int) { + if dp := parser.deltaPos[start].space; dp != 0 { + de := parser.deltaErr[start].space - 1 + if dp > 0 { + dp-- + } + return int(dp), int(de) + } + pos, perr := start, -1 + // " " + if len(parser.text[pos:]) < 1 || parser.text[pos:pos+1] != " " { + perr = _max(perr, pos) + goto fail + } + pos++ + parser.deltaPos[start].space = int32(pos-start) + 1 + parser.deltaErr[start].space = int32(perr-start) + 1 + parser.lastFail = perr + return pos - start, perr - start +fail: + parser.deltaPos[start].space = -1 + parser.deltaErr[start].space = int32(perr-start) + 1 + parser.lastFail = perr + return -1, perr - start +} + +func _spaceNode(parser *_Parser, start int) (int, *peg.Node) { + dp := parser.deltaPos[start].space + if dp < 0 { + return -1, nil + } + key := _key{start: start, name: "space"} + node := parser.node[key] + if node != nil { + return start + int(dp-1), node + } + pos := start + node = _node("space") + // " " + if len(parser.text[pos:]) < 1 || parser.text[pos:pos+1] != " " { + goto fail + } + node.Kids = append(node.Kids, _leaf(parser, pos, pos+1)) + pos++ + node.Text = parser.text[start:pos] + parser.node[key] = node + return pos, node +fail: + return -1, nil +} + +func _spaceFail(parser *_Parser, start, errPos int) (int, *peg.Fail) { + if start > parser.lastFail { + return -1, &peg.Fail{} + } + dp := parser.deltaPos[start].space + de := parser.deltaErr[start].space + if start+int(de-1) < errPos { + if dp > 0 { + return start + int(dp-1), &peg.Fail{} + } + return -1, &peg.Fail{} + } + key := _key{start: start, name: "space"} + failure := parser.fail[key] + if dp < 0 && failure != nil { + return -1, failure + } + if dp > 0 && failure != nil { + return start + int(dp-1), failure + } + pos := start + failure = &peg.Fail{ + Name: "space", + Pos: int(start), + } + // " " + if len(parser.text[pos:]) < 1 || parser.text[pos:pos+1] != " " { + if pos >= errPos { + failure.Kids = append(failure.Kids, &peg.Fail{ + Pos: int(pos), + Want: "\" \"", + }) + } + goto fail + } + pos++ + parser.fail[key] = failure + return pos, failure +fail: + parser.fail[key] = failure + return -1, failure +} + +func _hAccepts(parser *_Parser, start int) (deltaPos, deltaErr int) { + if dp := parser.deltaPos[start].h; dp != 0 { + de := parser.deltaErr[start].h - 1 + if dp > 0 { + dp-- + } + return int(dp), int(de) + } + pos, perr := start, -1 + // "h"/"'" + { + pos2 := pos + // "h" + if len(parser.text[pos:]) < 1 || parser.text[pos:pos+1] != "h" { + perr = _max(perr, pos) + goto fail3 + } + pos++ + goto ok0 + fail3: + pos = pos2 + // "'" + if len(parser.text[pos:]) < 1 || parser.text[pos:pos+1] != "'" { + perr = _max(perr, pos) + goto fail4 + } + pos++ + goto ok0 + fail4: + pos = pos2 + goto fail + ok0: + } + parser.deltaPos[start].h = int32(pos-start) + 1 + parser.deltaErr[start].h = int32(perr-start) + 1 + parser.lastFail = perr + return pos - start, perr - start +fail: + parser.deltaPos[start].h = -1 + parser.deltaErr[start].h = int32(perr-start) + 1 + parser.lastFail = perr + return -1, perr - start +} + +func _hNode(parser *_Parser, start int) (int, *peg.Node) { + dp := parser.deltaPos[start].h + if dp < 0 { + return -1, nil + } + key := _key{start: start, name: "h"} + node := parser.node[key] + if node != nil { + return start + int(dp-1), node + } + pos := start + node = _node("h") + // "h"/"'" + { + pos2 := pos + nkids1 := len(node.Kids) + // "h" + if len(parser.text[pos:]) < 1 || parser.text[pos:pos+1] != "h" { + goto fail3 + } + node.Kids = append(node.Kids, _leaf(parser, pos, pos+1)) + pos++ + goto ok0 + fail3: + node.Kids = node.Kids[:nkids1] + pos = pos2 + // "'" + if len(parser.text[pos:]) < 1 || parser.text[pos:pos+1] != "'" { + goto fail4 + } + node.Kids = append(node.Kids, _leaf(parser, pos, pos+1)) + pos++ + goto ok0 + fail4: + node.Kids = node.Kids[:nkids1] + pos = pos2 + goto fail + ok0: + } + node.Text = parser.text[start:pos] + parser.node[key] = node + return pos, node +fail: + return -1, nil +} + +func _hFail(parser *_Parser, start, errPos int) (int, *peg.Fail) { + if start > parser.lastFail { + return -1, &peg.Fail{} + } + dp := parser.deltaPos[start].h + de := parser.deltaErr[start].h + if start+int(de-1) < errPos { + if dp > 0 { + return start + int(dp-1), &peg.Fail{} + } + return -1, &peg.Fail{} + } + key := _key{start: start, name: "h"} + failure := parser.fail[key] + if dp < 0 && failure != nil { + return -1, failure + } + if dp > 0 && failure != nil { + return start + int(dp-1), failure + } + pos := start + failure = &peg.Fail{ + Name: "h", + Pos: int(start), + } + // "h"/"'" + { + pos2 := pos + // "h" + if len(parser.text[pos:]) < 1 || parser.text[pos:pos+1] != "h" { + if pos >= errPos { + failure.Kids = append(failure.Kids, &peg.Fail{ + Pos: int(pos), + Want: "\"h\"", + }) + } + goto fail3 + } + pos++ + goto ok0 + fail3: + pos = pos2 + // "'" + if len(parser.text[pos:]) < 1 || parser.text[pos:pos+1] != "'" { + if pos >= errPos { + failure.Kids = append(failure.Kids, &peg.Fail{ + Pos: int(pos), + Want: "\"'\"", + }) + } + goto fail4 + } + pos++ + goto ok0 + fail4: + pos = pos2 + goto fail + ok0: + } + parser.fail[key] = failure + return pos, failure +fail: + parser.fail[key] = failure + return -1, failure +} + +func _HAccepts(parser *_Parser, start int) (deltaPos, deltaErr int) { + if dp := parser.deltaPos[start].H; dp != 0 { + de := parser.deltaErr[start].H - 1 + if dp > 0 { + dp-- + } + return int(dp), int(de) + } + pos, perr := start, -1 + // h+ sep+/h + { + pos2 := pos + // h+ sep+ + // h+ + // h + if dp, de := _hAccepts(parser, pos); dp < 0 { + perr = _max(perr, pos+de) + goto fail3 + } else { + perr = _max(perr, pos+de) + pos += dp + } + for { + pos5 := pos + // h + if dp, de := _hAccepts(parser, pos); dp < 0 { + perr = _max(perr, pos+de) + goto fail6 + } else { + perr = _max(perr, pos+de) + pos += dp + } + continue + fail6: + pos = pos5 + break + } + // sep+ + // sep + if dp, de := _sepAccepts(parser, pos); dp < 0 { + perr = _max(perr, pos+de) + goto fail3 + } else { + perr = _max(perr, pos+de) + pos += dp + } + for { + pos8 := pos + // sep + if dp, de := _sepAccepts(parser, pos); dp < 0 { + perr = _max(perr, pos+de) + goto fail9 + } else { + perr = _max(perr, pos+de) + pos += dp + } + continue + fail9: + pos = pos8 + break + } + goto ok0 + fail3: + pos = pos2 + // h + if dp, de := _hAccepts(parser, pos); dp < 0 { + perr = _max(perr, pos+de) + goto fail10 + } else { + perr = _max(perr, pos+de) + pos += dp + } + goto ok0 + fail10: + pos = pos2 + goto fail + ok0: + } + parser.deltaPos[start].H = int32(pos-start) + 1 + parser.deltaErr[start].H = int32(perr-start) + 1 + parser.lastFail = perr + return pos - start, perr - start +fail: + parser.deltaPos[start].H = -1 + parser.deltaErr[start].H = int32(perr-start) + 1 + parser.lastFail = perr + return -1, perr - start +} + +func _HNode(parser *_Parser, start int) (int, *peg.Node) { + dp := parser.deltaPos[start].H + if dp < 0 { + return -1, nil + } + key := _key{start: start, name: "H"} + node := parser.node[key] + if node != nil { + return start + int(dp-1), node + } + pos := start + node = _node("H") + // h+ sep+/h + { + pos2 := pos + nkids1 := len(node.Kids) + // h+ sep+ + // h+ + // h + if p, kid := _hNode(parser, pos); kid == nil { + goto fail3 + } else { + node.Kids = append(node.Kids, kid) + pos = p + } + for { + nkids4 := len(node.Kids) + pos5 := pos + // h + if p, kid := _hNode(parser, pos); kid == nil { + goto fail6 + } else { + node.Kids = append(node.Kids, kid) + pos = p + } + continue + fail6: + node.Kids = node.Kids[:nkids4] + pos = pos5 + break + } + // sep+ + // sep + if p, kid := _sepNode(parser, pos); kid == nil { + goto fail3 + } else { + node.Kids = append(node.Kids, kid) + pos = p + } + for { + nkids7 := len(node.Kids) + pos8 := pos + // sep + if p, kid := _sepNode(parser, pos); kid == nil { + goto fail9 + } else { + node.Kids = append(node.Kids, kid) + pos = p + } + continue + fail9: + node.Kids = node.Kids[:nkids7] + pos = pos8 + break + } + goto ok0 + fail3: + node.Kids = node.Kids[:nkids1] + pos = pos2 + // h + if p, kid := _hNode(parser, pos); kid == nil { + goto fail10 + } else { + node.Kids = append(node.Kids, kid) + pos = p + } + goto ok0 + fail10: + node.Kids = node.Kids[:nkids1] + pos = pos2 + goto fail + ok0: + } + node.Text = parser.text[start:pos] + parser.node[key] = node + return pos, node +fail: + return -1, nil +} + +func _HFail(parser *_Parser, start, errPos int) (int, *peg.Fail) { + if start > parser.lastFail { + return -1, &peg.Fail{} + } + dp := parser.deltaPos[start].H + de := parser.deltaErr[start].H + if start+int(de-1) < errPos { + if dp > 0 { + return start + int(dp-1), &peg.Fail{} + } + return -1, &peg.Fail{} + } + key := _key{start: start, name: "H"} + failure := parser.fail[key] + if dp < 0 && failure != nil { + return -1, failure + } + if dp > 0 && failure != nil { + return start + int(dp-1), failure + } + pos := start + failure = &peg.Fail{ + Name: "H", + Pos: int(start), + } + // h+ sep+/h + { + pos2 := pos + // h+ sep+ + // h+ + // h + { + p, kid := _hFail(parser, pos, errPos) + if kid.Want != "" || len(kid.Kids) > 0 { + failure.Kids = append(failure.Kids, kid) + } + if p < 0 { + goto fail3 + } + pos = p + } + for { + pos5 := pos + // h + { + p, kid := _hFail(parser, pos, errPos) + if kid.Want != "" || len(kid.Kids) > 0 { + failure.Kids = append(failure.Kids, kid) + } + if p < 0 { + goto fail6 + } + pos = p + } + continue + fail6: + pos = pos5 + break + } + // sep+ + // sep + { + p, kid := _sepFail(parser, pos, errPos) + if kid.Want != "" || len(kid.Kids) > 0 { + failure.Kids = append(failure.Kids, kid) + } + if p < 0 { + goto fail3 + } + pos = p + } + for { + pos8 := pos + // sep + { + p, kid := _sepFail(parser, pos, errPos) + if kid.Want != "" || len(kid.Kids) > 0 { + failure.Kids = append(failure.Kids, kid) + } + if p < 0 { + goto fail9 + } + pos = p + } + continue + fail9: + pos = pos8 + break + } + goto ok0 + fail3: + pos = pos2 + // h + { + p, kid := _hFail(parser, pos, errPos) + if kid.Want != "" || len(kid.Kids) > 0 { + failure.Kids = append(failure.Kids, kid) + } + if p < 0 { + goto fail10 + } + pos = p + } + goto ok0 + fail10: + pos = pos2 + goto fail + ok0: + } + parser.fail[key] = failure + return pos, failure +fail: + parser.fail[key] = failure + return -1, failure +} diff --git a/cmd/hlang/h/parser.go b/cmd/hlang/h/parser.go new file mode 100644 index 0000000..4daa833 --- /dev/null +++ b/cmd/hlang/h/parser.go @@ -0,0 +1,42 @@ +package h + +//go:generate peggy -o h_gen.go h.peg + +import ( + "fmt" + + "github.com/eaburns/peggy/peg" + "within.website/x/jbo/namcu" +) + +func (p *_Parser) Parse() (int, bool) { + pos, perr := _HAccepts(p, 0) + return perr, pos >= 0 +} + +func (p *_Parser) ErrorTree(minPos int) *peg.Fail { + p.fail = make(map[_key]*peg.Fail) // reset fail memo table + _, tree := _HFail(p, 0, minPos) + return tree +} + +func (p *_Parser) ParseTree() *peg.Node { + _, tree := _HNode(p, 0) + return tree +} + +// Parse parses h. +// On success, the parseTree is returned. +// On failure, both the word-level and the raw, morphological errors are returned. +func Parse(text string) (*peg.Node, error) { + p := _NewParser(text) + if perr, ok := p.Parse(); !ok { + return nil, fmt.Errorf("h: gentoldra fi'o zvati fe li %s", namcu.Lerfu(perr)) + } + + tree := p.ParseTree() + RemoveSpace(tree) + CollapseLists(tree) + + return tree, nil +} diff --git a/cmd/hlang/h/simplify.go b/cmd/hlang/h/simplify.go new file mode 100644 index 0000000..36013df --- /dev/null +++ b/cmd/hlang/h/simplify.go @@ -0,0 +1,59 @@ +package h + +import ( + "strings" + + "github.com/eaburns/peggy/peg" +) + +// RemoveSpace removes whitespace-only nodes. +func RemoveSpace(n *peg.Node) { removeSpace(n) } + +func removeSpace(n *peg.Node) bool { + if whitespace(n.Text) { + return false + } + if len(n.Kids) == 0 { + return true + } + var kids []*peg.Node + for _, k := range n.Kids { + if removeSpace(k) { + kids = append(kids, k) + } + } + n.Kids = kids + return len(n.Kids) > 0 +} + +// SpaceChars is the string of all whitespace characters. +const SpaceChars = "\x20" + +func whitespace(s string) bool { + for _, r := range s { + if !strings.ContainsRune(SpaceChars, r) { + return false + } + } + return true +} + +// CollapseLists collapses chains of single-kid nodes. +func CollapseLists(n *peg.Node) { + if collapseLists(n) == 1 { + n.Kids = n.Kids[0].Kids + } +} + +func collapseLists(n *peg.Node) int { + var kids []*peg.Node + for _, k := range n.Kids { + if gk := collapseLists(k); gk == 1 { + kids = append(kids, k.Kids[0]) + } else { + kids = append(kids, k) + } + } + n.Kids = kids + return len(n.Kids) +} diff --git a/cmd/hlang/http.go b/cmd/hlang/http.go new file mode 100644 index 0000000..f096e3f --- /dev/null +++ b/cmd/hlang/http.go @@ -0,0 +1,442 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "net" + "net/http" + "os" + + "github.com/rs/cors" + "within.website/ln/ex" +) + +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.Handle("/", doTemplate(indexTemplate)) + http.Handle("/docs", doTemplate(docsTemplate)) + http.Handle("/faq", doTemplate(faqTemplate)) + http.Handle("/play", doTemplate(playgroundTemplate)) + http.HandleFunc("/api/playground", runPlayground) + + srv := &http.Server{ + Addr: ":" + *port, + Handler: ex.HTTPLog(cors.Default().Handler(http.DefaultServeMux)), + } + + if *sockpath != "" { + os.RemoveAll(*sockpath) + + l, err := net.Listen("unix", *sockpath) + if err != nil { + return fmt.Errorf("can't listen on %s: %v", *sockpath, err) + } + defer l.Close() + + return srv.Serve(l) + } else { + return srv.ListenAndServe() + } +} + +func httpError(w http.ResponseWriter, err error, code int) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + json.NewEncoder(w).Encode(struct { + Error string `json:"err"` + }{ + Error: err.Error(), + }) +} + +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 { + httpError(w, err, http.StatusBadGateway) + return + } + + comp, err := compile(string(data)) + if err != nil { + httpError(w, fmt.Errorf("compliation error: %v", err), http.StatusBadRequest) + return + } + + er, err := run(comp.Binary) + if err != nil { + httpError(w, fmt.Errorf("runtime error: %v", err), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(struct { + Program *CompiledProgram `json:"prog"` + Results *ExecResult `json:"res"` + }{ + Program: comp, + Results: er, + }) +} + +func doTemplate(body string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + fmt.Fprintln(w, body) + }) +} + +const indexTemplate = `<html> + <head> + <title>The h Programming Language</title> + <link rel="stylesheet" href="https://within.website/static/gruvbox.css"> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + </head> + <body> + <main> + <nav> + <a href="/">The h Programming Language</a> - + <a href="/docs">Docs</a> - + <a href="/play">Playground</a> - + <a href="/faq">FAQ</a> + </nav> + + <h1>The h Programming Language</h1> + + <p><big>A simple, fast, open-source, complete and safe language for developing modern software for the web</big></p> + + <hr /> + + <h2>Example Program</h2> + + <code><pre>h</pre></code> + + <p>Outputs:</p> + + <code><pre>h</pre></code> + + <hr /> + + <h2>Fast Compilation</h2> + + <p>h compiles hundreds of characters of source per second. I didn't really test how fast it is, but when I was testing it the speed was fast enough that I didn't care to profile it.</p> + + <hr /> + + <h2>Safety</h2> + + <p>h is completely memory safe with no garbage collector or heap allocations. It does not allow memory leaks to happen, nor do any programs in h have the possibility to allocate memory.</p> + + <ul> + <li>No null</li> + <li>Completely deterministic behavior</li> + <li>No mutable state</li> + <li>No persistence</li> + <li>All funct |
