about summary refs log tree commit diff
path: root/pkg/lang/vm
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/lang/vm')
-rw-r--r--pkg/lang/vm/code/builder.go60
-rw-r--r--pkg/lang/vm/code/code.go8
-rw-r--r--pkg/lang/vm/text/compiler.go55
-rw-r--r--pkg/lang/vm/text/compiler_test.go166
4 files changed, 173 insertions, 116 deletions
diff --git a/pkg/lang/vm/code/builder.go b/pkg/lang/vm/code/builder.go
new file mode 100644
index 0000000..3aa6b6b
--- /dev/null
+++ b/pkg/lang/vm/code/builder.go
@@ -0,0 +1,60 @@
+package code
+
+import (
+	"encoding/binary"
+	"math"
+)
+
+type Builder struct {
+	code      []byte
+	debugInfo DebugInfo
+}
+
+func NewBuilder() Builder {
+	return Builder{
+		code:      make([]byte, 0, 64),
+		debugInfo: NewDebugInfo("unknown file"),
+	}
+}
+
+func (b *Builder) AppendOp(op Op) {
+	b.code = append(b.code, byte(op))
+}
+
+func (b *Builder) AppendInt(x int64) {
+	b.code = append(b.code, make([]byte, 8)...)
+	binary.LittleEndian.PutUint64(b.code[len(b.code)-8:], uint64(x))
+}
+
+func (b *Builder) AppendFloat(x float64) {
+	b.AppendInt(int64(math.Float64bits(x)))
+}
+
+func (b *Builder) AppendString(s string) {
+	b.code = append(b.code, []byte(s)...)
+	b.code = append(b.code, 0)
+}
+
+func (b *Builder) AppendRaw(raw Raw) {
+	b.code = append(b.code, raw...)
+}
+
+func (b *Builder) AppendLine(line int) {
+	b.debugInfo.AppendLine(len(b.code)-1, line)
+}
+
+func (b *Builder) SetInt(at int, x int64) {
+	binary.LittleEndian.PutUint64(b.code[at:], uint64(x))
+}
+
+func (b *Builder) Code() Raw {
+	return Raw(b.code)
+}
+
+func (b *Builder) Len() int {
+	return len(b.code)
+}
+
+func (b *Builder) Build() Code {
+	return New(b.code, b.debugInfo)
+}
diff --git a/pkg/lang/vm/code/code.go b/pkg/lang/vm/code/code.go
index 38bbcf5..94fd612 100644
--- a/pkg/lang/vm/code/code.go
+++ b/pkg/lang/vm/code/code.go
@@ -7,12 +7,14 @@ import (
 	"strings"
 )
 
+type Raw []byte
+
 type Code struct {
-	code      []byte
+	code      Raw
 	debugInfo DebugInfo
 }
 
