Refactor VM and operators
This commit is contained in:
@@ -2,6 +2,7 @@ package object
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,30 +19,30 @@ func (ao *Array) Bool() bool {
|
|||||||
return len(ao.Elements) > 0
|
return len(ao.Elements) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Array) PopLeft() Object {
|
func (ao *Array) PopLeft() Object {
|
||||||
if len(a.Elements) > 0 {
|
if len(ao.Elements) > 0 {
|
||||||
e := a.Elements[0]
|
e := ao.Elements[0]
|
||||||
a.Elements = a.Elements[1:]
|
ao.Elements = ao.Elements[1:]
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
return &Null{}
|
return &Null{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Array) PopRight() Object {
|
func (ao *Array) PopRight() Object {
|
||||||
if len(a.Elements) > 0 {
|
if len(ao.Elements) > 0 {
|
||||||
e := a.Elements[(len(a.Elements) - 1)]
|
e := ao.Elements[(len(ao.Elements) - 1)]
|
||||||
a.Elements = a.Elements[:(len(a.Elements) - 1)]
|
ao.Elements = ao.Elements[:(len(ao.Elements) - 1)]
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
return &Null{}
|
return &Null{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Array) Prepend(obj Object) {
|
func (ao *Array) Prepend(obj Object) {
|
||||||
a.Elements = append([]Object{obj}, a.Elements...)
|
ao.Elements = append([]Object{obj}, ao.Elements...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Array) Append(obj Object) {
|
func (ao *Array) Append(obj Object) {
|
||||||
a.Elements = append(a.Elements, obj)
|
ao.Elements = append(ao.Elements, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ao *Array) Inspect() string {
|
func (ao *Array) Inspect() string {
|
||||||
@@ -69,6 +70,63 @@ func (ao *Array) Less(i, j int) bool {
|
|||||||
return false
|
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 {
|
func (ao *Array) Compare(other Object) int {
|
||||||
if obj, ok := other.(*Array); ok {
|
if obj, ok := other.(*Array); ok {
|
||||||
if len(ao.Elements) != len(obj.Elements) {
|
if len(ao.Elements) != len(obj.Elements) {
|
||||||
|
|||||||
@@ -35,6 +35,24 @@ func (b *Boolean) Int() int {
|
|||||||
return 0
|
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 {
|
func (b *Boolean) Compare(other Object) int {
|
||||||
if obj, ok := other.(*Boolean); ok {
|
if obj, ok := other.(*Boolean); ok {
|
||||||
return b.Int() - obj.Int()
|
return b.Int() - obj.Int()
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package object
|
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 {
|
type Error struct {
|
||||||
Message string
|
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()
|
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 {
|
func (h *Hash) Compare(other Object) int {
|
||||||
if obj, ok := other.(*Hash); ok {
|
if obj, ok := other.(*Hash); ok {
|
||||||
if len(h.Pairs) != len(obj.Pairs) {
|
if len(h.Pairs) != len(obj.Pairs) {
|
||||||
|
|||||||
@@ -26,6 +26,94 @@ func (i *Integer) String() string {
|
|||||||
return i.Inspect()
|
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 {
|
func (i *Integer) Compare(other Object) int {
|
||||||
if obj, ok := other.(*Integer); ok {
|
if obj, ok := other.(*Integer); ok {
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
@@ -24,6 +24,20 @@ func (m Module) Inspect() string {
|
|||||||
return fmt.Sprintf("<module '%s'>", m.Name)
|
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 {
|
func (m Module) Compare(other Object) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ func (n *Null) String() string {
|
|||||||
return n.Inspect()
|
return n.Inspect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *Null) LogicalNot() Object {
|
||||||
|
return &Boolean{!n.Bool()}
|
||||||
|
}
|
||||||
|
|
||||||
func (n *Null) Compare(other Object) int {
|
func (n *Null) Compare(other Object) int {
|
||||||
if _, ok := other.(*Null); ok {
|
if _, ok := other.(*Null); ok {
|
||||||
return 0
|
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
|
// Comparable is the interface for comparing two Object and their underlying
|
||||||
// values. It is the responsibility of the caller (left) to check for types.
|
// values. It is the responsibility of the caller (left) to check for types.
|
||||||
// Returns `true` iif the types and values are identical, `false` otherwise.
|
// Returns `true` iif the types and values are identical, `false` otherwise.
|
||||||
@@ -91,3 +176,12 @@ type Hashable interface {
|
|||||||
|
|
||||||
// BuiltinFunction represents the builtin function type
|
// BuiltinFunction represents the builtin function type
|
||||||
type BuiltinFunction func(args ...Object) Object
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,6 +34,33 @@ func (s *String) String() string {
|
|||||||
return s.Value
|
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 {
|
func (s *String) Compare(other Object) int {
|
||||||
if obj, ok := other.(*String); ok {
|
if obj, ok := other.(*String); ok {
|
||||||
switch {
|
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