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.go108
-rw-r--r--pkg/lang/vm/code/builder_test.go31
-rw-r--r--pkg/lang/vm/code/errors.go32
-rw-r--r--pkg/lang/vm/code/marker.go23
-rw-r--r--pkg/lang/vm/text/compiler.go2
5 files changed, 145 insertions, 51 deletions
diff --git a/pkg/lang/vm/code/builder.go b/pkg/lang/vm/code/builder.go
index f027c49..adb2eed 100644
--- a/pkg/lang/vm/code/builder.go
+++ b/pkg/lang/vm/code/builder.go
@@ -2,14 +2,15 @@ package code
import (
"encoding/binary"
- "fmt"
"math"
)
type Builder struct {
- code []byte
- debugInfo DebugInfo
- relativePcRefs map[int][]int
+ code []byte
+ debugInfo DebugInfo
+
+ markers map[Marker]int
+ markerRefs map[int]Marker
currentLine int
lineStart int
@@ -17,11 +18,14 @@ type Builder struct {
func NewBuilder() Builder {
return Builder{
- code: make([]byte, 0, 64),
- debugInfo: NewDebugInfo("unknown file"),
- relativePcRefs: make(map[int][]int),
- lineStart: -1,
- currentLine: -1,
+ code: make([]byte, 0, 64),
+ debugInfo: NewDebugInfo("unknown file"),
+
+ markers: make(map[Marker]int, 0),
+ markerRefs: make(map[int]Marker, 0),
+
+ lineStart: -1,
+ currentLine: -1,
}
}
@@ -43,50 +47,45 @@ func (b *Builder) AppendString(s string) {
b.code = append(b.code, 0)
}
-func (b *Builder) AppendReferenceToPc(pc int64) {
- b.addPcRef(int(pc), b.Len())
- b.AppendInt(pc)
+const UnfilledMarkerReference = 0x11_5AB07A6ED_600D5
+
+func (b *Builder) AppendMarkerReference(marker Marker) {
+ b.markerRefs[b.Len()] = marker
+ b.AppendInt(UnfilledMarkerReference)
}
func (b *Builder) AppendRaw(raw Raw) {
b.code = append(b.code, raw...)
}
-func (b *Builder) AppendBuilder(other Builder) {
- code := other.code
- for pc, refsToPc := range other.relativePcRefs {
- newPc := b.Len() + pc
- for _, ref := range refsToPc {
- valueAtRef := binary.LittleEndian.Uint64(code[ref : ref+8])
- if int(valueAtRef) != pc {
- panic(fmt.Errorf("reference to pc in builder does not actually reference pc. (pc: %d, value at reference: %d)", pc, valueAtRef))
+func (b *Builder) AppendBuilder(other Builder) error {
+ for marker, pc := range other.markers {
+ if parentPc, ok := b.markers[marker]; ok {
+ return ErrBuilderAppendMarkerDefinitionClash{
+ Marker: marker,
+ ParentPc: parentPc,
+ ChildPc: pc,
}
-
- binary.LittleEndian.PutUint64(code[ref:], uint64(newPc))
- b.addPcRef(newPc, ref+b.Len())
}
- }
-
- b.code = append(b.code, code...)
- if other.debugInfo.pcToLine.Len() != 0 || other.currentLine != -1 || other.lineStart != -1 {
- panic("appending debug infos not implemented yet")
+ b.SetMarker(marker, pc)
}
-}
-func (b *Builder) AppendBuilderWithoutAdjustingReferences(other Builder) {
- code := other.code
- for pc, refsToPc := range other.relativePcRefs {
- for _, ref := range refsToPc {
- b.addPcRef(pc, ref+b.Len())
- }
+ for pc, marker := range other.markerRefs {
+ b.markerRefs[b.Len()+pc] = marker
}
- b.code = append(b.code, code...)
+ b.AppendRaw(other.code)
- if other.debugInfo.pcToLine.Len() != 0 || other.currentLine != -1 || other.lineStart != -1 {
- panic("appending debug infos not implemented yet")
- }
+ return nil
+}
+
+func (b *Builder) PutMarker(marker Marker) {
+ b.SetMarker(marker, b.Len())
+}
+
+func (b *Builder) SetMarker(marker Marker, pc int) {
+ b.markers[marker] = pc
}
func (b *Builder) StartLine(line int) {
@@ -127,18 +126,27 @@ func (b *Builder) Len() int {
return len(b.code)
}
-func (b *Builder) Build() Code {
- return New(b.code, b.debugInfo)
-}
+func (b *Builder) Build() (Code, error) {
+ for pc, marker := range b.markerRefs {
+ markerPc, ok := b.markers[marker]
+ if !ok {
+ return Code{}, ErrBuilderUnfulfilledMarker{
+ Marker: marker,
+ Pc: pc,
+ }
+ }
+
+ valueAtRefPc := binary.LittleEndian.Uint64(b.code[pc : pc+8])
+ if valueAtRefPc != UnfilledMarkerReference {
+ return Code{}, ErrBuilderOverwrittenMarkerReference{
+ Marker: marker,
+ Pc: pc,
+ UnexpectedValue: valueAtRefPc,
+ }
+ }
-func (b *Builder) addPcRef(pc int, at int) {
- refs, ok := b.relativePcRefs[pc]
- if ok {
- refs = append(refs, at)
- } else {
- refs = make([]int, 1)
- refs[0] = at
+ b.SetInt(pc, int64(markerPc))
}
- b.relativePcRefs[pc] = refs
+ return New(b.code, b.debugInfo), nil
}
diff --git a/pkg/lang/vm/code/builder_test.go b/pkg/lang/vm/code/builder_test.go
new file mode 100644
index 0000000..29f1974
--- /dev/null
+++ b/pkg/lang/vm/code/builder_test.go
@@ -0,0 +1,31 @@
+package code_test
+
+import (
+ "jinx/pkg/lang/vm/code"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMarker(t *testing.T) {
+ builder := code.NewBuilder()
+
+ builder.AppendInt(123)
+ builder.AppendMarkerReference("hello world")
+
+ builder.PutMarker("hello world")
+ builder.AppendInt(123)
+
+ result, err := builder.Build()
+ assert.NoError(t, err)
+
+ expectedBuilder := code.NewBuilder()
+ expectedBuilder.AppendInt(123)
+ expectedBuilder.AppendInt(16)
+ expectedBuilder.AppendInt(123)
+
+ expected, err := expectedBuilder.Build()
+ assert.NoError(t, err)
+
+ assert.Equal(t, expected.Code(), result.Code())
+}
diff --git a/pkg/lang/vm/code/errors.go b/pkg/lang/vm/code/errors.go
new file mode 100644
index 0000000..3261dd9
--- /dev/null
+++ b/pkg/lang/vm/code/errors.go
@@ -0,0 +1,32 @@
+package code
+
+import "fmt"
+
+type ErrBuilderAppendMarkerDefinitionClash struct {
+ Marker Marker
+ ParentPc int
+ ChildPc int
+}
+
+func (e ErrBuilderAppendMarkerDefinitionClash) Error() string {
+ return fmt.Sprintf("parent and child builder definitions for marker '%v' clash (parent: %d, child: %d)", e.Marker, e.ParentPc, e.ChildPc)
+}
+
+type ErrBuilderUnfulfilledMarker struct {
+ Marker Marker
+ Pc int
+}
+
+func (e ErrBuilderUnfulfilledMarker) Error() string {
+ return fmt.Sprintf("encountered unfulfilled marker '%v' at pc %d", e.Marker, e.Pc)
+}
+
+type ErrBuilderOverwrittenMarkerReference struct {
+ Marker Marker
+ Pc int
+ UnexpectedValue uint64
+}
+
+func (e ErrBuilderOverwrittenMarkerReference) Error() string {
+ return fmt.Sprintf("marker reference for marker '%v' at pc %d was overwritten (value: %x)", e.Marker, e.Pc, e.UnexpectedValue)
+}
diff --git a/pkg/lang/vm/code/marker.go b/pkg/lang/vm/code/marker.go
new file mode 100644
index 0000000..18b4157
--- /dev/null
+++ b/pkg/lang/vm/code/marker.go
@@ -0,0 +1,23 @@
+package code
+
+import "fmt"
+
+type Marker string
+
+func (m Marker) SubUnit(name string) Marker {
+ return Marker(fmt.Sprintf("%v:%s", m, name))
+}
+
+func (m Marker) SubMarker(format string, args ...any) Marker {
+ part := fmt.Sprintf(format, args...)
+
+ return Marker(fmt.Sprintf("%v_%s", m, part))
+}
+
+func (m Marker) String() string {
+ return string(m)
+}
+
+func (m Marker) IsEmpty() bool {
+ return string(m) == ""
+}
diff --git a/pkg/lang/vm/text/compiler.go b/pkg/lang/vm/text/compiler.go
index 21d8dae..d8b53fd 100644
--- a/pkg/lang/vm/text/compiler.go
+++ b/pkg/lang/vm/text/compiler.go
@@ -56,7 +56,7 @@ func (cpl *Compiler) Compile() (code.Code, error) {
return code.Code{}, err
}
- return builder.Build(), nil
+ return builder.Build()
}
func (cpl *Compiler) compileLine() (code.Raw, error) {