Refactor VM and operators
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
20
internal/object/errors.go
Normal 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}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
1226
internal/vm/vm.go
1226
internal/vm/vm.go
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user