Kaynağa Gözat

компилятор brainfuck2wasm плюс фиксы

kpmy 9 yıl önce
ebeveyn
işleme
ac07f6cf0e
6 değiştirilmiş dosya ile 349 ekleme ve 18 silme
  1. 306 0
      demo/bf/bf.go
  2. 4 0
      demo/bf/test0.bf
  3. 8 7
      ir/ast.go
  4. 25 4
      ir/mem.go
  5. 3 3
      ps/impl/sexpr/sexpr.go
  6. 3 4
      tiss_test.go

+ 306 - 0
demo/bf/bf.go

@@ -0,0 +1,306 @@
+package main
+
+import (
+	"bufio"
+	"bytes"
+	"container/list"
+	"flag"
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"unicode"
+
+	"github.com/kpmy/tiss/gen"
+	_ "github.com/kpmy/tiss/gen/impl"
+	"github.com/kpmy/tiss/ir"
+	"github.com/kpmy/tiss/ir/ops"
+	"github.com/kpmy/tiss/ir/types"
+	"github.com/kpmy/ypk/fn"
+	. "github.com/kpmy/ypk/tc"
+)
+
+type ConsFunc func(ir.CodeExpr)
+
+var filename string
+var rd io.RuneReader
+var mod *ir.Module
+var consumer_stack *list.List = list.New()
+
+func init() {
+	flag.StringVar(&filename, "i", "test0.bf", "-i <filename>")
+}
+
+func emit(e ir.CodeExpr) {
+	Assert(consumer_stack.Len() > 0, 20)
+	consumer := consumer_stack.Front().Value.(ConsFunc)
+	consumer(e)
+}
+
+func down(f ConsFunc) {
+	consumer_stack.PushFront(f)
+}
+
+func up() {
+	Assert(consumer_stack.Len() > 1, 20)
+	consumer_stack.Remove(consumer_stack.Front())
+}
+
+func depth() int {
+	return consumer_stack.Len()
+}
+
+func module() {
+	p := &ir.Local{}
+	p.Name("$p")
+	p.Type(types.I32)
+
+	v := &ir.Local{}
+	v.Name("$v")
+	v.Type(types.I32)
+
+	f := &ir.FuncExpr{}
+	f.Name("$start")
+	f.Locals = append(f.Locals, p, v)
+
+	down(func(e ir.CodeExpr) {
+		f.Code = append(f.Code, e)
+	})
+
+	ip := &ir.Param{}
+	ip.Type(types.I32)
+
+	imp := &ir.Import{}
+	imp.Name("$print")
+	imp.Mod = "spectest"
+	imp.Func = "print"
+	imp.Params = append(imp.Params, ip)
+
+	s := &ir.StartExpr{}
+	s.Var = ir.ThisVar("$start")
+
+	m := &ir.Memory{}
+	m.Initial = 1
+	m.Max = 1
+
+	mod.Mem = m
+	mod.Func = append(mod.Func, f)
+	mod.Imp = append(mod.Imp, imp)
+	mod.Start = s
+}
+
+var Vv = ir.ThisVar("$v")
+var Pv = ir.ThisVar("$p")
+var Pr = ir.ThisVar("$print")
+
+func do(cmd string) {
+	switch cmd {
+	case "+":
+		{
+			get := &ir.GetLocalExpr{}
+			get.Var = Pv
+
+			ld := &ir.LoadExpr{}
+			ld.Size = ir.Load8
+			ld.Type = types.I32
+			ld.Signed = false
+			ld.Expr = get
+
+			set := &ir.SetLocalExpr{}
+			set.Var = Vv
+			set.Expr = ld
+
+			emit(set)
+		}
+		{
+			get := &ir.GetLocalExpr{Var: Vv}
+
+			inc := &ir.DyadicOp{}
+			inc.Left = get
+			inc.Right = &ir.ConstExpr{Type: types.I32, Value: 1}
+			op := ops.Dyadic(types.I32, types.I32, ops.Add)
+			inc.Op = &op
+
+			set := &ir.SetLocalExpr{}
+			set.Var = Vv
+			set.Expr = inc
+
+			emit(set)
+		}
+		{
+			set := &ir.StoreExpr{}
+			set.Type = types.I32
+			set.Size = ir.Load8
+			set.Expr = &ir.GetLocalExpr{Var: Pv}
+			set.Value = &ir.GetLocalExpr{Var: Vv}
+			emit(set)
+		}
+	case "-":
+		{
+			get := &ir.GetLocalExpr{}
+			get.Var = Pv
+
+			ld := &ir.LoadExpr{}
+			ld.Size = ir.Load8
+			ld.Type = types.I32
+			ld.Signed = false
+			ld.Expr = get
+
+			inc := &ir.DyadicOp{}
+			inc.Left = ld
+			inc.Right = &ir.ConstExpr{Type: types.I32, Value: -1}
+			op := ops.Dyadic(types.I32, types.I32, ops.Add)
+			inc.Op = &op
+
+			set := &ir.StoreExpr{}
+			set.Type = types.I32
+			set.Size = ir.Load8
+			set.Expr = &ir.GetLocalExpr{Var: Pv}
+			set.Value = inc
+			emit(set)
+		}
+	case ".":
+		{
+			get := &ir.LoadExpr{}
+			get.Type = types.I32
+			get.Signed = false
+			get.Size = ir.Load8
+			get.Expr = &ir.GetLocalExpr{Var: Pv}
+
+			call := &ir.CallImportExpr{}
+			call.Var = Pr
+			call.Params = append(call.Params, get)
+
+			emit(call)
+		}
+	case ">":
+		cmp := &ir.DyadicOp{}
+		cmp.Left = &ir.GetLocalExpr{Var: Pv}
+		cmp.Right = &ir.ConstExpr{Type: types.I32, Value: 30000}
+		op := ops.Dyadic(types.I32, types.I32, ops.Eq)
+		cmp.Op = &op
+
+		inc := &ir.DyadicOp{}
+		inc.Left = &ir.GetLocalExpr{Var: Pv}
+		inc.Right = &ir.ConstExpr{Type: types.I32, Value: 1}
+		aop := ops.Dyadic(types.I32, types.I32, ops.Add)
+		inc.Op = &aop
+
+		set := &ir.SetLocalExpr{}
+		set.Var = Pv
+		set.Expr = inc
+
+		cond := &ir.If{}
+		cond.CondExpr = cmp
+		cond.Expr = append(cond.Expr, &ir.SetLocalExpr{Var: Pv, Expr: &ir.ConstExpr{Type: types.I32, Value: 0}})
+		cond.ElseExpr = append(cond.ElseExpr, set)
+		emit(cond)
+	case "<":
+		cmp := &ir.DyadicOp{}
+		cmp.Left = &ir.GetLocalExpr{Var: Pv}
+		cmp.Right = &ir.ConstExpr{Type: types.I32, Value: 0}
+		op := ops.Dyadic(types.I32, types.I32, ops.Eq)
+		cmp.Op = &op
+
+		inc := &ir.DyadicOp{}
+		inc.Left = &ir.GetLocalExpr{Var: Pv}
+		inc.Right = &ir.ConstExpr{Type: types.I32, Value: -1}
+		aop := ops.Dyadic(types.I32, types.I32, ops.Add)
+		inc.Op = &aop
+
+		set := &ir.SetLocalExpr{}
+		set.Var = Pv
+		set.Expr = inc
+
+		cond := &ir.If{}
+		cond.CondExpr = cmp
+		cond.Expr = append(cond.Expr, &ir.SetLocalExpr{Var: Pv, Expr: &ir.ConstExpr{Type: types.I32, Value: 30000}})
+		cond.ElseExpr = append(cond.ElseExpr, set)
+		emit(cond)
+
+	case "[":
+		loop := &ir.Loop{}
+		loop.Start.Name(fmt.Sprint("$start", strconv.Itoa(depth())))
+		loop.End.Name(fmt.Sprint("$end", strconv.Itoa(depth())))
+		emit(loop)
+
+		down(func(e ir.CodeExpr) {
+			loop.Expr = append(loop.Expr, e)
+		})
+
+		end := &ir.Br{}
+		end.Var = ir.ThisVar(loop.End.Name())
+
+		get := &ir.GetLocalExpr{}
+		get.Var = Pv
+
+		ld := &ir.LoadExpr{}
+		ld.Size = ir.Load8
+		ld.Type = types.I32
+		ld.Signed = false
+		ld.Expr = get
+
+		cmp := &ir.DyadicOp{}
+		cmp.Left = ld
+		cmp.Right = &ir.ConstExpr{Type: types.I32, Value: 0}
+		op := ops.Dyadic(types.I32, types.I32, ops.Eq)
+		cmp.Op = &op
+
+		cond := &ir.If{}
+		cond.CondExpr = cmp
+		cond.Expr = append(cond.Expr, end)
+		cond.ElseExpr = append(cond.ElseExpr, &ir.NopExpr{})
+		emit(cond)
+	case "]":
+		br := &ir.Br{}
+		br.Var = ir.ThisVar(fmt.Sprint("$start", strconv.Itoa(depth()-1)))
+		emit(br)
+		up()
+	case ",":
+		log.Println("no input in this implementation :(")
+	default:
+	}
+}
+
+func compile() {
+	Assert(!fn.IsNil(rd), 20)
+	module()
+	var err error
+	for err == nil {
+		var r rune
+		if r, _, err = rd.ReadRune(); err == nil {
+			if !unicode.IsSpace(r) {
+				do(string([]rune{r}))
+			}
+		} else if err == io.EOF {
+			//that's ok
+		} else {
+			log.Fatal(err)
+		}
+	}
+}
+
+func main() {
+	flag.Parse()
+	if i, err := os.Open(filename); err == nil {
+		rd = bufio.NewReader(i)
+		mod = &ir.Module{}
+		compile()
+		if o, err := os.Create(filepath.Join(filepath.Dir(filename), strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filepath.Base(filename)))+".wast")); err == nil {
+			buf := bytes.NewBuffer(nil)
+			if err := gen.NewWriter(buf, gen.Opts{PrettyPrint: true}).WriteExpr(mod); err == nil {
+				log.Println(buf.String())
+				io.Copy(o, buf)
+			} else {
+				log.Fatal(err)
+			}
+		} else {
+			log.Fatal(err)
+		}
+	} else {
+		log.Fatal(err)
+	}
+}

