diff --git a/src/backend/types.nim b/src/backend/types.nim deleted file mode 100644 index 6894149..0000000 --- a/src/backend/types.nim +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright 2022 Mattia Giambirtone & All Contributors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import strformat -import strutils - - -type - ObjectKind* = enum - ## Enumeration of Peon - ## types - Int8, UInt8, Int16, UInt16, Int32, - UInt32, Int64, UInt64, Float32, Float64, - Char, Byte, String, Function, CustomType, - Nil, Nan, Bool, Inf, Reference, Pointer - PeonObject* = object - ## A generic Peon object - case kind*: ObjectKind: - of String: - str*: string - of Bool: - boolean*: bool - of Inf: - positive*: bool - of Byte: - `byte`*: byte - of Int8: - tiny*: int8 - of UInt8: - uTiny*: uint8 - of Int16: - short*: int16 - of UInt16: - uShort*: uint16 - of Int32: - `int`*: int32 - of UInt32: - uInt*: uint32 - of Int64: - long*: int64 - of UInt64: - uLong*: uint64 - of Nil, Nan: - discard - of CustomType: - fields*: seq[PeonObject] - of Float32: - halfFloat*: float32 - of Float64: - `float`*: float - of Function: - ip*: uint32 - of Reference, Pointer: - value*: ptr PeonObject - else: - discard # TODO - - -proc `$`*(self: PeonObject): string = - ## Returns a string representation - ## of a peon object - case self.kind: - of Int64: - result = &"{self.long}'i64" - of UInt64: - result = &"{self.uLong}'u64" - of Int32: - result = &"{self.`int`}'i32" - of UInt32: - result = &"{self.uInt}'u32" - of Int16: - result = &"{self.short}'i16" - of UInt16: - result = &"{self.uShort}'u16" - of Int8: - result = &"{self.tiny}'i8" - of UInt8: - result = &"{self.uTiny}'u8" - of Float32: - result = &"{self.halfFloat}'f32" - of Float64: - result = &"{self.`float`}'f64" - of ObjectKind.Inf: - if self.positive: - result ="inf" - else: - result ="-inf" - of ObjectKind.Nan, Nil: - result =($self.kind).toLowerAscii() - of Function: - result = &"fn(ip: {self.ip})" - of CustomType: - result = "typevar" - of String: - result = self.str - of Bool: - if self.boolean: - result = "true" - else: - result = "false" - else: - discard \ No newline at end of file diff --git a/src/backend/vm.nim b/src/backend/vm.nim index a8b771b..b0b1f05 100644 --- a/src/backend/vm.nim +++ b/src/backend/vm.nim @@ -11,46 +11,57 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - ## The Peon runtime environment +{.push checks:off.} # The VM is a critical point where checks are deleterious import std/monotimes +import std/math -import types import ../config import ../frontend/meta/bytecode import ../util/multibyte -export types +import strutils when DEBUG_TRACE_VM: - import strutils - import strformat + import std/strformat type PeonVM* = ref object - ## The Peon Virtual Machine - calls: seq[PeonObject] # Our call stack - operands: seq[PeonObject] # Our operand stack - ip: uint32 # Instruction pointer - cache: array[6, PeonObject] # Singletons cache + ## The Peon Virtual Machine. + ## Note how the only data + ## type we handle here is + ## a 64-bit unsigned integer: + ## This is to allow the use + ## of unboxed primitive types. + ## For more complex types, the + ## value represents a pointer to + ## some stack- or heap-allocated + ## object. The VM has no concept + ## of type by itself and it relies + ## on the compiler to produce the + ## correct results + ip: uint64 # Instruction pointer chunk: Chunk # Piece of bytecode to execute - frames: seq[int] # Stores the bottom of stack frames - closedOver: seq[PeonObject] # Stores variables that do not have stack semantics - results: seq[PeonObject] # Stores function's results + calls: seq[uint64] # Our call stack + operands: seq[uint64] # Our operand stack + cache: array[6, uint64] # Singletons cache + frames: seq[uint64] # Stores the bottom of stack frames + closedOver: seq[uint64] # Stores variables that do not have stack semantics + results: seq[uint64] # Stores function's results (return values) proc initCache*(self: PeonVM) = ## Initializes the VM's ## singletons cache - self.cache[0] = PeonObject(kind: Nil) - self.cache[1] = PeonObject(kind: Bool, boolean: true) - self.cache[2] = PeonObject(kind: Bool, boolean: false) - self.cache[3] = PeonObject(kind: ObjectKind.Inf, positive: true) - self.cache[4] = PeonObject(kind: ObjectKind.Inf, positive: false) - self.cache[5] = PeonObject(kind: ObjectKind.Nan) + self.cache[0] = 0x0 # Nil + self.cache[1] = 0x1 # True + self.cache[2] = 0x2 # False + self.cache[3] = 0x3 # Positive inf + self.cache[4] = 0x4 # Negative inf + self.cache[5] = 0x5 # NaN proc newPeonVM*: PeonVM = @@ -59,26 +70,26 @@ proc newPeonVM*: PeonVM = new(result) result.ip = 0 result.frames = @[] - result.calls = newSeq[PeonObject]() - result.operands = newSeq[PeonObject]() + result.calls = newSeq[uint64]() + result.operands = newSeq[uint64]() result.initCache() # Getters for singleton types (they are cached!) -proc getNil*(self: PeonVM): PeonObject {.inline.} = self.cache[0] +proc getNil*(self: PeonVM): uint64 {.inline.} = self.cache[2] -proc getBool*(self: PeonVM, value: bool): PeonObject {.inline.} = +proc getBool*(self: PeonVM, value: bool): uint64 {.inline.} = if value: return self.cache[1] - return self.cache[2] + return self.cache[0] -proc getInf*(self: PeonVM, positive: bool): PeonObject {.inline.} = +proc getInf*(self: PeonVM, positive: bool): uint64 {.inline.} = if positive: return self.cache[3] return self.cache[4] -proc getNan*(self: PeonVM): PeonObject {.inline.} = self.cache[5] +proc getNan*(self: PeonVM): uint64 {.inline.} = self.cache[5] # Thanks to nim's *genius* idea of making x !> y a template @@ -96,58 +107,68 @@ proc `!>=`[T](a, b: T): auto {.inline, used.} = b <= a -# Stack primitives. Note: all stack accessing that goes -# through the get(c)/set(c)/peek(c) wrappers is frame-relative, +# Stack primitives. Note: all accesses to the call stack +# that go through the getc/setc wrappers is frame-relative, # meaning that the index is added to the current stack frame's -# bottom to obtain an absolute stack index. - -proc push(self: PeonVM, obj: PeonObject) = - ## Pushes a Peon object onto the +# bottom to obtain an absolute stack index +{.push inline.} +proc push(self: PeonVM, obj: uint64) = + ## Pushes a value object onto the ## operand stack self.operands.add(obj) -proc pop(self: PeonVM): PeonObject = - ## Pops a Peon object off the - ## operand stack. The object - ## is returned +proc pop(self: PeonVM): uint64 = + ## Pops a value off the + ## operand stack and + ## returns it return self.operands.pop() -proc peek(self: PeonVM, distance: int = 0): PeonObject = - ## Returns the Peon object at the +proc peekb(self: PeonVM, distance: BackwardsIndex = ^1): uint64 = + ## Returns the value at the + ## given (backwards) distance from the top of + ## the operand stack without consuming it + return self.operands[distance] + + +proc peek(self: PeonVM, distance: int = 0): uint64 = + ## Returns the value at the ## given distance from the top of ## the operand stack without consuming it + if distance < 0: + return self.peekb(^(-distance)) return self.operands[self.operands.high() + distance] -proc pushc(self: PeonVM, val: PeonObject) = - ## Pushes a new object to the + +proc pushc(self: PeonVM, val: uint64) = + ## Pushes a value to the ## call stack self.calls.add(val) -proc popc(self: PeonVM): PeonObject = - ## Pops an object off the call +proc popc(self: PeonVM): uint64 = + ## Pops a value off the call ## stack and returns it return self.calls.pop() -proc peekc(self: PeonVM, distance: int = 0): PeonObject {.used.} = - ## Returns the Peon object at the +proc peekc(self: PeonVM, distance: int = 0): uint64 {.used.} = + ## Returns the value at the ## given distance from the top of ## the call stack without consuming it return self.calls[self.calls.high() + distance] -proc getc(self: PeonVM, idx: int): PeonObject = +proc getc(self: PeonVM, idx: uint): uint64 = ## Accessor method that abstracts ## indexing our call stack through stack ## frames return self.calls[idx + self.frames[^1]] -proc setc(self: PeonVM, idx: int, val: PeonObject) = +proc setc(self: PeonVM, idx: uint, val: uint64) = ## Setter method that abstracts ## indexing our call stack through stack ## frames @@ -156,7 +177,7 @@ proc setc(self: PeonVM, idx: int, val: PeonObject) = # Byte-level primitives to read and decode # bytecode -proc readByte(self: PeonVM): uint8 = +proc readByte(self: PeonVM): uint8 = ## Reads a single byte from the ## bytecode and returns it as an ## unsigned 8 bit integer @@ -164,7 +185,7 @@ proc readByte(self: PeonVM): uint8 = return self.chunk.code[self.ip - 1] -proc readShort(self: PeonVM): uint16 = +proc readShort(self: PeonVM): uint16 = ## Reads two bytes from the ## bytecode and returns them ## as an unsigned 16 bit @@ -172,7 +193,7 @@ proc readShort(self: PeonVM): uint16 = return [self.readByte(), self.readByte()].fromDouble() -proc readLong(self: PeonVM): uint32 = +proc readLong(self: PeonVM): uint32 = ## Reads three bytes from the ## bytecode and returns them ## as an unsigned 32 bit @@ -182,7 +203,7 @@ proc readLong(self: PeonVM): uint32 = return uint32([self.readByte(), self.readByte(), self.readByte()].fromTriple()) -proc readUInt(self: PeonVM): uint32 = +proc readUInt(self: PeonVM): uint32 = ## Reads three bytes from the ## bytecode and returns them ## as an unsigned 32 bit @@ -193,122 +214,100 @@ proc readUInt(self: PeonVM): uint32 = # Functions to read primitives from the chunk's # constants table -proc constReadInt64(self: PeonVM, idx: int): PeonObject = +proc constReadInt64(self: PeonVM, idx: int): int64 = ## Reads a constant from the ## chunk's constant table and - ## returns a Peon object. Assumes - ## the constant is an Int64 + ## returns it as an int64 var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1], self.chunk.consts[idx + 2], self.chunk.consts[idx + 3], self.chunk.consts[idx + 4], self.chunk.consts[idx + 5], self.chunk.consts[idx + 6], self.chunk.consts[idx + 7], ] - result = PeonObject(kind: Int64) - copyMem(result.long.addr, arr.addr, sizeof(arr)) + copyMem(result.addr, arr.addr, sizeof(arr)) -proc constReadUInt64(self: PeonVM, idx: int): PeonObject = +proc constReadUInt64(self: PeonVM, idx: int): uint64 = ## Reads a constant from the ## chunk's constant table and - ## returns a Peon object. Assumes - ## the constant is an UInt64 + ## returns it as an uint64 var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1], self.chunk.consts[idx + 2], self.chunk.consts[idx + 3], self.chunk.consts[idx + 4], self.chunk.consts[idx + 5], self.chunk.consts[idx + 6], self.chunk.consts[idx + 7], ] - result = PeonObject(kind: UInt64) - copyMem(result.uLong.addr, arr.addr, sizeof(arr)) + copyMem(result.addr, arr.addr, sizeof(arr)) -proc constReadUInt32(self: PeonVM, idx: int): PeonObject = +proc constReadUInt32(self: PeonVM, idx: int): uint32 = ## Reads a constant from the ## chunk's constant table and - ## returns a Peon object. Assumes - ## the constant is an UInt32 + ## returns it as an int32 var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1], self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]] - result = PeonObject(kind: UInt32) - copyMem(result.uInt.addr, arr.addr, sizeof(arr)) + copyMem(result.addr, arr.addr, sizeof(arr)) -proc constReadInt32(self: PeonVM, idx: int): PeonObject = +proc constReadInt32(self: PeonVM, idx: int): int32 = ## Reads a constant from the ## chunk's constant table and - ## returns a Peon object. Assumes - ## the constant is an Int32 + ## returns it as an uint32 var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1], self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]] - result = PeonObject(kind: Int32) - copyMem(result.`int`.addr, arr.addr, sizeof(arr)) + copyMem(result.addr, arr.addr, sizeof(arr)) -proc constReadInt16(self: PeonVM, idx: int): PeonObject = +proc constReadInt16(self: PeonVM, idx: int): int16 = ## Reads a constant from the ## chunk's constant table and - ## returns a Peon object. Assumes - ## the constant is an Int16 + ## returns it as an int16 var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1]] - result = PeonObject(kind: Int16) - copyMem(result.short.addr, arr.addr, sizeof(arr)) + copyMem(result.addr, arr.addr, sizeof(arr)) -proc constReadUInt16(self: PeonVM, idx: int): PeonObject = +proc constReadUInt16(self: PeonVM, idx: int): uint16 = ## Reads a constant from the ## chunk's constant table and - ## returns a Peon object. Assumes - ## the constant is an UInt16 + ## returns it as an uint16 var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1]] - result = PeonObject(kind: UInt16) - copyMem(result.uShort.addr, arr.addr, sizeof(arr)) + copyMem(result.addr, arr.addr, sizeof(arr)) -proc constReadInt8(self: PeonVM, idx: int): PeonObject = +proc constReadInt8(self: PeonVM, idx: int): int8 = ## Reads a constant from the ## chunk's constant table and - ## returns a Peon object. Assumes - ## the constant is an Int8 - result = PeonObject(kind: Int8, tiny: int8(self.chunk.consts[idx])) + ## returns it as an int8 + result = int8(self.chunk.consts[idx]) -proc constReadUInt8(self: PeonVM, idx: int): PeonObject = +proc constReadUInt8(self: PeonVM, idx: int): uint8 = ## Reads a constant from the ## chunk's constant table and - ## returns a Peon object. Assumes - ## the constant is an UInt8 - result = PeonObject(kind: UInt8, uTiny: self.chunk.consts[idx]) + ## returns it as an uint8 + result = self.chunk.consts[idx] -proc constReadString(self: PeonVM, size: int, idx: int): PeonObject = + + +proc constReadFloat32(self: PeonVM, idx: int): float32 = ## Reads a constant from the ## chunk's constant table and - ## returns a Peon object. Assumes - ## the constant is a string - result = PeonObject(kind: String, str: self.chunk.consts[idx..