Files
monkey/internal/compiler/bytecode.go
Chuck Smith f65d7bfb1c
Some checks failed
Build / build (push) Failing after 5m28s
Publish Image / publish (push) Failing after 25s
Test / build (push) Failing after 5m23s
small changes
2024-03-29 08:18:24 -04:00

180 lines
4.1 KiB
Go

package compiler
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"math"
"monkey/internal/code"
"monkey/internal/object"
"strings"
)
func Indent(text, ident string) string {
if text[len(text)-1:] == "\n" {
result := ""
for _, j := range strings.Split(text[:len(text)-1], "\n") {
result += ident + j + "\n"
}
return result
}
result := ""
for _, j := range strings.Split(strings.TrimRight(text, "\n"), "\n") {
result += ident + j + "\n"
}
return result[:len(result)-1]
}
type Bytecode struct {
Instructions code.Instructions
Constants []object.Object
}
func (b *Bytecode) String() string {
var s strings.Builder
s.WriteString("Constants:\n")
for i, c := range b.Constants {
s.WriteString(fmt.Sprintf("%02d %s\n", i, c))
if cf, ok := c.(*object.CompiledFunction); ok {
s.WriteString(fmt.Sprintf(" Instructions:\n%s\n", Indent(cf.Instructions.String(), " ")))
}
}
s.WriteString(fmt.Sprintf("Instructions:\n%s\n", b.Instructions))
return s.String()
}
type encoder struct {
bytes.Buffer
}
func (e *encoder) Write(b []byte) (err error) {
_, err = e.Buffer.Write(b)
return
}
func (e *encoder) WriteString(s string) (err error) {
_, err = e.Buffer.WriteString(s)
return
}
func (e *encoder) WriteValue(data any) error {
return binary.Write(&e.Buffer, binary.BigEndian, data)
}
func (e *encoder) WriteObjects(objs ...object.Object) (err error) {
for _, obj := range objs {
err = errors.Join(err, e.WriteValue(len(string(obj.Type()))))
err = errors.Join(err, e.WriteString(string(obj.Type())))
switch o := obj.(type) {
case *object.Null:
break
case *object.Boolean:
err = errors.Join(err, e.WriteValue(o.Value))
case *object.Integer:
err = errors.Join(err, e.WriteValue(o.Value))
case *object.String:
err = errors.Join(err, e.WriteValue(len(o.Value)))
err = errors.Join(err, e.WriteValue(o.Value))
case *object.CompiledFunction:
err = errors.Join(err, e.WriteValue(o.NumParameters))
err = errors.Join(err, e.WriteValue(o.NumLocals))
err = errors.Join(err, e.WriteValue(len(o.Instructions)))
err = errors.Join(err, e.Write(o.Instructions))
}
}
return
}
func (b Bytecode) Encode() (data []byte, err error) {
var e encoder
err = errors.Join(err, e.WriteValue(len(b.Instructions)))
err = errors.Join(err, e.Write(b.Instructions))
err = errors.Join(err, e.WriteValue(len(b.Constants)))
err = errors.Join(err, e.WriteObjects(b.Constants...))
return e.Bytes(), err
}
type decoder struct {
pos int
b []byte
}
func (d *decoder) Byte() (b byte) {
b = d.b[d.pos]
d.pos++
return
}
func (d *decoder) Int() (i int) {
i = int(binary.BigEndian.Uint32(d.b[d.pos:]))
d.pos += 4
return
}
func (d *decoder) Uint64() (i uint64) {
i = binary.BigEndian.Uint64(d.b[d.pos:])
d.pos += 8
return
}
func (d *decoder) Int64() (i int64) {
return int64(d.Uint64())
}
func (d *decoder) Float64() (f float64) {
return math.Float64frombits(d.Uint64())
}
func (d *decoder) Bytes(len int) (b []byte) {
b = d.b[d.pos : d.pos+len]
d.pos += len
return
}
func (d *decoder) String(len int) (s string) {
s = string(d.b[d.pos : d.pos+len])
d.pos += len
return
}
func (d *decoder) Objects(len int) (o []object.Object) {
for i := 0; i < len; i++ {
switch t := d.String(d.Int()); t {
case object.NULL_OBJ:
o = append(o, &object.Null{})
case object.BOOLEAN_OBJ:
o = append(o, &object.Boolean{Value: d.Byte() == 1})
case object.INTEGER_OBJ:
o = append(o, &object.Integer{Value: d.Int64()})
case object.STRING_OBJ:
o = append(o, &object.String{Value: d.String(d.Int())})
case object.COMPILED_FUNCTION_OBJ:
// The order of the fields has to reflect the data layout in the encoded bytecode.
o = append(o, &object.CompiledFunction{
NumParameters: d.Int(),
NumLocals: d.Int(),
Instructions: d.Bytes(d.Int()),
})
default:
panic(fmt.Sprintf("decoder: unsupported decoding for type %s", t))
}
}
return
}
func Decode(b []byte) *Bytecode {
var d = decoder{b: b}
// The order of the fields has to reflect the data layout in the encoded bytecode.
return &Bytecode{
Instructions: d.Bytes(d.Int()),
Constants: d.Objects(d.Int()),
}
}