diff options
| author | Mel <einebeere@gmail.com> | 2022-07-11 00:35:59 +0200 |
|---|---|---|
| committer | Mel <einebeere@gmail.com> | 2022-07-11 00:35:59 +0200 |
| commit | 1b6ef1e43e1ec1107ce29a6438b399352d09fbc2 (patch) | |
| tree | 20b8cc80564f0c76c9e68e9fb6df6163b06b24f2 /pkg/lang/vm | |
| parent | b16b70fd40ffc72ff861afe0517cba0e37ba1145 (diff) | |
| download | jinx-1b6ef1e43e1ec1107ce29a6438b399352d09fbc2.tar.zst jinx-1b6ef1e43e1ec1107ce29a6438b399352d09fbc2.zip | |
Rebuild compiler and code builder with markers
Diffstat (limited to 'pkg/lang/vm')
| -rw-r--r-- | pkg/lang/vm/code/builder.go | 108 | ||||
| -rw-r--r-- | pkg/lang/vm/code/builder_test.go | 31 | ||||
| -rw-r--r-- | pkg/lang/vm/code/errors.go | 32 | ||||
| -rw-r--r-- | pkg/lang/vm/code/marker.go | 23 | ||||
| -rw-r--r-- | pkg/lang/vm/text/compiler.go | 2 |
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) { |
