aboutsummaryrefslogtreecommitdiff
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.go152
4 files changed, 166 insertions, 109 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),
- }
+ 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, joinSlices(parts), res.Code())
+ 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")
+ exp := 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.AppendLine(8, 1)
+ exp.AppendLine(17, 2)
+ exp.AppendLine(18, 4)
+ exp.AppendLine(19, 5)
+ exp.AppendLine(20, 8)
+ exp.AppendLine(21, 9)
- require.Equal(t, expected, *res.Debug())
+ require.Equal(t, exp, *res.Debug())
}
-func opBin(op code.Op) []byte {
- return []byte{byte(op)}
-}
+// 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 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 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 stringBin(x string) []byte {
+// res := []byte(x)
+// res = append(res, 0)
+// return res
+// }
-func joinSlices[T any](slices [][]T) []T {
- res := []T{}
+// func joinSlices[T any](slices [][]T) []T {
+// res := []T{}
- for _, slice := range slices {
- res = append(res, slice...)
- }
+// for _, slice := range slices {
+// res = append(res, slice...)
+// }
- return res
-}
+// return res
+// }