diff options
| author | Christine Dodrill <me@christine.website> | 2019-06-25 22:59:06 +0000 |
|---|---|---|
| committer | Christine Dodrill <me@christine.website> | 2019-06-25 22:59:06 +0000 |
| commit | 96a073ecddf7eee051050347a09786dc38b3a8ac (patch) | |
| tree | 44644398eaf655e5be51cdbd8bab25330bc0bd58 | |
| parent | ab2f1f58d4de28aa708cdbc1602c5ae1a941fce6 (diff) | |
| download | x-96a073ecddf7eee051050347a09786dc38b3a8ac.tar.xz x-96a073ecddf7eee051050347a09786dc38b3a8ac.zip | |
h
| -rw-r--r-- | cmd/h/compile.go | 98 | ||||
| -rw-r--r-- | cmd/h/main.go | 59 | ||||
| -rw-r--r-- | cmd/h/run.go | 76 | ||||
| -rw-r--r-- | go.mod | 5 | ||||
| -rw-r--r-- | go.sum | 6 | ||||
| -rw-r--r-- | h/h.peg | 12 | ||||
| -rw-r--r-- | h/h_gen.go | 789 | ||||
| -rw-r--r-- | h/parser.go | 42 | ||||
| -rw-r--r-- | h/simplify.go | 59 | ||||
| -rw-r--r-- | jbo/namcu/namcu.go | 4 |
10 files changed, 1149 insertions, 1 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 +} @@ -12,7 +12,7 @@ require ( github.com/dgryski/go-failure v0.0.0-20151001134759-4963dbd58fd0 github.com/dgryski/go-onlinestats v0.0.0-20170612111826-1c7d19468768 // indirect github.com/disintegration/imaging v1.6.0 - github.com/eaburns/peggy v0.0.0-20190420135231-b61cdde6efe6 // indirect + github.com/eaburns/peggy v0.0.0-20190420135231-b61cdde6efe6 github.com/eaburns/pretty v0.0.0-20190404101635-2e1d2550ef0b // indirect github.com/elazarl/go-bindata-assetfs v1.0.0 github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 @@ -46,6 +46,7 @@ require ( github.com/otiai10/copy v1.0.1 github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776 // indirect github.com/pborman/uuid v1.2.0 + github.com/perlin-network/life v0.0.0-20190625155037-103174020946 github.com/peterh/liner v1.1.0 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 github.com/pkg/errors v0.8.1 @@ -79,3 +80,5 @@ require ( within.website/johaus v1.1.0 within.website/ln v0.6.0 ) + +replace github.com/go-interpreter/wagon v0.0.0 => github.com/perlin-network/wagon v0.3.1-0.20180825141017-f8cb99b55a39 @@ -114,6 +114,10 @@ github.com/otiai10/mint v1.2.4 h1:DxYL0itZyPaR5Z9HILdxSoHx+gNs6Yx+neOGS3IVUk0= github.com/otiai10/mint v1.2.4/go.mod h1:d+b7n/0R3tdyUYYylALXpWQ/kTN+QobSq/4SRGBkR3M= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/perlin-network/life v0.0.0-20190625155037-103174020946 h1:qA5Q7rgctaRZKDiTKbEJrazx1M4Pbiwb8Gfr3WiuVTg= +github.com/perlin-network/life v0.0.0-20190625155037-103174020946/go.mod h1:z/EH0mO9zbeuTT5NX4u2VqVSG8y2vDQXz6iDKxikW2I= +github.com/perlin-network/wagon v0.3.1-0.20180825141017-f8cb99b55a39 h1:CYHXy6CWxxL7ugjvCbTELOm2j5iRLEWGPl3AQYvretw= +github.com/perlin-network/wagon v0.3.1-0.20180825141017-f8cb99b55a39/go.mod h1:zHOMvbitcZek8oshsMO5VpyBjWjV9X8cn8WTZwdebpM= github.com/peterh/liner v1.1.0 h1:f+aAedNJA6uk7+6rXsYBnhdo4Xux7ESLe+kcuVUF5os= github.com/peterh/liner v1.1.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= @@ -150,6 +154,8 @@ github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/ github.com/tmc/scp v0.0.0-20170824174625-f7b48647feef h1:7D6Nm4D6f0ci9yttWaKjM1TMAXrH5Su72dojqYGntFY= github.com/tmc/scp v0.0.0-20170824174625-f7b48647feef/go.mod h1:WLFStEdnJXpjK8kd4qKLwQKX/1vrDzp5BcDyiZJBHJM= github.com/velour/chat v0.0.0-20180713122344-fd1d1606cb89/go.mod h1:ejwOYCjnDMyO5LXFXRARQJGBZ6xQJZ3rgAHE5drSuMM= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 h1:YyPWX3jLOtYKulBR6AScGIs74lLrJcgeKRwcbAuQOG4= github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/vnIPqR89fFKonnWPiZymN5ydRJkX8= @@ -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/h/h_gen.go b/h/h_gen.go new file mode 100644 index 0000000..365b630 --- /dev/null +++ b/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/h/parser.go b/h/parser.go new file mode 100644 index 0000000..f061ec7 --- /dev/null +++ b/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 zo %s", namcu.Lerfu(perr)) + } + + tree := p.ParseTree() + RemoveSpace(tree) + CollapseLists(tree) + + return tree, nil +} diff --git a/h/simplify.go b/h/simplify.go new file mode 100644 index 0000000..36013df --- /dev/null +++ b/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/jbo/namcu/namcu.go b/jbo/namcu/namcu.go index 7742dde..1943c21 100644 --- a/jbo/namcu/namcu.go +++ b/jbo/namcu/namcu.go @@ -14,6 +14,10 @@ const ( Nine = "so" ) +func Lerfu(i int) string { + return lojbanDigit(i) +} + func lojbanDigit(i int) string { switch i { case 0: |
