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(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 := 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", 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()), } }