186 lines
4.1 KiB
Go
186 lines
4.1 KiB
Go
package compiler
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"monkey/internal/code"
|
|
"monkey/internal/object"
|
|
"strings"
|
|
)
|
|
|
|
func indent(text, indent string) string {
|
|
if text[len(text)-1:] == "\n" {
|
|
result := ""
|
|
for _, j := range strings.Split(text[:len(text)-1], "\n") {
|
|
result += indent + j + "\n"
|
|
}
|
|
return result
|
|
}
|
|
result := ""
|
|
for _, j := range strings.Split(strings.TrimRight(text, "\n"), "\n") {
|
|
result += indent + 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) WriteBytes(b []byte) error {
|
|
if err := e.WriteValue(len(b)); err != nil {
|
|
return err
|
|
}
|
|
if _, err := e.Buffer.Write(b); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *encoder) WriteString(s string) error {
|
|
if err := e.WriteValue(len(s)); err != nil {
|
|
return err
|
|
}
|
|
if _, err := e.Buffer.WriteString(s); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (e *encoder) WriteValue(data any) error {
|
|
switch val := data.(type) {
|
|
case int:
|
|
return binary.Write(&e.Buffer, binary.BigEndian, int64(val))
|
|
case object.Type:
|
|
return e.WriteValue(int(val))
|
|
default:
|
|
return binary.Write(&e.Buffer, binary.BigEndian, data)
|
|
}
|
|
}
|
|
|
|
func (e *encoder) WriteObjects(objects ...object.Object) (err error) {
|
|
err = errors.Join(err, e.WriteValue(len(objects)))
|
|
for _, obj := range objects {
|
|
err = errors.Join(err, e.WriteValue(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.WriteString(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.WriteBytes(o.Instructions))
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (b Bytecode) Encode() (data []byte, err error) {
|
|
var e encoder
|
|
|
|
err = errors.Join(err, e.WriteBytes(b.Instructions))
|
|
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() int {
|
|
return int(d.Int64())
|
|
}
|
|
|
|
func (d *decoder) Uint64() (i uint64) {
|
|
i = binary.BigEndian.Uint64(d.b[d.pos:])
|
|
d.pos += 8
|
|
return
|
|
}
|
|
|
|
func (d *decoder) Int64() int64 {
|
|
return int64(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 := object.Type(d.Int()); t {
|
|
case object.NullType:
|
|
o = append(o, object.Null{})
|
|
case object.BooleanType:
|
|
o = append(o, object.Boolean{Value: d.Byte() == 1})
|
|
case object.IntegerType:
|
|
o = append(o, object.Integer{Value: d.Int64()})
|
|
case object.StringType:
|
|
o = append(o, object.String{Value: d.String(d.Int())})
|
|
case object.CFunctionType:
|
|
// 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 %d (%s)", t, 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()),
|
|
}
|
|
}
|