-func New(code []byte, info DebugInfo) Code {
+func New(code Raw, info DebugInfo) Code {
 	return Code{
 		code:      code,
 		debugInfo: info,
@@ -23,7 +25,7 @@ func (c *Code) Len() int {
 	return len(c.code)
 }
 
-func (c *Code) Code() []byte {
+func (c *Code) Code() Raw {
 	return c.code
 }
 
diff --git a/pkg/lang/vm/text/compiler.go b/pkg/lang/vm/text/compiler.go
index 1480171..68889eb 100644
--- a/pkg/lang/vm/text/compiler.go
+++ b/pkg/lang/vm/text/compiler.go
@@ -1,11 +1,9 @@
 package text
 
 import (
-	"encoding/binary"
 	"io"
 	"jinx/pkg/lang/vm/code"
 	"jinx/pkg/libs/source"
-	"math"
 	"strconv"
 	"strings"
 	"unicode"
@@ -30,8 +28,7 @@ func NewCompiler(src io.Reader) *Compiler {
 }
 
 func (cpl *Compiler) Compile() (code.Code, error) {
-	res := []byte{}
-	info := code.NewDebugInfo("unknown file")
+	builder := code.NewBuilder()
 
 	for {
 		_, eof, err := cpl.src.Peek()
@@ -50,22 +47,22 @@ func (cpl *Compiler) Compile() (code.Code, error) {
 
 		cpl.codePos += len(line)
 
-		if line != nil {
-			info.AppendLine(cpl.codePos-1, cpl.src.Loc().Row-1)
-		}
+		builder.AppendRaw(line)
 
-		res = append(res, line...)
+		if len(line) > 0 {
+			builder.AppendLine(cpl.src.Loc().Row - 1)
+		}
 	}
 
-	if err := cpl.linkLabels(res); err != nil {
+	if err := cpl.linkLabels(&builder); err != nil {
 		return code.Code{}, err
 	}
 
-	return code.New(res, info), nil
+	return builder.Build(), nil
 }
 
-func (cpl *Compiler) compileLine() ([]byte, error) {
-	res := []byte{}
+func (cpl *Compiler) compileLine() (code.Raw, error) {
+	builder := code.NewBuilder()
 
 	start, value, err := cpl.splitLine()
 	if err != nil || start == "" {
@@ -93,7 +90,7 @@ func (cpl *Compiler) compileLine() ([]byte, error) {
 		return nil, err
 	}
 
-	res = append(res, byte(op))
+	builder.AppendOp(op)
 
 	// Find the value, of which there is at most one.
 	if value != "" {
@@ -102,10 +99,10 @@ func (cpl *Compiler) compileLine() ([]byte, error) {
 			return nil, err
 		}
 
-		res = append(res, val...)
+		builder.AppendRaw(val)
 	}
 
-	return res, nil
+	return builder.Code(), nil
 }
 
 func (cpl *Compiler) compileOp(str string) (code.Op, error) {
@@ -117,8 +114,8 @@ func (cpl *Compiler) compileOp(str string) (code.Op, error) {
 	return op, nil
 }
 
-func (cpl *Compiler) compileValue(str string) ([]byte, error) {
-	res := make([]byte, 8)
+func (cpl *Compiler) compileValue(str string) (code.Raw, error) {
+	builder := code.NewBuilder()
 
 	// Save label reference.
 	if str[0] == '@' {
@@ -127,38 +124,38 @@ func (cpl *Compiler) compileValue(str string) ([]byte, error) {
 			label: label,
 			at:    cpl.codePos + 1, // +1 to skip the opcode.
 		})
-		return res, nil
+		builder.AppendInt(0)
+		return builder.Code(), nil
 	}
 
 	if unicode.IsDigit(rune(str[0])) || str[0] == '-' {
 		if strings.Contains(str, ".") {
 			val, err := strconv.ParseFloat(str, 64)
 			if err != nil {
-				return res, err
+				return nil, err
 			}
 
-			binary.LittleEndian.PutUint64(res, math.Float64bits(val))
+			builder.AppendFloat(val)
 		} else {
 			val, err := strconv.ParseInt(str, 10, 64)
 			if err != nil {
-				return res, err
+				return nil, err
 			}
 
-			binary.LittleEndian.PutUint64(res, uint64(val))
+			builder.AppendInt(val)
 		}
 
-		return res, nil
+		return builder.Code(), nil
 	}
 
 	if str[0] == '"' {
 		str = strings.Trim(str, "\"")
-		res = []byte(str)
-		res = append(res, 0)
+		builder.AppendString(str)
 
-		return res, nil
+		return builder.Code(), nil
 	}
 
-	return res, ErrInvalidValue{str}
+	return nil, ErrInvalidValue{str}
 }
 
 func (cpl *Compiler) splitLine() (string, string, error) {
@@ -195,14 +192,14 @@ type labelReference struct {
 	at    int
 }
 
-func (cpl *Compiler) linkLabels(code []byte) error {
+func (cpl *Compiler) linkLabels(builder *code.Builder) error {
 	for _, ref := range cpl.labelReferences {
 		pos, ok := cpl.labelPositions[ref.label]
 		if !ok {
 			return ErrUnkonwnLabel{ref.label}
 		}
 
-		binary.LittleEndian.PutUint64(code[ref.at:], uint64(pos))
+		builder.SetInt(ref.at, int64(pos))
 	}
 	return nil
 }
diff --git a/pkg/lang/vm/text/compiler_test.go b/pkg/lang/vm/text/compiler_test.go
index 56b886d..c9e3fa1 100644
--- a/pkg/lang/vm/text/compiler_test.go
+++ b/pkg/lang/vm/text/compiler_test.go
@@ -1,10 +1,8 @@
 package text_test
 
 import (
-	"encoding/binary"
 	"jinx/pkg/lang/vm/code"
 	"jinx/pkg/lang/vm/text"
-	"math"
 	"strings"
 	"testing"
 
@@ -22,13 +20,13 @@ func TestSimple(t *testing.T) {
 	res, err := c.Compile()
 	require.NoError(t, err)
 
-	parts := [][]byte{
-		opBin(code.OpAdd),
-		opBin(code.OpSub),
-		opBin(code.OpRet),
-	}
+	exp := code.NewBuilder()
 
-	require.Equal(t, joinSlices(parts), res.Code())
+	exp.AppendOp(code.OpAdd)
+	exp.AppendOp(code.OpSub)
+	exp.AppendOp(code.OpRet)
+
+	require.Equal(t, exp.Code(), res.Code())
 }
 
 func TestInt(t *testing.T) {
@@ -43,16 +41,16 @@ func TestInt(t *testing.T) {
 	res, err := c.Compile()
 	require.NoError(t, err)
 
-	parts := [][]byte{
-		opBin(code.OpPushInt),
-		uintBin(1),
-		opBin(code.OpPushInt),
-		uintBin(2),
-		opBin(code.OpAdd),
-		opBin(code.OpRet),
-	}
+	exp := code.NewBuilder()
+
+	exp.AppendOp(code.OpPushInt)
+	exp.AppendInt(1)
+	exp.AppendOp(code.OpPushInt)
+	exp.AppendInt(2)
+	exp.AppendOp(code.OpAdd)
+	exp.AppendOp(code.OpRet)
 
-	require.Equal(t, joinSlices(parts), res.Code())
+	require.Equal(t, exp.Code(), res.Code())
 }
 
 func TestFloat(t *testing.T) {
@@ -65,14 +63,14 @@ func TestFloat(t *testing.T) {
 	res, err := c.Compile()
 	require.NoError(t, err)
 
-	parts := [][]byte{
-		opBin(code.OpPushFloat),
-		floatBin(3.1415),
-		opBin(code.OpPushFloat),
-		floatBin(-2.71828),
-	}
+	exp := code.NewBuilder()
+
+	exp.AppendOp(code.OpPushFloat)
+	exp.AppendFloat(3.1415)
+	exp.AppendOp(code.OpPushFloat)
+	exp.AppendFloat(-2.71828)
 
-	require.Equal(t, joinSlices(parts), res.Code())
+	require.Equal(t, exp.Code(), res.Code())
 }
 
 func TestString(t *testing.T) {
@@ -86,15 +84,15 @@ func TestString(t *testing.T) {
 	res, err := c.Compile()
 	require.NoError(t, err)
 
-	parts := [][]byte{
-		opBin(code.OpPushString),
-		stringBin("Hello, "),
-		opBin(code.OpPushString),
-		stringBin("world!"),
-		opBin(code.OpAdd),
-	}
+	exp := code.NewBuilder()
 
-	require.Equal(t, joinSlices(parts), res.Code())
+	exp.AppendOp(code.OpPushString)
+	exp.AppendString("Hello, ")
+	exp.AppendOp(code.OpPushString)
+	exp.AppendString("world!")
+	exp.AppendOp(code.OpAdd)
+
+	require.Equal(t, exp.Code(), res.Code())
 }
 
 func TestLabels(t *testing.T) {
@@ -114,19 +112,19 @@ func TestLabels(t *testing.T) {
 	res, err := c.Compile()
 	require.NoError(t, err)
 
-	parts := [][]byte{
-		opBin(code.OpNop),
-		opBin(code.OpNop),
-		opBin(code.OpNop),
-		opBin(code.OpJmp),
-		uintBin(0),
-		opBin(code.OpJmp),
-		uintBin(1),
-		opBin(code.OpJmp),
-		uintBin(2),
-	}
-
-	require.Equal(t, joinSlices(parts), res.Code())
+	exp := code.NewBuilder()
+
+	exp.AppendOp(code.OpNop)
+	exp.AppendOp(code.OpNop)
+	exp.AppendOp(code.OpNop)
+	exp.AppendOp(code.OpJmp)
+	exp.AppendInt(0)
+	exp.AppendOp(code.OpJmp)
+	exp.AppendInt(1)
+	exp.AppendOp(code.OpJmp)
+	exp.AppendInt(2)
+
+	require.Equal(t, exp.Code(), res.Code())
 }
 
 func TestDebugInfo(t *testing.T) {
@@ -146,46 +144,46 @@ func TestDebugInfo(t *testing.T) {
 	res, err := c.Compile()
 	require.NoError(t, err)
 
-	expected := code.NewDebugInfo("unknown file")
-
-	expected.AppendLine(8, 1)
-	expected.AppendLine(17, 2)
-	expected.AppendLine(18, 4)
-	expected.AppendLine(19, 5)
-	expected.AppendLine(20, 8)
-	expected.AppendLine(21, 9)
+	exp := code.NewDebugInfo("unknown file")
 
-	require.Equal(t, expected, *res.Debug())
-}
+	exp.AppendLine(8, 1)
+	exp.AppendLine(17, 2)
+	exp.AppendLine(18, 4)
+	exp.AppendLine(19, 5)
+	exp.AppendLine(20, 8)
+	exp.AppendLine(21, 9)
 
-func opBin(op code.Op) []byte {
-	return []byte{byte(op)}
+	require.Equal(t, exp, *res.Debug())
 }
 
-func uintBin(x uint64) []byte {
-	res := make([]byte, 8)
-	binary.LittleEndian.PutUint64(res, x)
-	return res
-}
-
-func floatBin(x float64) []byte {
-	res := make([]byte, 8)
-	binary.LittleEndian.PutUint64(res, math.Float64bits(x))
-	return res
-}
-
-func stringBin(x string) []byte {
-	res := []byte(x)
-	res = append(res, 0)
-	return res
-}
-
-func joinSlices[T any](slices [][]T) []T {
-	res := []T{}
-
-	for _, slice := range slices {
-		res = append(res, slice...)
-	}
-
-	return res
-}
+// func opBin(op code.Op) []byte {
+// 	return []byte{byte(op)}
+// }
+
+// func uintBin(x uint64) []byte {
+// 	res := make([]byte, 8)
+// 	binary.LittleEndian.PutUint64(res, x)
+// 	return res
+// }
+
+// func floatBin(x float64) []byte {
+// 	res := make([]byte, 8)
+// 	binary.LittleEndian.PutUint64(res, math.Float64bits(x))
+// 	return res
+// }
+
+// func stringBin(x string) []byte {
+// 	res := []byte(x)
+// 	res = append(res, 0)
+// 	return res
+// }
+
+// func joinSlices[T any](slices [][]T) []T {
+// 	res := []T{}
+
+// 	for _, slice := range slices {
+// 		res = append(res, slice...)
+// 	}
+
+// 	return res
+// }