Refactor VM and operators
Some checks failed
Build / build (push) Failing after 5m54s
Publish Image / publish (push) Failing after 44s
Test / build (push) Failing after 5m21s

This commit is contained in:
Chuck Smith
2024-03-29 17:59:34 -04:00
parent 7435a993d9
commit 3b6df3e813
11 changed files with 979 additions and 639 deletions

View File

@@ -2,6 +2,7 @@ package object
import (
"bytes"
"fmt"
"strings"
)
@@ -18,30 +19,30 @@ func (ao *Array) Bool() bool {
return len(ao.Elements) > 0
}
func (a *Array) PopLeft() Object {
if len(a.Elements) > 0 {
e := a.Elements[0]
a.Elements = a.Elements[1:]
func (ao *Array) PopLeft() Object {
if len(ao.Elements) > 0 {
e := ao.Elements[0]
ao.Elements = ao.Elements[1:]
return e
}
return &Null{}
}
func (a *Array) PopRight() Object {
if len(a.Elements) > 0 {
e := a.Elements[(len(a.Elements) - 1)]
a.Elements = a.Elements[:(len(a.Elements) - 1)]
func (ao *Array) PopRight() Object {
if len(ao.Elements) > 0 {
e := ao.Elements[(len(ao.Elements) - 1)]
ao.Elements = ao.Elements[:(len(ao.Elements) - 1)]
return e
}
return &Null{}
}
func (a *Array) Prepend(obj Object) {
a.Elements = append([]Object{obj}, a.Elements...)
func (ao *Array) Prepend(obj Object) {
ao.Elements = append([]Object{obj}, ao.Elements...)
}
func (a *Array) Append(obj Object) {
a.Elements = append(a.Elements, obj)
func (ao *Array) Append(obj Object) {
ao.Elements = append(ao.Elements, obj)
}
func (ao *Array) Inspect() string {
@@ -69,6 +70,63 @@ func (ao *Array) Less(i, j int) bool {
return false
}
func (ao *Array) Add(other Object) (Object, error) {
if other.Type() != ao.Type() {
return nil, NewBinaryOpError(ao, other, "+")
}
var elements []Object
elements = append(elements, ao.Elements...)
elements = append(elements, other.(*Array).Elements...)
return &Array{Elements: elements}, nil
}
func (ao *Array) Mul(other Object) (Object, error) {
if other.Type() != IntegerType {
return nil, NewBinaryOpError(ao, other, "*")
}
var elements []Object
N := int(other.(*Integer).Value)
for i := 0; i < N; i++ {
elements = append(elements, ao.Elements...)
}
return &Array{Elements: elements}, nil
}
func (ao *Array) Get(index Object) (Object, error) {
if !AssertTypes(index, IntegerType) {
return nil, fmt.Errorf("invalid type for array index, expected Integer got %s", index.Type())
}
i := index.(*Integer).Value
N := int64(len(ao.Elements))
if i < 0 || i >= N {
return &Null{}, nil
}
return ao.Elements[i], nil
}
func (ao *Array) Set(index, other Object) error {
if !AssertTypes(index, IntegerType) {
return fmt.Errorf("invalid type for array index, expected Integer got %s", index.Type())
}
i := index.(*Integer).Value
N := int64(len(ao.Elements))
if i < 0 || i >= N {
return fmt.Errorf("index out of bounds %d with array length %d", i, N)
}
ao.Elements[i] = other
return nil
}
func (ao *Array) Compare(other Object) int {
if obj, ok := other.(*Array); ok {
if len(ao.Elements) != len(obj.Elements) {

View File

@@ -35,6 +35,24 @@ func (b *Boolean) Int() int {
return 0
}
func (b *Boolean) LogicalAnd(other Object) (Object, error) {
if !AssertTypes(other, BooleanType, IntegerType) {
return nil, NewBinaryOpError(b, other, "&&")
}
return &Boolean{b.Value && other.Bool()}, nil
}
func (b *Boolean) LogicalOr(other Object) (Object, error) {
if !AssertTypes(other, BooleanType, IntegerType) {
return nil, NewBinaryOpError(b, other, "||")
}
return &Boolean{b.Value || other.Bool()}, nil
}
func (b *Boolean) LogicalNot() Object {
return &Boolean{!b.Value}
}
func (b *Boolean) Compare(other Object) int {
if obj, ok := other.(*Boolean); ok {
return b.Int() - obj.Int()

View File

@@ -1,5 +1,7 @@
package object
// error encountered. This object is tracked through the evaluator and when
// encountered stops evaluation of the program or body of a function.
type Error struct {
Message string
}

20
internal/object/errors.go Normal file
View File

@@ -0,0 +1,20 @@
package object
import (
"fmt"
)
// BinaryOpError is an binary operation error usually resulting from mismatched types
type BinaryOpError struct {
left, right Object
op string
}
func (e BinaryOpError) Error() string {
return fmt.Sprintf("unsupported types for binary operation: %s %s %s", e.left.Type(), e.op, e.right.Type())
}
// NewBinaryOpError returns a new BinaryOpError
func NewBinaryOpError(left, right Object, op string) BinaryOpError {
return BinaryOpError{left, right, op}
}

View File

@@ -75,6 +75,46 @@ func (h *Hash) String() string {
return h.Inspect()
}
func (h *Hash) Add(other Object) (Object, error) {
if other.Type() != h.Type() {
return nil, NewBinaryOpError(h, other, "+")
}
pairs := make(map[HashKey]HashPair)
for k, v := range h.Pairs {
pairs[k] = v
}
for k, v := range other.(*Hash).Pairs {
pairs[k] = v
}
return &Hash{Pairs: pairs}, nil
}
func (h *Hash) Get(index Object) (Object, error) {
key, ok := index.(Hashable)
if !ok {
return nil, fmt.Errorf("invalid hash key %s", index.Type())
}
pair, found := h.Pairs[key.HashKey()]
if !found {
return &Null{}, nil
}
return pair.Value, nil
}
func (h *Hash) Set(index, other Object) error {
key, ok := index.(Hashable)
if !ok {
return fmt.Errorf("invalid hash key %s", index.Type())
}
h.Pairs[key.HashKey()] = HashPair{Key: index, Value: other}
return nil
}
func (h *Hash) Compare(other Object) int {
if obj, ok := other.(*Hash); ok {
if len(h.Pairs) != len(obj.Pairs) {

View File

@@ -26,6 +26,94 @@ func (i *Integer) String() string {
return i.Inspect()
}
func (i *Integer) Add(other Object) (Object, error) {
if !AssertTypes(other, IntegerType) {
return nil, NewBinaryOpError(i, other, "+")
}
return &Integer{i.Value + other.(*Integer).Value}, nil
}
func (i *Integer) Sub(other Object) (Object, error) {
if !AssertTypes(other, IntegerType) {
return nil, NewBinaryOpError(i, other, "-")
}
return &Integer{i.Value - other.(*Integer).Value}, nil
}
func (i *Integer) Mul(other Object) (Object, error) {
switch other.Type() {
case IntegerType:
return &Integer{i.Value * other.(*Integer).Value}, nil
case StringType:
return other.(Mul).Mul(i)
case ArrayType:
return other.(Mul).Mul(i)
default:
return nil, NewBinaryOpError(i, other, "*")
}
}
func (i *Integer) Div(other Object) (Object, error) {
if !AssertTypes(other, IntegerType) {
return nil, NewBinaryOpError(i, other, "/")
}
return &Integer{i.Value / other.(*Integer).Value}, nil
}
func (i *Integer) Mod(other Object) (Object, error) {
if !AssertTypes(other, IntegerType) {
return nil, NewBinaryOpError(i, other, "%")
}
return &Integer{i.Value % other.(*Integer).Value}, nil
}
func (i *Integer) BitwiseOr(other Object) (Object, error) {
if !AssertTypes(other, IntegerType) {
return nil, NewBinaryOpError(i, other, "|")
}
return &Integer{i.Value | other.(*Integer).Value}, nil
}
func (i *Integer) BitwiseXor(other Object) (Object, error) {
if !AssertTypes(other, IntegerType) {
return nil, NewBinaryOpError(i, other, "^")
}
return &Integer{i.Value ^ other.(*Integer).Value}, nil
}
func (i *Integer) BitwiseAnd(other Object) (Object, error) {
if !AssertTypes(other, IntegerType) {
return nil, NewBinaryOpError(i, other, "&")
}
return &Integer{i.Value & other.(*Integer).Value}, nil
}
func (i *Integer) BitwiseNot() Object {
return &Integer{^i.Value}
}
func (i *Integer) LeftShift(other Object) (Object, error) {
if !AssertTypes(other, IntegerType) {
return nil, NewBinaryOpError(i, other, "<<")
}
return &Integer{i.Value << other.(*Integer).Value}, nil
}
func (i *Integer) RightShift(other Object) (Object, error) {
if !AssertTypes(other, IntegerType) {
return nil, NewBinaryOpError(i, other, ">>")
}
return &Integer{i.Value >> other.(*Integer).Value}, nil
}
func (i *Integer) LogicalNot() Object {
return &Boolean{!i.Bool()}
}
func (i *Integer) Negate() Object {
return &Integer{-i.Value}
}
func (i *Integer) Compare(other Object) int {
if obj, ok := other.(*Integer); ok {
switch {

View File

@@ -24,6 +24,20 @@ func (m Module) Inspect() string {
return fmt.Sprintf("<module '%s'>", m.Name)
}
func (m *Module) Get(index Object) (Object, error) {
key, ok := index.(Hashable)
if !ok {
return nil, fmt.Errorf("invalid module attribute %s", index.Type())
}
attr, found := m.Attrs.(*Hash).Pairs[key.HashKey()]
if !found {
return &Null{}, nil
}
return attr.Value, nil
}
func (m Module) Compare(other Object) int {
return 1
}

View File

@@ -18,6 +18,10 @@ func (n *Null) String() string {
return n.Inspect()
}
func (n *Null) LogicalNot() Object {
return &Boolean{!n.Bool()}
}
func (n *Null) Compare(other Object) int {
if _, ok := other.(*Null); ok {
return 0

View File

@@ -54,6 +54,91 @@ func (t Type) String() string {
}
}
// Add is the interface for binary addition
type Add interface {
Add(other Object) (Object, error)
}
// Sub is the interface for binary subtraction
type Sub interface {
Sub(other Object) (Object, error)
}
// Mul is the interface for binary multiplication
type Mul interface {
Mul(other Object) (Object, error)
}
// Div is the interface for binary division
type Div interface {
Div(other Object) (Object, error)
}
// Mod is the interface for binary modulo
type Mod interface {
Mod(other Object) (Object, error)
}
// LogicalOr is the interface for logical or
type LogicalOr interface {
LogicalOr(other Object) (Object, error)
}
// LogicalAnd is the interface for logical and
type LogicalAnd interface {
LogicalAnd(other Object) (Object, error)
}
// BitwiseOr is the interface for bitwise or
type BitwiseOr interface {
BitwiseOr(other Object) (Object, error)
}
// BitwiseAnd is the interface for bitwise and
type BitwiseAnd interface {
BitwiseAnd(other Object) (Object, error)
}
// BitwiseXor is the interface for bitwise xor
type BitwiseXor interface {
BitwiseXor(other Object) (Object, error)
}
// BitwiseNot is the interface for bitwise not
type BitwiseNot interface {
BitwiseNot() Object
}
// LeftShift is the interface for bitwise left shift
type LeftShift interface {
LeftShift(other Object) (Object, error)
}
// RightShift is the interface for bitwise right shift
type RightShift interface {
RightShift(other Object) (Object, error)
}
// LogicalNot is the interface for logical not
type LogicalNot interface {
LogicalNot() Object
}
// Negate is the interface for unary negation
type Negate interface {
Negate() Object
}
// Setter is the interface for assigning a value to an index
type Setter interface {
Set(index, value Object) error
}
// Getter is the interface for getting a value from an index
type Getter interface {
Get(index Object) (Object, error)
}
// Comparable is the interface for comparing two Object and their underlying
// values. It is the responsibility of the caller (left) to check for types.
// Returns `true` iif the types and values are identical, `false` otherwise.
@@ -91,3 +176,12 @@ type Hashable interface {
// BuiltinFunction represents the builtin function type
type BuiltinFunction func(args ...Object) Object
func AssertTypes(obj Object, types ...Type) bool {
for _, t := range types {
if t == obj.Type() {
return true
}
}
return false
}

View File

@@ -2,6 +2,7 @@ package object
import (
"fmt"
"strings"
"unicode/utf8"
)
@@ -33,6 +34,33 @@ func (s *String) String() string {
return s.Value
}
func (s *String) Add(other Object) (Object, error) {
if !AssertTypes(other, StringType) {
return nil, NewBinaryOpError(s, other, "+")
}
return &String{s.Value + other.(*String).Value}, nil
}
func (s *String) Mul(other Object) (Object, error) {
if !AssertTypes(other, IntegerType) {
return nil, NewBinaryOpError(s, other, "*")
}
return &String{strings.Repeat(s.Value, int(other.(*Integer).Value))}, nil
}
func (s *String) Get(index Object) (Object, error) {
if !AssertTypes(index, IntegerType) {
return nil, fmt.Errorf("invalid type for string index, expected Integer got %s", index.Type())
}
i := int(index.(*Integer).Value)
if i < 0 || i >= len(s.Value) {
return &String{}, nil
}
return &String{string(s.Value[i])}, nil
}
func (s *String) Compare(other Object) int {
if obj, ok := other.(*String); ok {
switch {

File diff suppressed because it is too large Load Diff