+ 4 - 0
demo/bf/test0.bf

@@ -0,0 +1,4 @@
+нужен wasm интерпретатор
+ ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++
+ .>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.
+ ------.--------.>+.>.

+ 8 - 7
ir/ast.go

@@ -50,7 +50,7 @@ type Variable struct {
 	iv *int
 }
 
-func ThisVariable(_x interface{}) (ret Variable) {
+func ThisVar(_x interface{}) (ret Variable) {
 	switch x := _x.(type) {
 	case string:
 		Assert(x[0] == '$', 20)
@@ -270,10 +270,6 @@ func (o *object) Type(t ...types.Type) types.Type {
 }
 
 func (o *object) Validate() error {
-	if o.name == "" {
-		return Error("empty object name")
-	}
-
 	if o.typ == "" {
 		return Error("empty object type")
 	}
@@ -282,7 +278,10 @@ func (o *object) Validate() error {
 }
 
 func (o *object) Children() (ret []interface{}) {
-	return append(ret, o.name, string(o.typ))
+	if o.name != "" {
+		ret = append(ret, o.name)
+	}
+	return append(ret, string(o.typ))
 }
 
 type Module struct {
@@ -321,7 +320,9 @@ func (m *Module) Children() (ret []interface{}) {
 		ret = append(ret, e)
 	}
 
-	ret = append(ret, m.Start)
+	if m.Start != nil {
+		ret = append(ret, m.Start)
+	}
 	return
 }
 

+ 25 - 4
ir/mem.go

@@ -2,6 +2,7 @@ package ir
 
 import (
 	"fmt"
+	"strconv"
 
 	"github.com/kpmy/tiss/ir/types"
 	"github.com/kpmy/ypk/fn"
@@ -75,7 +76,7 @@ func (l *LoadExpr) Validate() error {
 		return Error("invalid load size")
 	}
 
-	if l.Align != 1 && l.Align%2 == 1 {
+	if l.Align > 1 && l.Align%2 == 1 {
 		return Error("load align must be power of 2")
 	}
 
@@ -86,7 +87,17 @@ func (l *LoadExpr) Validate() error {
 }
 
 func (l *LoadExpr) Children() (ret []interface{}) {
-	return append(ret, l.Offset, l.Align, l.Expr)
+
+	if l.Offset > 0 {
+		ret = append(ret, fmt.Sprint("offset=", strconv.FormatUint(uint64(l.Offset), 32)))
+	}
+
+	if l.Align > 0 {
+		ret = append(ret, fmt.Sprint("align=", strconv.FormatUint(uint64(l.Align), 32)))
+	}
+
+	ret = append(ret, l.Expr)
+	return
 }
 
 func (l *LoadExpr) Eval() {}
@@ -113,7 +124,7 @@ func (s *StoreExpr) Validate() error {
 		return Error("invalid store size")
 	}
 
-	if s.Align != 1 && s.Align%2 == 1 {
+	if s.Align > 1 && s.Align%2 == 1 {
 		return Error("store align must be power of 2")
 	}
 
@@ -128,7 +139,17 @@ func (s *StoreExpr) Validate() error {
 }
 
 func (s *StoreExpr) Children() (ret []interface{}) {
-	return append(ret, s.Offset, s.Align, s.Expr, s.Value)
+
+	if s.Offset > 0 {
+		ret = append(ret, fmt.Sprint("offset=", strconv.FormatUint(uint64(s.Offset), 32)))
+	}
+
+	if s.Align > 0 {
+		ret = append(ret, fmt.Sprint("align=", strconv.FormatUint(uint64(s.Align), 32)))
+	}
+
+	ret = append(ret, s.Expr, s.Value)
+	return
 }
 
 func (l *StoreExpr) Eval() {}

+ 3 - 3
ps/impl/sexpr/sexpr.go

@@ -123,7 +123,7 @@ func (p *pr) list2obj(n []*sexp.Node) (ret interface{}) {
 			ret = t
 		} else if len(data) == 1 && data[0].IsScalar() {
 			t := &ir.TypeRef{}
-			t.Type = ir.ThisVariable(data[0].Value)
+			t.Type = ir.ThisVar(data[0].Value)
 			ret = t
 		}
 	case typ == "func":
@@ -156,7 +156,7 @@ func (p *pr) list2obj(n []*sexp.Node) (ret interface{}) {
 	case typ == "start":
 		s := &ir.StartExpr{}
 		Assert(len(data) > 0, 20)
-		s.Var = ir.ThisVariable(data[0].Value)
+		s.Var = ir.ThisVar(data[0].Value)
 
 		ret = s
 	case typ == "param":
@@ -201,7 +201,7 @@ func (p *pr) list2obj(n []*sexp.Node) (ret interface{}) {
 		c := &ir.CallExpr{}
 		start := 0
 		if len(data) > 0 && data[0].IsScalar() {
-			c.Var = ir.ThisVariable(data[0].Value)
+			c.Var = ir.ThisVar(data[0].Value)
 			start = 1
 		}
 		for i := start; i < len(data); i++ {

+ 3 - 4
tiss_test.go

@@ -38,16 +38,15 @@ func TestDump(t *testing.T) {
 		ret := &ir.ReturnExpr{}
 		ret.Expr = &ir.ConstExpr{Type: types.I64, Value: 0}
 		f0.Code = append(f0.Code, ret)
-
 		fn := &ir.FuncExpr{}
 		fn.Name("$start")
-		fn.Type = &ir.TypeRef{Type: ir.ThisVariable("$t0")}
+		fn.Type = &ir.TypeRef{Type: ir.ThisVar("$t0")}
 		call := &ir.CallExpr{}
-		call.Var = ir.ThisVariable("$fib")
+		call.Var = ir.ThisVar("$fib")
 		call.Params = []ir.CodeExpr{&ir.ConstExpr{Type: types.I64, Value: 0}}
 		fn.Code = append(fn.Code, call)
 		m.Func = append(m.Func, f0, fn)
-		m.Start = &ir.StartExpr{Var: ir.ThisVariable("$start")}
+		m.Start = &ir.StartExpr{Var: ir.ThisVar("$start")}
 
 		buf := bytes.NewBuffer(nil)
 		if err = gen.NewWriter(buf, gen.Opts{PrettyPrint: false}).WriteExpr(m); err == nil {