about summary refs log tree commit diff
path: root/pkg/lang/vm/mem
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/lang/vm/mem')
-rw-r--r--pkg/lang/vm/mem/cell.go25
-rw-r--r--pkg/lang/vm/mem/errors.go17
-rw-r--r--pkg/lang/vm/mem/mem.go74
3 files changed, 94 insertions, 22 deletions
diff --git a/pkg/lang/vm/mem/cell.go b/pkg/lang/vm/mem/cell.go
index 3f052d5..fcf9a50 100644
--- a/pkg/lang/vm/mem/cell.go
+++ b/pkg/lang/vm/mem/cell.go
@@ -13,8 +13,31 @@ const (
 	CellKindForbidden
 )
 
+func (c CellKind) String() string {
+	switch c {
+	case CellKindEmpty:
+		return "empty"
+	case CellKindString:
+		return "string"
+	case CellKindArray:
+		return "array"
+	case CellKindEnv:
+		return "env"
+	case CellKindOutlet:
+		return "outlet"
+	case CellKindForbidden:
+		return "forbidden"
+	default:
+		return "unknown"
+	}
+}
+
 type cell struct {
 	kind CellKind
 	refs int
-	data any
+	data CellData
+}
+
+type CellData interface {
+	DropCell(*Mem)
 }
diff --git a/pkg/lang/vm/mem/errors.go b/pkg/lang/vm/mem/errors.go
new file mode 100644
index 0000000..e7a3f8f
--- /dev/null
+++ b/pkg/lang/vm/mem/errors.go
@@ -0,0 +1,17 @@
+package mem
+
+import "errors"
+
+var (
+	ErrMemOverflow = errors.New("memory overflow, cannot allocate more than 10000 memory cells")
+
+	ErrFatalNonFreeCell = errors.New("non-free cell marked as free")
+)
+
+type ErrInvalidMemAccess struct {
+	Ptr Ptr
+}
+
+func (e ErrInvalidMemAccess) Error() string {
+	return "invalid memory access at " + e.Ptr.String()
+}
diff --git a/pkg/lang/vm/mem/mem.go b/pkg/lang/vm/mem/mem.go
index 4cb6fc4..bdcf01f 100644
--- a/pkg/lang/vm/mem/mem.go
+++ b/pkg/lang/vm/mem/mem.go
@@ -2,7 +2,7 @@ package mem
 
 type Mem struct {
 	cells []cell
-	free  []int
+	free  []Ptr
 }
 
 func New() Mem {
@@ -13,69 +13,101 @@ func New() Mem {
 
 	return Mem{
 		cells: cells,
-		free:  make([]int, 0),
+		free:  make([]Ptr, 0),
 	}
 }
 
-func (m *Mem) Allocate(kind CellKind) Ptr {
+func (m *Mem) Allocate(kind CellKind) (Ptr, error) {
 	if len(m.free) > 0 {
 		idx := m.free[len(m.free)-1]
 		m.free = m.free[:len(m.free)-1]
 
 		if m.cells[idx].kind != CellKindEmpty {
-			panic("invalid free cell")
+			return NullPtr, ErrFatalNonFreeCell
 		}
 
 		m.cells[idx].kind = kind
-		return Ptr(idx)
+		return Ptr(idx), nil
 	} else {
+		if len(m.cells) > 10000 {
+			return NullPtr, ErrMemOverflow
+		}
+
 		idx := len(m.cells)
 		m.cells = append(m.cells, cell{kind: kind, refs: 1})
-		return Ptr(idx)
+		return Ptr(idx), nil
 	}
 
 }
 
-func (m *Mem) Set(ptr Ptr, v any) {
-	if ptr >= Ptr(len(m.cells)) {
-		panic("out of bounds")
+func (m *Mem) Set(ptr Ptr, v CellData) error {
+	if err := m.validPtr(ptr); err != nil {
+		return err
 	}
 
 	m.cells[ptr].data = v
+	return nil
 }
 
-func (m *Mem) Get(ptr Ptr) any {
-	if ptr >= Ptr(len(m.cells)) {
-		panic("out of bounds")
+func (m *Mem) Get(ptr Ptr) (CellData, error) {
+	if err := m.validPtr(ptr); err != nil {
+		return nil, err
 	}
 
-	return m.cells[ptr].data
+	return m.cells[ptr].data, nil
 }
 
 func (m *Mem) Is(ptr Ptr, kind CellKind) bool {
 	if ptr >= Ptr(len(m.cells)) {
-		panic("out of bounds")
+		return false
 	}
 
 	return m.cells[ptr].kind == kind
 }
 
-func (m *Mem) Retain(ptr Ptr) {
+func (m *Mem) Kind(ptr Ptr) CellKind {
 	if ptr >= Ptr(len(m.cells)) {
-		panic("out of bounds")
+		return CellKindForbidden
+	}
+
+	return m.cells[ptr].kind
+}
+
+func (m *Mem) Retain(ptr Ptr) error {
+	if err := m.validPtr(ptr); err != nil {
+		return err
 	}
 
 	m.cells[ptr].refs++
+	return nil
 }
 
-func (m *Mem) Release(ptr Ptr) {
-	if ptr >= Ptr(len(m.cells)) {
-		panic("out of bounds")
+func (m *Mem) Release(ptr Ptr) error {
+	if err := m.validPtr(ptr); err != nil {
+		return err
 	}
 
 	m.cells[ptr].refs--
 	if m.cells[ptr].refs == 0 {
-		m.cells[ptr].kind = CellKindEmpty
-		m.free = append(m.free, int(ptr))
+		c := m.cells[ptr].data
+		c.DropCell(m)
+
+		m.cells[ptr] = cell{}
+		m.free = append(m.free, ptr)
 	}
+
+	return nil
+}
+
+func (m *Mem) validPtr(ptr Ptr) error {
+	if ptr >= Ptr(len(m.cells)) {
+		return ErrInvalidMemAccess{ptr}
+	}
+
+	kind := m.cells[ptr].kind
+	if kind == CellKindForbidden || kind == CellKindEmpty {
+		return ErrInvalidMemAccess{ptr}
+	}
+
+	return nil
 }