Move towards unboxed types in the Peon VM
This commit is contained in:
parent
fc14cfec2d
commit
19a089f4a2
|
@ -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
|
|
@ -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..<idx + size].fromBytes())
|
||||
|
||||
|
||||
proc constReadFloat32(self: PeonVM, idx: int): PeonObject =
|
||||
## Reads a constant from the
|
||||
## chunk's constant table and
|
||||
## returns a Peon object. Assumes
|
||||
## the constant is a Float32
|
||||
## returns it as a float32
|
||||
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
||||
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]]
|
||||
result = PeonObject(kind: Float32)
|
||||
copyMem(result.halfFloat.addr, arr.addr, sizeof(arr))
|
||||
copyMem(result.addr, arr.addr, sizeof(arr))
|
||||
|
||||
|
||||
proc constReadFloat64(self: PeonVM, idx: int): PeonObject =
|
||||
proc constReadFloat64(self: PeonVM, idx: int): float =
|
||||
## Reads a constant from the
|
||||
## chunk's constant table and
|
||||
## returns a Peon object. Assumes
|
||||
## the constant is a Float64
|
||||
## returns it as a float
|
||||
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: Float64)
|
||||
copyMem(result.`float`.addr, arr.addr, sizeof(arr))
|
||||
copyMem(result.addr, arr.addr, sizeof(arr))
|
||||
|
||||
{.pop.}
|
||||
|
||||
|
||||
proc dispatch*(self: PeonVM) =
|
||||
|
@ -345,62 +344,68 @@ proc dispatch*(self: PeonVM) =
|
|||
of LoadInf:
|
||||
self.push(self.getInf(true))
|
||||
of LoadInt64:
|
||||
self.push(self.constReadInt64(int(self.readLong())))
|
||||
self.push(uint64(self.constReadInt64(int(self.readLong()))))
|
||||
of LoadUInt64:
|
||||
self.push(self.constReadUInt64(int(self.readLong())))
|
||||
self.push(uint64(self.constReadUInt64(int(self.readLong()))))
|
||||
of LoadUInt32:
|
||||
self.push(self.constReadUInt32(int(self.readLong())))
|
||||
self.push(uint64(self.constReadUInt32(int(self.readLong()))))
|
||||
of LoadInt32:
|
||||
self.push(self.constReadInt32(int(self.readLong())))
|
||||
self.push(uint64(self.constReadInt32(int(self.readLong()))))
|
||||
of LoadInt16:
|
||||
self.push(self.constReadInt16(int(self.readLong())))
|
||||
self.push(uint64(self.constReadInt16(int(self.readLong()))))
|
||||
of LoadUInt16:
|
||||
self.push(self.constReadUInt16(int(self.readLong())))
|
||||
self.push(uint64(self.constReadUInt16(int(self.readLong()))))
|
||||
of LoadInt8:
|
||||
self.push(self.constReadInt8(int(self.readLong())))
|
||||
self.push(uint64(self.constReadInt8(int(self.readLong()))))
|
||||
of LoadUInt8:
|
||||
self.push(self.constReadUInt8(int(self.readLong())))
|
||||
self.push(uint64(self.constReadUInt8(int(self.readLong()))))
|
||||
of LoadString:
|
||||
self.push(self.constReadString(int(self.readLong()), int(self.readLong())))
|
||||
# TODO: Use constReadString with own memory manager
|
||||
# Strings are broken rn!!
|
||||
let size = int(self.readLong())
|
||||
let idx = int(self.readLong())
|
||||
var str = self.chunk.consts[idx..<idx + size].fromBytes()
|
||||
self.push(cast[uint64](str.addr))
|
||||
# We cast instead of converting because, unlike with integers,
|
||||
# we don't want nim to touch any of the bits of the underlying
|
||||
# value!
|
||||
of LoadFloat32:
|
||||
self.push(self.constReadFloat32(int(self.readLong())))
|
||||
self.push(cast[uint64](self.constReadFloat32(int(self.readLong()))))
|
||||
of LoadFloat64:
|
||||
self.push(self.constReadFloat64(int(self.readLong())))
|
||||
self.push(cast[uint64](self.constReadFloat64(int(self.readLong()))))
|
||||
of LoadFunction:
|
||||
# Loads a function onto the operand stack by reading its
|
||||
# instruction pointer
|
||||
self.push(PeonObject(kind: Function, ip: self.readLong()))
|
||||
# Loads a function address onto the operand stack
|
||||
self.push(uint64(self.readLong()))
|
||||
of LoadReturnAddress:
|
||||
# Loads a 32-bit unsigned integer onto the operand stack.
|
||||
# Used to load function return addresses
|
||||
self.push(PeonObject(kind: UInt32, uInt: self.readUInt()))
|
||||
self.push(uint64(self.readUInt()))
|
||||
of Call:
|
||||
# Calls a function. The calling convention for peon
|
||||
# functions is pretty simple: the first item in the
|
||||
# frame is a function object which contains the new
|
||||
# instruction pointer to jump to, followed by a 32-bit
|
||||
# return address. After that, all arguments and locals
|
||||
# follow. Note that, due to how the stack works, all
|
||||
# arguments before the call are in the reverse order in
|
||||
# which they are passed to the function
|
||||
# Calls a peon function. The calling convention here
|
||||
# is pretty simple: the first value in the frame is
|
||||
# the new instruction pointer to jump to, then a
|
||||
# 32-bit return address follows. After that, all
|
||||
# arguments and locals follow. Note that, due to
|
||||
# how the stack works, all arguments before the call
|
||||
# are in the reverse order in which they are passed
|
||||
# to the function
|
||||
var argc {.used.} = self.readLong().int
|
||||
let retAddr = self.peek(-argc) # Return address
|
||||
let fnObj = self.peek(-argc - 1) # Function object
|
||||
self.ip = fnObj.ip
|
||||
self.pushc(fnObj)
|
||||
let retAddr = self.peek(-argc - 1) # Return address
|
||||
let jmpAddr = self.peek(-argc - 2) # Function address
|
||||
self.ip = jmpAddr
|
||||
self.pushc(jmpAddr)
|
||||
self.pushc(retAddr)
|
||||
# Creates a new result slot for the
|
||||
# function's return value
|
||||
self.results.add(self.getNil())
|
||||
# Creates a new call frame
|
||||
self.frames.add(self.calls.len() - 2)
|
||||
self.frames.add(uint64(self.calls.len() - 2))
|
||||
# Loads the arguments onto the stack
|
||||
for _ in 0..<argc:
|
||||
self.pushc(self.pop())
|
||||
# Pops the function object and
|
||||
# return address off the operand
|
||||
# stack since they're not needed
|
||||
# there anymore
|
||||
# Pops the function and return address
|
||||
# off the operand stack since they're
|
||||
# not needed there anymore
|
||||
discard self.pop()
|
||||
discard self.pop()
|
||||
# TODO: Use the frame's initial size once
|
||||
|
@ -416,12 +421,12 @@ proc dispatch*(self: PeonVM) =
|
|||
# in a hidden function, so this
|
||||
# will also exit the VM if we're
|
||||
# at the end of the program
|
||||
while self.calls.len() !> self.frames[^1] + 2:
|
||||
while self.calls.len().uint64 !> self.frames[^1] + 2'u64:
|
||||
# Discards the function's local variables,
|
||||
# if there is any
|
||||
discard self.popc()
|
||||
let ret = self.popc() # Return address
|
||||
discard self.popc() # Function object
|
||||
discard self.popc() # Function address
|
||||
if self.readByte() == 1:
|
||||
# Function is non-void!
|
||||
self.push(self.results.pop())
|
||||
|
@ -444,8 +449,10 @@ proc dispatch*(self: PeonVM) =
|
|||
of StoreVar:
|
||||
# Stores the value at the top of the operand stack
|
||||
# into the given call stack index
|
||||
let idx = int(self.readLong())
|
||||
if idx + self.frames[^1] <= self.calls.high():
|
||||
let idx = self.readLong()
|
||||
when DEBUG_TRACE_VM:
|
||||
assert idx.int - self.calls.high() <= 1, "StoreVar index is bigger than the length of the call stack"
|
||||
if idx + self.frames[^1] <= self.calls.high().uint:
|
||||
self.setc(idx, self.pop())
|
||||
else:
|
||||
self.pushc(self.pop())
|
||||
|
@ -466,7 +473,7 @@ proc dispatch*(self: PeonVM) =
|
|||
of LoadVar:
|
||||
# Pushes a variable onto the operand
|
||||
# stack
|
||||
self.push(self.getc(int(self.readLong())))
|
||||
self.push(self.getc(self.readLong()))
|
||||
of NoOp:
|
||||
# Does nothing
|
||||
continue
|
||||
|
@ -488,257 +495,164 @@ proc dispatch*(self: PeonVM) =
|
|||
discard self.pop()
|
||||
continue
|
||||
echo self.pop()
|
||||
of GenericPrint:
|
||||
# Prints the peon object at the top
|
||||
# of the operand stack
|
||||
echo self.pop()
|
||||
of PopN:
|
||||
# Pops N elements off the call stack
|
||||
for _ in 0..<int(self.readShort()):
|
||||
discard self.popc()
|
||||
# Jump opcodes
|
||||
of Jump:
|
||||
# Absolute jump
|
||||
self.ip = self.readLong()
|
||||
of JumpForwards:
|
||||
# Relative, forward-jump
|
||||
self.ip += self.readLong()
|
||||
of JumpBackwards:
|
||||
# Relative, backward-jump
|
||||
self.ip -= self.readLong()
|
||||
of JumpIfFalse:
|
||||
if not self.peek().boolean:
|
||||
# Conditional positive jump
|
||||
if not self.peek().bool:
|
||||
self.ip += self.readLong()
|
||||
of JumpIfTrue:
|
||||
if self.peek().boolean:
|
||||
# Conditional positive jump
|
||||
if self.peek().bool:
|
||||
self.ip += self.readLong()
|
||||
of JumpIfFalsePop:
|
||||
let ip = self.readLong()
|
||||
if not self.peek().boolean:
|
||||
if not self.peek().bool:
|
||||
self.ip += ip
|
||||
discard self.pop()
|
||||
of JumpIfFalseOrPop:
|
||||
if not self.peek().boolean:
|
||||
if not self.peek().bool:
|
||||
self.ip += self.readLong()
|
||||
else:
|
||||
discard self.pop()
|
||||
# Built-in operations on primitive types
|
||||
of AddInt64:
|
||||
self.push(PeonObject(kind: Int64, long: self.pop().long + self.pop().long))
|
||||
of SubInt64:
|
||||
self.push(PeonObject(kind: Int64, long: self.pop().long - self.pop().long))
|
||||
of MulInt64:
|
||||
self.push(PeonObject(kind: Int64, long: self.pop().long * self.pop().long))
|
||||
of DivInt64:
|
||||
self.push(PeonObject(kind: Int64, long: self.pop().long div self.pop().long))
|
||||
of AddUInt64:
|
||||
self.push(PeonObject(kind: UInt64, uLong: self.pop().uLong + self.pop().uLong))
|
||||
of SubUInt64:
|
||||
self.push(PeonObject(kind: UInt64, uLong: self.pop().uLong - self.pop().uLong))
|
||||
of MulUInt64:
|
||||
self.push(PeonObject(kind: UInt64, uLong: self.pop().uLong * self.pop().uLong))
|
||||
of DivUInt64:
|
||||
self.push(PeonObject(kind: UInt64, uLong: self.pop().uLong div self.pop().uLong))
|
||||
of AddInt32:
|
||||
self.push(PeonObject(kind: Int32, `int`: self.pop().`int` + self.pop().`int`))
|
||||
of SubInt32:
|
||||
self.push(PeonObject(kind: Int32, `int`: self.pop().`int` - self.pop().`int`))
|
||||
of MulInt32:
|
||||
self.push(PeonObject(kind: Int32, `int`: self.pop().`int` * self.pop().`int`))
|
||||
of DivInt32:
|
||||
self.push(PeonObject(kind: Int32, `int`: self.pop().`int` div self.pop().`int`))
|
||||
of AddUInt32:
|
||||
self.push(PeonObject(kind: UInt32, uInt: self.pop().uInt + self.pop().uInt))
|
||||
of SubUInt32:
|
||||
self.push(PeonObject(kind: UInt32, uInt: self.pop().uInt - self.pop().uInt))
|
||||
of MulUInt32:
|
||||
self.push(PeonObject(kind: UInt32, uInt: self.pop().uInt * self.pop().uInt))
|
||||
of DivUInt32:
|
||||
self.push(PeonObject(kind: UInt32, uInt: self.pop().uInt div self.pop().uInt))
|
||||
of AddInt16:
|
||||
self.push(PeonObject(kind: Int16, short: self.pop().short + self.pop().short))
|
||||
of SubInt16:
|
||||
self.push(PeonObject(kind: Int16, short: self.pop().short - self.pop().short))
|
||||
of MulInt16:
|
||||
self.push(PeonObject(kind: Int16, short: self.pop().short * self.pop().short))
|
||||
of DivInt16:
|
||||
self.push(PeonObject(kind: Int16, short: self.pop().short div self.pop().short))
|
||||
of AddUInt16:
|
||||
self.push(PeonObject(kind: UInt16, uShort: self.pop().uShort + self.pop().uShort))
|
||||
of SubUInt16:
|
||||
self.push(PeonObject(kind: UInt16, uShort: self.pop().uShort - self.pop().uShort))
|
||||
of MulUInt16:
|
||||
self.push(PeonObject(kind: UInt16, uShort: self.pop().uShort * self.pop().uShort))
|
||||
of DivUInt16:
|
||||
self.push(PeonObject(kind: UInt16, uShort: self.pop().uShort div self.pop().uShort))
|
||||
of AddInt8:
|
||||
self.push(PeonObject(kind: Int8, tiny: self.pop().tiny + self.pop().tiny))
|
||||
of SubInt8:
|
||||
self.push(PeonObject(kind: Int8, tiny: self.pop().tiny - self.pop().tiny))
|
||||
of MulInt8:
|
||||
self.push(PeonObject(kind: Int8, tiny: self.pop().tiny * self.pop().tiny))
|
||||
of DivInt8:
|
||||
self.push(PeonObject(kind: Int8, tiny: self.pop().tiny div self.pop().tiny))
|
||||
of AddUInt8:
|
||||
self.push(PeonObject(kind: UInt8, uTiny: self.pop().uTiny + self.pop().uTiny))
|
||||
of SubUInt8:
|
||||
self.push(PeonObject(kind: UInt8, uTiny: self.pop().uTiny - self.pop().uTiny))
|
||||
of MulUInt8:
|
||||
self.push(PeonObject(kind: UInt8, uTiny: self.pop().uTiny * self.pop().uTiny))
|
||||
of DivUInt8:
|
||||
self.push(PeonObject(kind: UInt8, uTiny: self.pop().uTiny div self.pop().uTiny))
|
||||
# Built-in operations on primitive types.
|
||||
# Note: for operations where the order of
|
||||
# the operands matters, we don't need to
|
||||
# swap the order of the calls to pop: this
|
||||
# is because operators are handled like peon
|
||||
# functions, which means the arguments are
|
||||
# already reversed on the stack when we
|
||||
# execute the instruction
|
||||
of Negate:
|
||||
self.push(uint64(-int64(self.pop())))
|
||||
of NegateFloat64:
|
||||
self.push(cast[uint64](-cast[float](self.pop())))
|
||||
of NegateFloat32:
|
||||
self.push(cast[uint64](-cast[float32](self.pop())))
|
||||
of Add:
|
||||
self.push(self.pop() + self.pop())
|
||||
of Subtract:
|
||||
self.push(self.pop() - self.pop())
|
||||
of Multiply:
|
||||
self.push(self.pop() * self.pop())
|
||||
of Divide:
|
||||
self.push(self.pop() div self.pop())
|
||||
of SignedDivide:
|
||||
self.push(uint64(int64(self.pop()) div int64(self.pop())))
|
||||
of AddFloat64:
|
||||
self.push(PeonObject(kind: Float64, `float`: self.pop().`float` + self.pop().`float`))
|
||||
of SubFloat64:
|
||||
self.push(PeonObject(kind: Float64, `float`: self.pop().`float` - self.pop().`float`))
|
||||
of MulFloat64:
|
||||
self.push(PeonObject(kind: Float64, `float`: self.pop().`float` * self.pop().`float`))
|
||||
of DivFloat64:
|
||||
self.push(PeonObject(kind: Float64, `float`: self.pop().`float` / self.pop().`float`))
|
||||
self.push(cast[uint64](cast[float](self.pop()) + cast[float](self.pop())))
|
||||
of SubtractFloat64:
|
||||
self.push(cast[uint64](cast[float](self.pop()) - cast[float](self.pop())))
|
||||
of MultiplyFloat64:
|
||||
self.push(cast[uint64](cast[float](self.pop()) * cast[float](self.pop())))
|
||||
of DivideFloat64:
|
||||
self.push(cast[uint64](cast[float](self.pop()) / cast[float](self.pop())))
|
||||
of AddFloat32:
|
||||
self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat + self.pop().halfFloat))
|
||||
of SubFloat32:
|
||||
self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat - self.pop().halfFloat))
|
||||
of MulFloat32:
|
||||
self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat * self.pop().halfFloat))
|
||||
of DivFloat32:
|
||||
self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat / self.pop().halfFloat))
|
||||
of NegInt64:
|
||||
self.push(PeonObject(kind: Int64, long: -self.pop().long))
|
||||
of NegInt32:
|
||||
self.push(PeonObject(kind: Int32, `int`: -self.pop().`int`))
|
||||
of NegInt16:
|
||||
self.push(PeonObject(kind: Int16, short: -self.pop().short))
|
||||
of NegInt8:
|
||||
self.push(PeonObject(kind: Int8, tiny: -self.pop().tiny))
|
||||
of NegFloat64:
|
||||
self.push(PeonObject(kind: Float64, `float`: -self.pop().`float`))
|
||||
of NegFloat32:
|
||||
self.push(PeonObject(kind: Float32, halfFloat: -self.pop().halfFloat))
|
||||
of LessThanInt64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().long < self.pop().long))
|
||||
of GreaterThanInt64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().long !> self.pop().long))
|
||||
of EqualInt64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().long == self.pop().long))
|
||||
of NotEqualInt64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().long != self.pop().long))
|
||||
of LessThanUInt64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uLong < self.pop().uLong))
|
||||
of GreaterThanUInt64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uLong !> self.pop().uLong))
|
||||
of EqualUInt64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uLong == self.pop().uLong))
|
||||
of NotEqualUInt64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uLong != self.pop().uLong))
|
||||
of LessThanInt32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().`int` < self.pop().`int`))
|
||||
of GreaterThanInt32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().`int` !> self.pop().`int`))
|
||||
of EqualInt32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().`int` == self.pop().`int`))
|
||||
of NotEqualInt32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().`int` != self.pop().`int`))
|
||||
of LessThanUInt32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uInt < self.pop().uInt))
|
||||
of GreaterThanUInt32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uInt !> self.pop().uInt))
|
||||
of EqualUInt32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uInt == self.pop().uInt))
|
||||
of NotEqualUInt32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uInt != self.pop().uInt))
|
||||
of LessThanInt16:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().short < self.pop().short))
|
||||
of GreaterThanInt16:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().short !> self.pop().short))
|
||||
of EqualInt16:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().short == self.pop().short))
|
||||
of NotEqualInt16:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().short != self.pop().short))
|
||||
of LessThanUInt16:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uShort < self.pop().uShort))
|
||||
of GreaterThanUInt16:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uShort !> self.pop().uShort))
|
||||
of EqualUInt16:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uShort == self.pop().uShort))
|
||||
of NotEqualUInt16:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uShort != self.pop().uShort))
|
||||
of LessThanInt8:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().tiny < self.pop().tiny))
|
||||
of GreaterThanInt8:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().tiny !> self.pop().tiny))
|
||||
of EqualInt8:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().tiny == self.pop().tiny))
|
||||
of NotEqualInt8:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().tiny != self.pop().tiny))
|
||||
of LessThanUInt8:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uTiny < self.pop().uTiny))
|
||||
of GreaterThanUInt8:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uTiny !> self.pop().uTiny))
|
||||
of EqualUInt8:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uTiny == self.pop().uTiny))
|
||||
of NotEqualUInt8:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uTiny != self.pop().uTiny))
|
||||
of LessThanFloat64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().`float` < self.pop().`float`))
|
||||
of GreaterThanFloat64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().`float` !> self.pop().`float`))
|
||||
of EqualFloat64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().`float` == self.pop().`float`))
|
||||
of NotEqualFLoat64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().`float` != self.pop().`float`))
|
||||
of LessThanFloat32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().halfFloat < self.pop().halfFloat))
|
||||
of GreaterThanFloat32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().halfFloat !> self.pop().halfFloat))
|
||||
of EqualFloat32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().halfFloat == self.pop().halfFloat))
|
||||
of NotEqualFloat32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().halfFloat != self.pop().halfFloat))
|
||||
of GreaterOrEqualInt64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().long !>= self.pop().long))
|
||||
of LessOrEqualInt64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().long <= self.pop().long))
|
||||
of GreaterOrEqualUInt64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uLong !>= self.pop().uLong))
|
||||
of LessOrEqualUInt64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uLong <= self.pop().uLong))
|
||||
of GreaterOrEqualInt32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().`int` !>= self.pop().`int`))
|
||||
of LessOrEqualInt32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().`int` <= self.pop().`int`))
|
||||
of GreaterOrEqualUInt32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uInt !>= self.pop().uInt))
|
||||
of LessOrEqualUInt32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uInt <= self.pop().uInt))
|
||||
of GreaterOrEqualInt16:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().short !>= self.pop().short))
|
||||
of LessOrEqualInt16:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().short <= self.pop().short))
|
||||
of GreaterOrEqualUInt16:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uShort !>= self.pop().uShort))
|
||||
of LessOrEqualUInt16:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uShort <= self.pop().uShort))
|
||||
of GreaterOrEqualInt8:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().tiny !>= self.pop().tiny))
|
||||
of LessOrEqualInt8:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().tiny <= self.pop().tiny))
|
||||
of GreaterOrEqualUInt8:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uTiny !>= self.pop().uTiny))
|
||||
of LessOrEqualUInt8:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().uTiny <= self.pop().uTiny))
|
||||
of GreaterOrEqualFloat64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().`float` !>= self.pop().`float`))
|
||||
of LessOrEqualFloat64:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().`float` <= self.pop().`float`))
|
||||
of GreaterOrEqualFloat32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().halfFloat !>= self.pop().halfFloat))
|
||||
of LessOrEqualFloat32:
|
||||
self.push(PeonObject(kind: Bool, boolean: self.pop().halfFloat <= self.pop().halfFloat))
|
||||
self.push(cast[uint64](cast[float32](self.pop()) + cast[float32](self.pop())))
|
||||
of SubtractFloat32:
|
||||
self.push(cast[uint64](cast[float32](self.pop()) - cast[float32](self.pop())))
|
||||
of MultiplyFloat32:
|
||||
self.push(cast[uint64](cast[float32](self.pop()) * cast[float32](self.pop())))
|
||||
of DivideFloat32:
|
||||
self.push(cast[uint64](cast[float32](self.pop()) / cast[float32](self.pop())))
|
||||
of Pow:
|
||||
self.push(uint64(self.pop() ^ self.pop()))
|
||||
of SignedPow:
|
||||
self.push(uint64(int64(self.pop()) ^ int64(self.pop())))
|
||||
of PowFloat64:
|
||||
self.push(cast[uint64](pow(cast[float](self.pop()), cast[float](self.pop()))))
|
||||
of PowFloat32:
|
||||
self.push(cast[uint64](pow(cast[float](self.pop()), cast[float](self.pop()))))
|
||||
of Mod:
|
||||
self.push(uint64(self.pop() mod self.pop()))
|
||||
of SignedMod:
|
||||
self.push(uint64(int64(self.pop()) mod int64(self.pop())))
|
||||
of ModFloat64:
|
||||
self.push(cast[uint64](floorMod(cast[float](self.pop()), cast[float](self.pop()))))
|
||||
of ModFloat32:
|
||||
self.push(cast[uint64](floorMod(cast[float](self.pop()), cast[float](self.pop()))))
|
||||
of LShift:
|
||||
self.push(self.pop() shl self.pop())
|
||||
of RShift:
|
||||
self.push(self.pop() shr self.pop())
|
||||
of Xor:
|
||||
self.push(self.pop() xor self.pop())
|
||||
of Not:
|
||||
self.push(not self.pop())
|
||||
of And:
|
||||
self.push(self.pop() and self.pop())
|
||||
of Equal:
|
||||
self.push(self.getBool(self.pop() == self.pop()))
|
||||
of NotEqual:
|
||||
self.push(self.getBool(self.pop() != self.pop()))
|
||||
of GreaterThan:
|
||||
self.push(self.getBool(self.pop() !> self.pop()))
|
||||
of LessThan:
|
||||
self.push(self.getBool(self.pop() < self.pop()))
|
||||
of GreaterOrEqual:
|
||||
self.push(self.getBool(self.pop() !>= self.pop()))
|
||||
of LessOrEqual:
|
||||
self.push(self.getBool(self.pop() <= self.pop()))
|
||||
of PrintInt64:
|
||||
# Prints the value at the top of the stack
|
||||
# as an int64
|
||||
echo int64(self.pop())
|
||||
of PrintUInt64:
|
||||
# Prints the value at the top of the stack
|
||||
echo self.pop()
|
||||
of PrintInt32:
|
||||
echo int32(self.pop())
|
||||
of PrintUInt32:
|
||||
echo uint32(self.pop())
|
||||
of PrintInt16:
|
||||
echo int16(self.pop())
|
||||
of PrintUInt16:
|
||||
echo uint16(self.pop())
|
||||
of PrintInt8:
|
||||
echo int8(self.pop())
|
||||
of PrintUInt8:
|
||||
echo uint8(self.pop())
|
||||
of PrintFloat32:
|
||||
echo cast[float32](self.pop())
|
||||
of PrintFloat64:
|
||||
echo cast[float](self.pop())
|
||||
of PrintHex:
|
||||
# Prints the value at the top of the stack
|
||||
# as a hexadecimal integer
|
||||
echo "0x" & self.pop().toHex().strip(chars={'0'})
|
||||
of PrintBool:
|
||||
if self.pop().bool:
|
||||
echo "true"
|
||||
else:
|
||||
echo "false"
|
||||
of PrintInf:
|
||||
if self.pop() == 0x3:
|
||||
echo "-inf"
|
||||
else:
|
||||
echo "inf"
|
||||
of PrintNan:
|
||||
echo "nan"
|
||||
of PrintString:
|
||||
echo cast[ptr string](self.pop())[] # TODO
|
||||
of SysClock64:
|
||||
# Pushes the value of a monotonic clock
|
||||
# as a 64 bit float onto the operand stack.
|
||||
# Used to track system runtime accurately,
|
||||
# but cannot be converted to a date. The number
|
||||
# onto the operand stack. This can be used
|
||||
# to track system time accurately, but it
|
||||
# cannot be converted to a date. The number
|
||||
# is in seconds
|
||||
self.push(PeonObject(kind: Float64, `float`: getMonoTime().ticks().float() / 1_000_000_000))
|
||||
self.push(cast[uint64](getMonoTime().ticks().float() / 1_000_000_000))
|
||||
else:
|
||||
discard
|
||||
|
||||
|
@ -751,3 +665,5 @@ proc run*(self: PeonVM, chunk: Chunk) =
|
|||
self.operands = @[]
|
||||
self.ip = 0
|
||||
self.dispatch()
|
||||
|
||||
{.pop.}
|
||||
|
|
|
@ -21,10 +21,10 @@ when HEAP_GROW_FACTOR <= 1:
|
|||
{.fatal: "Heap growth factor must be > 1".}
|
||||
const PEON_VERSION* = (major: 0, minor: 1, patch: 0)
|
||||
const PEON_RELEASE* = "alpha"
|
||||
const PEON_COMMIT_HASH* = "e90ac2f4eb7ea5b95467bae5439cd21d2e89a812"
|
||||
const PEON_COMMIT_HASH* = "b273cd744883458a4a6354a0cc5f4f5d0f560c31"
|
||||
when len(PEON_COMMIT_HASH) != 40:
|
||||
{.fatal: "The git commit hash must be exactly 40 characters long".}
|
||||
const PEON_BRANCH* = "master"
|
||||
const PEON_BRANCH* = "unboxed-types"
|
||||
when len(PEON_BRANCH) > 255:
|
||||
{.fatal: "The git branch name's length must be less than or equal to 255 characters".}
|
||||
const DEBUG_TRACE_VM* = debugVM # Traces VM execution
|
||||
|
|
|
@ -183,6 +183,8 @@ type
|
|||
source: string
|
||||
# Currently imported modules
|
||||
modules: HashSet[string]
|
||||
# TODO
|
||||
scopes: seq[Type]
|
||||
CompileError* = ref object of PeonException
|
||||
compiler*: Compiler
|
||||
node*: ASTNode
|
||||
|
@ -372,6 +374,9 @@ proc patchJump(self: Compiler, offset: int) =
|
|||
var jump: int = self.chunk.code.len() - offset
|
||||
if jump > 16777215:
|
||||
self.error("cannot jump more than 16777215 instructions")
|
||||
# We subtract 4 because that's the size of our jump instruction
|
||||
# which the caller of patchJump doesn't take into account (and
|
||||
# that's by design)
|
||||
let offsetArray = (jump - 4).toTriple()
|
||||
self.chunk.code[offset + 1] = offsetArray[0]
|
||||
self.chunk.code[offset + 2] = offsetArray[1]
|
||||
|
@ -395,13 +400,13 @@ proc resolve(self: Compiler, name: IdentExpr,
|
|||
return nil
|
||||
|
||||
|
||||
proc getStackPos(self: Compiler, name: Name, depth: int = self.scopeDepth): int =
|
||||
proc getStackPos(self: Compiler, name: Name): int =
|
||||
## Returns the predicted call stack position of a given name, relative
|
||||
## to the current frame
|
||||
var found = false
|
||||
result = 2
|
||||
for variable in self.names:
|
||||
if variable.isFunDecl:
|
||||
if variable.isFunDecl or variable.valueType.kind in {CustomType, Generic}:
|
||||
continue
|
||||
if name == variable:
|
||||
found = true
|
||||
|
@ -455,12 +460,13 @@ proc detectClosureVariable(self: Compiler, name: var Name, depth: int = self.sco
|
|||
## unpredictably or crash
|
||||
if name.isNil() or name.depth == 0 or name.isClosedOver:
|
||||
return
|
||||
elif name.depth < depth:
|
||||
# Ding! The given name is closed over: we need to
|
||||
# change the dummy Jump instruction that self.declareName
|
||||
elif name.depth < depth and self.scopes[name.depth - 1] != self.scopes[depth - 1]:
|
||||
# Ding! The given name is closed over in another function:
|
||||
# we need to change the Jump instruction that self.declareName
|
||||
# put in place for us into a StoreClosure. We also update
|
||||
# the name's isClosedOver field so that self.identifier()
|
||||
# can emit a LoadClosure instruction instead of a LoadVar
|
||||
# once this name is referenced in the future
|
||||
if not name.isFunctionArgument:
|
||||
# We handle closed-over function arguments later
|
||||
self.closedOver.add(name)
|
||||
|
@ -535,13 +541,13 @@ proc toIntrinsic(name: string): Type =
|
|||
## otherwise
|
||||
if name in ["int", "int64", "i64"]:
|
||||
return Type(kind: Int64)
|
||||
elif name in ["uint64", "u64"]:
|
||||
elif name in ["uint64", "u64", "uint"]:
|
||||
return Type(kind: UInt64)
|
||||
elif name in ["int32", "i32"]:
|
||||
return Type(kind: Int32)
|
||||
elif name in ["uint32", "u32"]:
|
||||
return Type(kind: UInt32)
|
||||
elif name in ["int16", "i16"]:
|
||||
elif name in ["int16", "i16", "short"]:
|
||||
return Type(kind: Int16)
|
||||
elif name in ["uint16", "u16"]:
|
||||
return Type(kind: UInt16)
|
||||
|
@ -553,9 +559,9 @@ proc toIntrinsic(name: string): Type =
|
|||
return Type(kind: Float64)
|
||||
elif name in ["f32", "float32"]:
|
||||
return Type(kind: Float32)
|
||||
elif name == "byte":
|
||||
elif name in ["byte", "b"]:
|
||||
return Type(kind: Byte)
|
||||
elif name == "char":
|
||||
elif name in ["char", "c"]:
|
||||
return Type(kind: Char)
|
||||
elif name == "nan":
|
||||
return Type(kind: Nan)
|
||||
|
@ -802,12 +808,12 @@ proc check(self: Compiler, term: Expression, kind: Type) =
|
|||
let k = self.inferType(term)
|
||||
if k.isNil():
|
||||
if term.kind == identExpr:
|
||||
self.error(&"reference to undeclared name '{term.token.lexeme}'")
|
||||
self.error(&"reference to undeclared name '{term.token.lexeme}'", term)
|
||||
elif term.kind == callExpr and CallExpr(term).callee.kind == identExpr:
|
||||
self.error(&"call to undeclared function '{CallExpr(term).callee.token.lexeme}'")
|
||||
self.error(&"expecting value of type '{self.typeToStr(kind)}', but expression has no type")
|
||||
self.error(&"call to undeclared function '{CallExpr(term).callee.token.lexeme}'", term)
|
||||
self.error(&"expecting value of type '{self.typeToStr(kind)}', but expression has no type", term)
|
||||
elif not self.compareTypes(k, kind):
|
||||
self.error(&"expecting value of type '{self.typeToStr(kind)}', got '{self.typeToStr(k)}' instead")
|
||||
self.error(&"expecting value of type '{self.typeToStr(kind)}', got '{self.typeToStr(k)}' instead", term)
|
||||
|
||||
|
||||
|
||||
|
@ -921,101 +927,65 @@ proc handleBuiltinFunction(self: Compiler, fn: Name, args: seq[Expression]) =
|
|||
self.expression(args[0])
|
||||
elif len(args) == 1:
|
||||
self.expression(args[0])
|
||||
const codes: Table[string, OpCode] = {"Negate": Negate,
|
||||
"NegateFloat32": NegateFloat32,
|
||||
"NegateFloat64": NegateFloat64,
|
||||
"Add": Add,
|
||||
"Subtract": Subtract,
|
||||
"Divide": Divide,
|
||||
"Multiply": Multiply,
|
||||
"SignedDivide": SignedDivide,
|
||||
"AddFloat64": AddFloat64,
|
||||
"SubtractFloat64": SubtractFloat64,
|
||||
"DivideFloat64": DivideFloat64,
|
||||
"MultiplyFloat64": MultiplyFloat64,
|
||||
"AddFloat32": AddFloat32,
|
||||
"SubtractFloat32": SubtractFloat32,
|
||||
"DivideFloat32": DivideFloat32,
|
||||
"MultiplyFloat32": MultiplyFloat32,
|
||||
"Pow": Pow,
|
||||
"SignedPow": SignedPow,
|
||||
"PowFloat32": PowFloat32,
|
||||
"PowFloat64": PowFloat64,
|
||||
"Mod": Mod,
|
||||
"SignedMod": SignedMod,
|
||||
"ModFloat32": ModFloat32,
|
||||
"ModFloat64": ModFloat64,
|
||||
"Or": Or,
|
||||
"And": And,
|
||||
"Xor": Xor,
|
||||
"Not": Not,
|
||||
"LShift": LShift,
|
||||
"RShift": RShift,
|
||||
"Equal": Equal,
|
||||
"NotEqual": NotEqual,
|
||||
"LessThan": LessThan,
|
||||
"GreaterThan": GreaterThan,
|
||||
"LessOrEqual": LessOrEqual,
|
||||
"GreaterOrEqual": GreaterOrEqual,
|
||||
"PrintInt64": PrintInt64,
|
||||
"PrintUInt64": PrintUInt64,
|
||||
"PrintInt32": PrintInt32,
|
||||
"PrintUInt32": PrintUInt32,
|
||||
"PrintInt16": PrintInt16,
|
||||
"PrintUInt16": PrintUInt16,
|
||||
"PrintInt8": PrintInt8,
|
||||
"PrintUInt8": PrintUInt8,
|
||||
"PrintFloat64": PrintFloat64,
|
||||
"PrintFloat32": PrintFloat32,
|
||||
"PrintHex": PrintHex,
|
||||
"PrintBool": PrintBool,
|
||||
"PrintNan": PrintNan,
|
||||
"PrintInf": PrintInf,
|
||||
"PrintString": PrintString,
|
||||
"SysClock64": SysClock64
|
||||
}.to_table()
|
||||
if fn.valueType.builtinOp in codes:
|
||||
self.emitByte(codes[fn.valueType.builtinOp])
|
||||
return
|
||||
# Some builtin operations are slightly more complex
|
||||
# so we handle them separately
|
||||
case fn.valueType.builtinOp:
|
||||
of "GenericPrint":
|
||||
self.emitByte(GenericPrint)
|
||||
of "AddInt64":
|
||||
self.emitByte(AddInt64)
|
||||
of "SubInt64":
|
||||
self.emitByte(SubInt64)
|
||||
of "DivInt64":
|
||||
self.emitByte(DivInt64)
|
||||
of "MulInt64":
|
||||
self.emitByte(MulInt64)
|
||||
of "AddInt32":
|
||||
self.emitByte(AddInt32)
|
||||
of "SubInt32":
|
||||
self.emitByte(SubInt32)
|
||||
of "DivInt32":
|
||||
self.emitByte(DivInt32)
|
||||
of "MulInt32":
|
||||
self.emitByte(MulInt32)
|
||||
of "AddInt16":
|
||||
self.emitByte(AddInt16)
|
||||
of "SubInt16":
|
||||
self.emitByte(SubInt16)
|
||||
of "DivInt16":
|
||||
self.emitByte(DivInt16)
|
||||
of "MulInt16":
|
||||
self.emitByte(MulInt16)
|
||||
of "AddInt8":
|
||||
self.emitByte(AddInt8)
|
||||
of "SubInt8":
|
||||
self.emitByte(SubInt8)
|
||||
of "DivInt8":
|
||||
self.emitByte(DivInt8)
|
||||
of "MulInt8":
|
||||
self.emitByte(MulInt8)
|
||||
of "AddUInt64":
|
||||
self.emitByte(AddUInt64)
|
||||
of "SubUInt64":
|
||||
self.emitByte(SubUInt64)
|
||||
of "DivUInt64":
|
||||
self.emitByte(DivUInt64)
|
||||
of "MulUInt64":
|
||||
self.emitByte(MulUInt64)
|
||||
of "AddUInt32":
|
||||
self.emitByte(AddUInt32)
|
||||
of "SubUInt32":
|
||||
self.emitByte(SubUInt32)
|
||||
of "DivUInt32":
|
||||
self.emitByte(DivUInt32)
|
||||
of "MulUInt32":
|
||||
self.emitByte(MulUInt32)
|
||||
of "AddUInt16":
|
||||
self.emitByte(AddUInt16)
|
||||
of "SubUInt16":
|
||||
self.emitByte(SubUInt16)
|
||||
of "DivUInt16":
|
||||
self.emitByte(DivUInt16)
|
||||
of "MulUInt16":
|
||||
self.emitByte(MulUInt16)
|
||||
of "AddUInt8":
|
||||
self.emitByte(AddUInt8)
|
||||
of "SubUInt8":
|
||||
self.emitByte(SubUInt8)
|
||||
of "DivUInt8":
|
||||
self.emitByte(DivUInt8)
|
||||
of "MulUInt8":
|
||||
self.emitByte(MulUInt8)
|
||||
of "AddFloat64":
|
||||
self.emitByte(AddFloat64)
|
||||
of "SubFloat64":
|
||||
self.emitByte(SubFloat64)
|
||||
of "DivFloat64":
|
||||
self.emitByte(DivFloat64)
|
||||
of "MulFloat64":
|
||||
self.emitByte(MulFloat64)
|
||||
of "AddFloat32":
|
||||
self.emitByte(AddFloat32)
|
||||
of "SubFloat32":
|
||||
self.emitByte(SubFloat32)
|
||||
of "DivFloat32":
|
||||
self.emitByte(DivFloat32)
|
||||
of "MulFloat32":
|
||||
self.emitByte(MulFloat32)
|
||||
of "NegInt64":
|
||||
self.emitByte(NegInt64)
|
||||
of "NegInt32":
|
||||
self.emitByte(NegInt32)
|
||||
of "NegInt16":
|
||||
self.emitByte(NegInt16)
|
||||
of "NegInt8":
|
||||
self.emitByte(NegInt8)
|
||||
of "NegFloat64":
|
||||
self.emitByte(NegFloat64)
|
||||
of "NegFloat32":
|
||||
self.emitByte(NegFloat32)
|
||||
of "LogicalOr":
|
||||
self.expression(args[0])
|
||||
let jump = self.emitJump(JumpIfTrue)
|
||||
|
@ -1026,130 +996,8 @@ proc handleBuiltinFunction(self: Compiler, fn: Name, args: seq[Expression]) =
|
|||
var jump = self.emitJump(JumpIfFalseOrPop)
|
||||
self.expression(args[1])
|
||||
self.patchJump(jump)
|
||||
of "LessThanInt64":
|
||||
self.emitByte(LessThanInt64)
|
||||
of "GreaterThanInt64":
|
||||
self.emitByte(GreaterThanInt64)
|
||||
of "EqualInt64":
|
||||
self.emitByte(EqualInt64)
|
||||
of "NotEqualInt64":
|
||||
self.emitByte(NotEqualInt64)
|
||||
of "LessThanUInt64":
|
||||
self.emitByte(LessThanUInt64)
|
||||
of "GreaterThanUInt64":
|
||||
self.emitByte(GreaterThanUInt64)
|
||||
of "EqualUInt64":
|
||||
self.emitByte(EqualUInt64)
|
||||
of "NotEqualUInt64":
|
||||
self.emitByte(NotEqualUInt64)
|
||||
of "LessThanInt32":
|
||||
self.emitByte(LessThanInt32)
|
||||
of "GreaterThanInt32":
|
||||
self.emitByte(GreaterThanInt32)
|
||||
of "EqualInt32":
|
||||
self.emitByte(EqualInt32)
|
||||
of "NotEqualInt32":
|
||||
self.emitByte(NotEqualInt32)
|
||||
of "LessThanUInt32":
|
||||
self.emitByte(LessThanUInt32)
|
||||
of "GreaterThanUInt32":
|
||||
self.emitByte(GreaterThanUInt32)
|
||||
of "EqualUInt32":
|
||||
self.emitByte(EqualUInt32)
|
||||
of "NotEqualUInt32":
|
||||
self.emitByte(NotEqualUInt32)
|
||||
of "LessThanInt16":
|
||||
self.emitByte(LessThanInt16)
|
||||
of "GreaterThanInt16":
|
||||
self.emitByte(GreaterThanInt16)
|
||||
of "EqualInt16":
|
||||
self.emitByte(EqualInt16)
|
||||
of "NotEqualInt16":
|
||||
self.emitByte(NotEqualInt16)
|
||||
of "LessThanUInt16":
|
||||
self.emitByte(LessThanUInt16)
|
||||
of "GreaterThanUInt16":
|
||||
self.emitByte(GreaterThanUInt16)
|
||||
of "EqualUInt16":
|
||||
self.emitByte(EqualUInt16)
|
||||
of "NotEqualUInt16":
|
||||
self.emitByte(NotEqualUInt16)
|
||||
of "LessThanInt8":
|
||||
self.emitByte(LessThanInt8)
|
||||
of "GreaterThanInt8":
|
||||
self.emitByte(GreaterThanInt8)
|
||||
of "EqualInt8":
|
||||
self.emitByte(EqualInt8)
|
||||
of "NotEqualInt8":
|
||||
self.emitByte(NotEqualInt8)
|
||||
of "LessThanUInt8":
|
||||
self.emitByte(LessThanUInt8)
|
||||
of "GreaterThanUInt8":
|
||||
self.emitByte(GreaterThanUInt8)
|
||||
of "EqualUInt8":
|
||||
self.emitByte(EqualUInt8)
|
||||
of "NotEqualUInt8":
|
||||
self.emitByte(NotEqualUInt8)
|
||||
of "LessThanFloat64":
|
||||
self.emitByte(LessThanFloat64)
|
||||
of "GreaterThanFloat64":
|
||||
self.emitByte(GreaterThanFloat64)
|
||||
of "EqualFloat64":
|
||||
self.emitByte(EqualFloat64)
|
||||
of "NotEqualFloat64":
|
||||
self.emitByte(NotEqualFloat64)
|
||||
of "LessThanFloat32":
|
||||
self.emitByte(LessThanFloat32)
|
||||
of "GreaterThanFloat32":
|
||||
self.emitByte(GreaterThanFloat32)
|
||||
of "EqualFloat32":
|
||||
self.emitByte(EqualFloat32)
|
||||
of "NotEqualFloat32":
|
||||
self.emitByte(NotEqualFloat32)
|
||||
of "GreaterOrEqualInt64":
|
||||
self.emitByte(GreaterOrEqualInt64)
|
||||
of "LessOrEqualInt64":
|
||||
self.emitByte(LessOrEqualInt64)
|
||||
of "GreaterOrEqualUInt64":
|
||||
self.emitByte(GreaterOrEqualUInt64)
|
||||
of "LessOrEqualUInt64":
|
||||
self.emitByte(LessOrEqualUInt64)
|
||||
of "GreaterOrEqualInt32":
|
||||
self.emitByte(GreaterOrEqualInt32)
|
||||
of "LessOrEqualInt32":
|
||||
self.emitByte(LessOrEqualInt32)
|
||||
of "GreaterOrEqualUInt32":
|
||||
self.emitByte(GreaterOrEqualUInt32)
|
||||
of "LessOrEqualUInt32":
|
||||
self.emitByte(LessOrEqualUInt32)
|
||||
of "GreaterOrEqualInt16":
|
||||
self.emitByte(GreaterOrEqualInt16)
|
||||
of "LessOrEqualInt16":
|
||||
self.emitByte(LessOrEqualInt16)
|
||||
of "GreaterOrEqualUInt16":
|
||||
self.emitByte(GreaterOrEqualUInt16)
|
||||
of "LessOrEqualUInt16":
|
||||
self.emitByte(LessOrEqualUInt16)
|
||||
of "GreaterOrEqualInt8":
|
||||
self.emitByte(GreaterOrEqualInt8)
|
||||
of "LessOrEqualInt8":
|
||||
self.emitByte(LessOrEqualInt8)
|
||||
of "GreaterOrEqualUInt8":
|
||||
self.emitByte(GreaterOrEqualUInt8)
|
||||
of "LessOrEqualUInt8":
|
||||
self.emitByte(LessOrEqualUInt8)
|
||||
of "GreaterOrEqualFloat64":
|
||||
self.emitByte(GreaterOrEqualFloat64)
|
||||
of "LessOrEqualFloat64":
|
||||
self.emitByte(LessOrEqualFloat64)
|
||||
of "GreaterOrEqualFloat32":
|
||||
self.emitByte(GreaterOrEqualFloat32)
|
||||
of "LessOrEqualFloat32":
|
||||
self.emitByte(LessOrEqualFloat32)
|
||||
of "SysClock64":
|
||||
self.emitByte(SysClock64)
|
||||
else:
|
||||
self.error(&"unknown built-in: '{fn.valueType.builtinOp}'")
|
||||
self.error(&"unknown built-in: '{fn.valueType.builtinOp}'", fn.valueType.fun)
|
||||
|
||||
|
||||
proc generateCall(self: Compiler, fn: Name, args: seq[Expression], onStack: bool = false) =
|
||||
|
@ -1349,11 +1197,11 @@ proc assignment(self: Compiler, node: ASTNode) =
|
|||
let name = IdentExpr(node.name)
|
||||
var r = self.resolve(name)
|
||||
if r.isNil():
|
||||
self.error(&"assignment to undeclared name '{name.token.lexeme}'")
|
||||
self.error(&"assignment to undeclared name '{name.token.lexeme}'", name)
|
||||
elif r.isConst:
|
||||
self.error(&"cannot assign to '{name.token.lexeme}' (constant)")
|
||||
self.error(&"cannot assign to '{name.token.lexeme}' (constant)", name)
|
||||
elif r.isLet:
|
||||
self.error(&"cannot reassign '{name.token.lexeme}'")
|
||||
self.error(&"cannot reassign '{name.token.lexeme}'", name)
|
||||
self.expression(node.value)
|
||||
self.detectClosureVariable(r)
|
||||
if not r.isClosedOver:
|
||||
|
@ -1379,6 +1227,7 @@ proc beginScope(self: Compiler) =
|
|||
## Begins a new local scope by incrementing the current
|
||||
## scope's depth
|
||||
inc(self.scopeDepth)
|
||||
self.scopes.add(self.currentFunction)
|
||||
|
||||
|
||||
proc endScope(self: Compiler) =
|
||||
|
@ -1386,6 +1235,8 @@ proc endScope(self: Compiler) =
|
|||
if self.scopeDepth < 0:
|
||||
self.error("cannot call endScope with scopeDepth < 0 (This is an internal error and most likely a bug)")
|
||||
dec(self.scopeDepth)
|
||||
if self.scopeDepth > 0:
|
||||
discard self.scopes.pop()
|
||||
var names: seq[Name] = @[]
|
||||
var popCount = 0
|
||||
for name in self.names:
|
||||
|
@ -1450,7 +1301,7 @@ proc ifStmt(self: Compiler, node: IfStmt) =
|
|||
proc emitLoop(self: Compiler, begin: int) =
|
||||
## Emits a JumpBackwards instruction with the correct
|
||||
## jump offset
|
||||
let offset = self.chunk.code.len() - begin + 4
|
||||
let offset = self.chunk.code.high() - begin + 4
|
||||
if offset > 16777215:
|
||||
self.error("cannot jump more than 16777215 bytecode instructions")
|
||||
self.emitByte(JumpBackwards)
|
||||
|
@ -1461,8 +1312,8 @@ proc whileStmt(self: Compiler, node: WhileStmt) =
|
|||
## Compiles C-style while loops and
|
||||
## desugared C-style for loops
|
||||
self.check(node.condition, Type(kind: Bool))
|
||||
let start = self.chunk.code.high()
|
||||
self.expression(node.condition)
|
||||
let start = self.chunk.code.len()
|
||||
let jump = self.emitJump(JumpIfFalsePop)
|
||||
self.statement(node.body)
|
||||
self.patchJump(jump)
|
||||
|
@ -1666,14 +1517,45 @@ proc statement(self: Compiler, node: Statement) =
|
|||
## Compiles all statements
|
||||
case node.kind:
|
||||
of exprStmt:
|
||||
var expression = ExprStmt(node).expression
|
||||
let expression = ExprStmt(node).expression
|
||||
let kind = self.inferType(expression)
|
||||
self.expression(expression)
|
||||
if expression.kind == callExpr and self.inferType(CallExpr(expression).callee).returnType.isNil():
|
||||
# The expression has no type, so we don't have to
|
||||
# pop anything
|
||||
if kind.isNil():
|
||||
# The expression has no type and produces no value,
|
||||
# so we don't have to pop anything
|
||||
discard
|
||||
elif self.replMode:
|
||||
self.emitByte(PopRepl)
|
||||
case kind.kind:
|
||||
of Int64:
|
||||
self.emitByte(PrintInt64)
|
||||
of UInt64:
|
||||
self.emitByte(PrintUInt64)
|
||||
of Int32:
|
||||
self.emitByte(PrintInt32)
|
||||
of UInt32:
|
||||
self.emitByte(PrintInt32)
|
||||
of Int16:
|
||||
self.emitByte(PrintInt16)
|
||||
of UInt16:
|
||||
self.emitByte(PrintUInt16)
|
||||
of Int8:
|
||||
self.emitByte(PrintInt8)
|
||||
of UInt8:
|
||||
self.emitByte(PrintUInt8)
|
||||
of Float64:
|
||||
self.emitByte(PrintFloat64)
|
||||
of Float32:
|
||||
self.emitByte(PrintFloat32)
|
||||
of Bool:
|
||||
self.emitByte(PrintBool)
|
||||
of Nan:
|
||||
self.emitByte(PrintNan)
|
||||
of Inf:
|
||||
self.emitByte(PrintInf)
|
||||
of String:
|
||||
self.emitByte(PrintString)
|
||||
else:
|
||||
self.emitByte(PrintHex)
|
||||
else:
|
||||
self.emitByte(Pop)
|
||||
of NodeKind.ifStmt:
|
||||
|
@ -1734,7 +1616,7 @@ proc varDecl(self: Compiler, node: VarDecl) =
|
|||
self.expression(node.value)
|
||||
self.declareName(node, mutable=node.token.kind == TokenType.Var)
|
||||
self.emitByte(StoreVar)
|
||||
self.emitBytes((self.getStackPos(self.names[^1]) + 1).toTriple())
|
||||
self.emitBytes(self.getStackPos(self.names[^1]).toTriple())
|
||||
|
||||
|
||||
proc typeDecl(self: Compiler, node: TypeDecl) =
|
||||
|
@ -2047,4 +1929,3 @@ proc compileModule(self: Compiler, filename: string) =
|
|||
self.names.add(name)
|
||||
self.modules.incl(path)
|
||||
self.closedOver &= compiler.closedOver
|
||||
compiler.endScope()
|
|
@ -68,7 +68,7 @@ type
|
|||
# or 24 bit numbers that are defined statically
|
||||
# at compilation time into the bytecode
|
||||
|
||||
# These push a constant at position x in the
|
||||
# These push a constant at position x in the
|
||||
# constant table onto the stack
|
||||
LoadInt64 = 0u8,
|
||||
LoadUInt64,
|
||||
|
@ -90,114 +90,58 @@ type
|
|||
LoadNan,
|
||||
LoadInf,
|
||||
## Operations on primitive types
|
||||
GenericPrint,
|
||||
NegInt64, # No unsigned variants (how would you negate something that has no sign?)
|
||||
NegInt32,
|
||||
NegInt16,
|
||||
NegInt8,
|
||||
NegFloat32,
|
||||
NegFloat64,
|
||||
AddInt64,
|
||||
AddUInt64,
|
||||
AddInt32,
|
||||
AddUInt32
|
||||
AddInt16,
|
||||
AddUInt16,
|
||||
AddInt8,
|
||||
AddUInt8,
|
||||
SubInt64,
|
||||
SubUInt64,
|
||||
SubInt32,
|
||||
SubUInt32,
|
||||
SubInt16,
|
||||
SubUInt16,
|
||||
SubInt8,
|
||||
SubUInt8,
|
||||
MulInt64,
|
||||
MulUInt64,
|
||||
MulInt32,
|
||||
MulUInt32,
|
||||
MulInt16,
|
||||
MulUInt16,
|
||||
MulInt8,
|
||||
MulUInt8,
|
||||
DivInt64,
|
||||
DivUInt64,
|
||||
DivInt32,
|
||||
DivUInt32,
|
||||
DivInt16,
|
||||
DivUInt16,
|
||||
DivInt8,
|
||||
DivUInt8,
|
||||
Negate,
|
||||
NegateFloat64,
|
||||
NegateFloat32,
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
SignedDivide,
|
||||
AddFloat64,
|
||||
SubFloat64,
|
||||
DivFloat64,
|
||||
MulFloat64,
|
||||
SubtractFloat64,
|
||||
MultiplyFloat64,
|
||||
DivideFloat64,
|
||||
AddFloat32,
|
||||
SubFloat32,
|
||||
DivFloat32,
|
||||
MulFloat32,
|
||||
LessThanInt64,
|
||||
GreaterThanInt64,
|
||||
EqualInt64,
|
||||
NotEqualInt64,
|
||||
LessThanUInt64,
|
||||
GreaterThanUInt64,
|
||||
EqualUInt64,
|
||||
NotEqualUInt64,
|
||||
LessThanInt32,
|
||||
GreaterThanInt32,
|
||||
EqualInt32,
|
||||
NotEqualInt32,
|
||||
LessThanUInt32,
|
||||
GreaterThanUInt32,
|
||||
EqualUInt32,
|
||||
NotEqualUInt32,
|
||||
LessThanInt16,
|
||||
GreaterThanInt16,
|
||||
EqualInt16,
|
||||
NotEqualInt16,
|
||||
LessThanUInt16,
|
||||
GreaterThanUInt16,
|
||||
EqualUInt16,
|
||||
NotEqualUInt16,
|
||||
LessThanInt8,
|
||||
GreaterThanInt8,
|
||||
EqualInt8,
|
||||
NotEqualInt8,
|
||||
LessThanUInt8,
|
||||
GreaterThanUInt8,
|
||||
EqualUInt8,
|
||||
NotEqualUInt8,
|
||||
LessThanFloat64,
|
||||
GreaterThanFloat64,
|
||||
EqualFloat64,
|
||||
NotEqualFloat64,
|
||||
LessThanFloat32,
|
||||
GreaterThanFloat32,
|
||||
EqualFloat32,
|
||||
NotEqualFloat32,
|
||||
GreaterOrEqualInt64,
|
||||
LessOrEqualInt64,
|
||||
GreaterOrEqualUInt64,
|
||||
LessOrEqualUInt64,
|
||||
GreaterOrEqualInt32,
|
||||
LessOrEqualInt32,
|
||||
GreaterOrEqualUInt32,
|
||||
LessOrEqualUInt32,
|
||||
GreaterOrEqualInt16,
|
||||
LessOrEqualInt16,
|
||||
GreaterOrEqualUInt16,
|
||||
LessOrEqualUInt16,
|
||||
GreaterOrEqualInt8,
|
||||
LessOrEqualInt8,
|
||||
GreaterOrEqualUInt8,
|
||||
LessOrEqualUInt8,
|
||||
GreaterOrEqualFloat64,
|
||||
LessOrEqualFloat64,
|
||||
GreaterOrEqualFloat32,
|
||||
LessOrEqualFloat32,
|
||||
SysClock64,
|
||||
SubtractFloat32,
|
||||
MultiplyFloat32,
|
||||
DivideFloat32,
|
||||
Pow,
|
||||
SignedPow,
|
||||
Mod,
|
||||
SignedMod,
|
||||
PowFloat64,
|
||||
PowFloat32,
|
||||
ModFloat64,
|
||||
ModFloat32,
|
||||
LShift,
|
||||
RSHift,
|
||||
Xor,
|
||||
Or,
|
||||
And,
|
||||
Not,
|
||||
Equal,
|
||||
NotEqual,
|
||||
GreaterThan,
|
||||
LessThan,
|
||||
GreaterOrEqual,
|
||||
LessOrEqual,
|
||||
## Print opcodes
|
||||
PrintInt64,
|
||||
PrintUInt64,
|
||||
PrintInt32,
|
||||
PrintUInt32,
|
||||
PrintInt16,
|
||||
PrintUint16,
|
||||
PrintInt8,
|
||||
PrintUInt8,
|
||||
PrintFloat64,
|
||||
PrintFloat32,
|
||||
PrintHex,
|
||||
PrintBool,
|
||||
PrintNan,
|
||||
PrintInf,
|
||||
PrintString,
|
||||
## Basic stack operations
|
||||
Pop, # Pops an element off the stack and discards it
|
||||
PopRepl, # Same as Pop, but also prints the value of what's popped (used in REPL mode)
|
||||
|
@ -229,10 +173,11 @@ type
|
|||
## Coroutines
|
||||
Await, # Calls an asynchronous function
|
||||
## Misc
|
||||
Assert, # Raises an AssertionFailed exception if x is false
|
||||
NoOp, # Just a no-op
|
||||
PopC, # Pop off the call stack onto the operand stack
|
||||
PushC # Pop off the operand stack onto the call stack
|
||||
Assert, # Raises an AssertionFailed exception if x is false
|
||||
NoOp, # Just a no-op
|
||||
PopC, # Pop off the call stack onto the operand stack
|
||||
PushC, # Pop off the operand stack onto the call stack
|
||||
SysClock64 # Pushes the output of a monotonic clock on the stack
|
||||
|
||||
|
||||
# We group instructions by their operation/operand types for easier handling when debugging
|
||||
|
@ -241,60 +186,62 @@ type
|
|||
const simpleInstructions* = {Return, LoadNil,
|
||||
LoadTrue, LoadFalse,
|
||||
LoadNan, LoadInf,
|
||||
Pop, PopRepl, Raise,
|
||||
Pop, Raise,
|
||||
BeginTry, FinishTry, Yield,
|
||||
Await, NoOp, SetResult,
|
||||
PopC, PushC,
|
||||
AddInt64, AddUInt64, AddInt32,
|
||||
AddUInt32, AddInt16, AddUInt16,
|
||||
AddInt8, AddUInt8, SubInt64,
|
||||
SubUInt64, SubInt32, SubUInt32,
|
||||
SubInt16, SubUInt16, SubInt8,
|
||||
SubUInt8, MulInt64, MulUInt64,
|
||||
MulInt32, MulUInt32, MulInt16,
|
||||
MulUInt16, MulInt8, MulUInt8,
|
||||
DivInt64, DivUInt64, DivInt32,
|
||||
DivUInt32, DivInt16, DivUInt16,
|
||||
DivInt8, DivUInt8, AddFloat64,
|
||||
SubFloat64, DivFloat64, MulFloat64,
|
||||
AddFloat32, SubFloat32, DivFloat32,
|
||||
MulFloat32, NegFloat32, NegFloat64,
|
||||
LessThanInt64, SysClock64, GenericPrint,
|
||||
GreaterThanInt64, EqualInt64, NotEqualInt64,
|
||||
LessThanUInt64, GreaterThanUInt64, EqualUInt64,
|
||||
NotEqualUInt64, LessThanInt32, GreaterThanInt32,
|
||||
EqualInt32, NotEqualInt32, LessThanUInt32,
|
||||
GreaterThanUInt32, EqualUInt32, NotEqualUInt32,
|
||||
LessThanInt16, GreaterThanInt16, EqualInt16,
|
||||
NotEqualInt16, LessThanUInt16, GreaterThanUInt16,
|
||||
EqualUInt16, NotEqualUInt16, LessThanInt8,
|
||||
GreaterThanInt8,EqualInt8, NotEqualInt8,
|
||||
LessThanUInt8, GreaterThanUInt8, EqualUInt8,
|
||||
NotEqualUInt8, LessThanFloat64, GreaterThanFloat64,
|
||||
EqualFloat64,NotEqualFloat64, LessThanFloat32,
|
||||
GreaterThanFloat32, EqualFloat32, NotEqualFloat32,
|
||||
GreaterOrEqualInt64,
|
||||
LessOrEqualInt64,
|
||||
GreaterOrEqualUInt64,
|
||||
LessOrEqualUInt64,
|
||||
GreaterOrEqualInt32,
|
||||
LessOrEqualInt32,
|
||||
GreaterOrEqualUInt32,
|
||||
LessOrEqualUInt32,
|
||||
GreaterOrEqualInt16,
|
||||
LessOrEqualInt16,
|
||||
GreaterOrEqualUInt16,
|
||||
LessOrEqualUInt16,
|
||||
GreaterOrEqualInt8,
|
||||
LessOrEqualInt8,
|
||||
GreaterOrEqualUInt8,
|
||||
LessOrEqualUInt8,
|
||||
GreaterOrEqualFloat64,
|
||||
LessOrEqualFloat64,
|
||||
GreaterOrEqualFloat32,
|
||||
LessOrEqualFloat32,
|
||||
SysClock64, NegInt16
|
||||
}
|
||||
PopC, PushC, SysClock64,
|
||||
Negate,
|
||||
NegateFloat64,
|
||||
NegateFloat32,
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
SignedDivide,
|
||||
AddFloat64,
|
||||
SubtractFloat64,
|
||||
MultiplyFloat64,
|
||||
DivideFloat64,
|
||||
AddFloat32,
|
||||
SubtractFloat32,
|
||||
MultiplyFloat32,
|
||||
DivideFloat32,
|
||||
Pow,
|
||||
SignedPow,
|
||||
Mod,
|
||||
SignedMod,
|
||||
PowFloat64,
|
||||
PowFloat32,
|
||||
ModFloat64,
|
||||
ModFloat32,
|
||||
LShift,
|
||||
RSHift,
|
||||
Xor,
|
||||
Or,
|
||||
And,
|
||||
Not,
|
||||
Equal,
|
||||
NotEqual,
|
||||
GreaterThan,
|
||||
LessThan,
|
||||
GreaterOrEqual,
|
||||
LessOrEqual,
|
||||
PrintInt64,
|
||||
PrintUInt64,
|
||||
PrintInt32,
|
||||
PrintUInt32,
|
||||
PrintInt16,
|
||||
PrintUint16,
|
||||
PrintInt8,
|
||||
PrintUInt8,
|
||||
PrintFloat64,
|
||||
PrintFloat32,
|
||||
PrintHex,
|
||||
PrintBool,
|
||||
PrintNan,
|
||||
PrintInf,
|
||||
PrintString,
|
||||
}
|
||||
|
||||
# Constant instructions are instructions that operate on the bytecode constant table
|
||||
const constantInstructions* = {LoadInt64, LoadUInt64,
|
||||
|
|
34
src/main.nim
34
src/main.nim
|
@ -36,7 +36,7 @@ const debugRuntime {.booldefine.} = false
|
|||
|
||||
|
||||
|
||||
proc repl(vm: PeonVM = newPeonVM()) =
|
||||
proc repl =
|
||||
styledEcho fgMagenta, "Welcome into the peon REPL!"
|
||||
var
|
||||
keep = true
|
||||
|
@ -47,6 +47,7 @@ proc repl(vm: PeonVM = newPeonVM()) =
|
|||
tokenizer = newLexer()
|
||||
parser = newParser()
|
||||
compiler = newCompiler(replMode=true)
|
||||
vm = newPeonVM()
|
||||
debugger = newDebugger()
|
||||
serializer = newSerializer()
|
||||
editor = getLineEditor()
|
||||
|
@ -90,7 +91,7 @@ proc repl(vm: PeonVM = newPeonVM()) =
|
|||
break
|
||||
styledEcho fgGreen, "\t", $token
|
||||
echo ""
|
||||
tree = parser.parse(tokens, "stdin", tokenizer.getLines(), input)
|
||||
tree = parser.parse(tokens, "stdin", tokenizer.getLines(), input, persist=true)
|
||||
if tree.len() == 0:
|
||||
continue
|
||||
when debugParser:
|
||||
|
@ -135,7 +136,9 @@ proc repl(vm: PeonVM = newPeonVM()) =
|
|||
vm.run(serialized.chunk)
|
||||
except LexingError:
|
||||
input = ""
|
||||
let exc = LexingError(getCurrentException())
|
||||
var exc = LexingError(getCurrentException())
|
||||
if exc.lexeme == "":
|
||||
exc.line -= 1
|
||||
let relPos = exc.lexer.getRelPos(exc.line)
|
||||
let line = exc.lexer.getSource().splitLines()[exc.line - 1].strip(chars={'\n'})
|
||||
stderr.styledWriteLine(fgRed, "A fatal error occurred while parsing ", fgYellow, &"'{exc.file}'", fgRed, ", module ",
|
||||
|
@ -147,7 +150,9 @@ proc repl(vm: PeonVM = newPeonVM()) =
|
|||
input = ""
|
||||
let exc = ParseError(getCurrentException())
|
||||
let lexeme = exc.token.lexeme
|
||||
let lineNo = exc.token.line
|
||||
var lineNo = exc.token.line
|
||||
if exc.token.kind == EndOfFile:
|
||||
lineNo -= 1
|
||||
let relPos = exc.parser.getRelPos(lineNo)
|
||||
let fn = parser.getCurrentFunction()
|
||||
let line = exc.parser.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
|
||||
|
@ -162,7 +167,9 @@ proc repl(vm: PeonVM = newPeonVM()) =
|
|||
except CompileError:
|
||||
let exc = CompileError(getCurrentException())
|
||||
let lexeme = exc.node.token.lexeme
|
||||
let lineNo = exc.node.token.line
|
||||
var lineNo = exc.node.token.line
|
||||
if exc.node.token.kind == EndOfFile:
|
||||
lineNo -= 1
|
||||
let relPos = exc.compiler.getRelPos(lineNo)
|
||||
let line = exc.compiler.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
|
||||
var fn = exc.compiler.getCurrentFunction()
|
||||
|
@ -263,8 +270,9 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
|
|||
styledEcho fgCyan, "\n\nExecution step: "
|
||||
vm.run(serialized.chunk)
|
||||
except LexingError:
|
||||
input = ""
|
||||
let exc = LexingError(getCurrentException())
|
||||
var exc = LexingError(getCurrentException())
|
||||
if exc.lexeme == "":
|
||||
exc.line -= 1
|
||||
let relPos = exc.lexer.getRelPos(exc.line)
|
||||
let line = exc.lexer.getSource().splitLines()[exc.line - 1].strip(chars={'\n'})
|
||||
stderr.styledWriteLine(fgRed, "A fatal error occurred while parsing ", fgYellow, &"'{exc.file}'", fgRed, ", module ",
|
||||
|
@ -273,10 +281,11 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
|
|||
styledEcho fgBlue, "Source line: " , fgDefault, line
|
||||
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
|
||||
except ParseError:
|
||||
input = ""
|
||||
let exc = ParseError(getCurrentException())
|
||||
let lexeme = exc.token.lexeme
|
||||
let lineNo = exc.token.line
|
||||
var lineNo = exc.token.line
|
||||
if exc.token.kind == EndOfFile:
|
||||
lineNo -= 1
|
||||
let relPos = exc.parser.getRelPos(lineNo)
|
||||
let fn = parser.getCurrentFunction()
|
||||
let line = exc.parser.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
|
||||
|
@ -291,7 +300,9 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
|
|||
except CompileError:
|
||||
let exc = CompileError(getCurrentException())
|
||||
let lexeme = exc.node.token.lexeme
|
||||
let lineNo = exc.node.token.line
|
||||
var lineNo = exc.node.token.line
|
||||
if exc.node.token.kind == EndOfFile:
|
||||
lineNo -= 1
|
||||
let relPos = exc.compiler.getRelPos(lineNo)
|
||||
let line = exc.compiler.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
|
||||
var fn = exc.compiler.getCurrentFunction()
|
||||
|
@ -310,8 +321,7 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
|
|||
stderr.styledWriteLine(fgRed, "An error occurred while trying to read ", fgYellow, &"'{f}'", fgGreen, &": {getCurrentExceptionMsg()}")
|
||||
except OSError:
|
||||
stderr.styledWriteLine(fgRed, "An error occurred while trying to read ", fgYellow, &"'{f}'", fgGreen, &": {osErrorMsg(osLastError())} [errno {osLastError()}]")
|
||||
if interactive:
|
||||
repl(vm)
|
||||
|
||||
|
||||
|
||||
when isMainModule:
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
import std;
|
||||
import b;
|
||||
|
||||
print("a");
|
|
@ -1,3 +0,0 @@
|
|||
import std;
|
||||
|
||||
print("b");
|
|
@ -1,3 +1,7 @@
|
|||
# Tests simple calls
|
||||
import std;
|
||||
|
||||
|
||||
fn noReturn(n: int) {
|
||||
var n = n;
|
||||
var `17` = 17;
|
||||
|
@ -12,4 +16,4 @@ fn fooBar(a, b: int): int {
|
|||
|
||||
|
||||
noReturn(1);
|
||||
fooBar(1, 3);
|
||||
print(fooBar(1, 3)); # 1
|
|
@ -1,3 +1,7 @@
|
|||
# Tests closures
|
||||
import std;
|
||||
|
||||
|
||||
fn makeClosure(n: int): fn: int {
|
||||
let n = n; # Workaround
|
||||
fn inner: int {
|
||||
|
@ -8,4 +12,5 @@ fn makeClosure(n: int): fn: int {
|
|||
|
||||
|
||||
var closure = makeClosure(1)();
|
||||
closure;
|
||||
print(closure); # 1
|
||||
print(makeClosure(2)()); # 2
|
|
@ -1,4 +1,7 @@
|
|||
# Tests that comparisons work
|
||||
import std;
|
||||
|
||||
|
||||
# int64
|
||||
print(3 > 2); # true
|
||||
print(2 < 3); # true
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
# Tests operator dispatching
|
||||
|
||||
|
||||
operator `+`(a: int): int {
|
||||
return a;
|
||||
}
|
||||
|
|
42
tests/fib.pn
42
tests/fib.pn
|
@ -1,4 +1,40 @@
|
|||
import std;
|
||||
operator `<`(a, b: int): bool {
|
||||
#pragma[magic: "LessThan", pure]
|
||||
}
|
||||
|
||||
|
||||
operator `+`(a, b: int): int {
|
||||
#pragma[magic: "Add", pure]
|
||||
}
|
||||
|
||||
|
||||
operator `-`(a, b: int): int {
|
||||
#pragma[magic: "Subtract", pure]
|
||||
}
|
||||
|
||||
|
||||
operator `-`(a, b: float): float {
|
||||
#pragma[magic: "SubtractFloat64", pure]
|
||||
}
|
||||
|
||||
fn clock: float {
|
||||
#pragma[magic: "SysClock64"]
|
||||
}
|
||||
|
||||
|
||||
fn print(x: int) {
|
||||
#pragma[magic: "PrintInt64"]
|
||||
}
|
||||
|
||||
|
||||
fn print(x: float) {
|
||||
#pragma[magic: "PrintFloat64"]
|
||||
}
|
||||
|
||||
|
||||
fn print(x: string) {
|
||||
#pragma[magic: "PrintString"]
|
||||
}
|
||||
|
||||
|
||||
fn fib(n: int): int {
|
||||
|
@ -9,8 +45,8 @@ fn fib(n: int): int {
|
|||
}
|
||||
|
||||
|
||||
print("Computing the value of fib(25)");
|
||||
print("Computing the value of fib(30)");
|
||||
var x = clock();
|
||||
# print(fib(25));
|
||||
print(fib(30));
|
||||
print(clock() - x);
|
||||
print("Done!");
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
fn getFunction: fn (n: int): int {
|
||||
# Tests first class functions
|
||||
|
||||
import std;
|
||||
|
||||
|
||||
fn outer: fn (n: int): int {
|
||||
fn inner(n: int): int {
|
||||
return n;
|
||||
}
|
||||
|
@ -6,11 +11,19 @@ fn getFunction: fn (n: int): int {
|
|||
}
|
||||
|
||||
|
||||
operator `+`(a, b: int): int {
|
||||
#pragma[magic: "AddInt64"]
|
||||
|
||||
fn getAdder(a, b: int): fn: int64 {
|
||||
var x = a;
|
||||
var y = b;
|
||||
fn adder: int {
|
||||
return x + y;
|
||||
}
|
||||
return adder;
|
||||
}
|
||||
|
||||
|
||||
getFunction()(5);
|
||||
var x = getFunction;
|
||||
x()(3);
|
||||
print(outer()(1)); # 1
|
||||
var a = 1;
|
||||
var b = 2;
|
||||
var adder = getAdder(a, b);
|
||||
print(adder()); # 3
|
|
@ -1,5 +1,6 @@
|
|||
import std;
|
||||
# Tests var parameters
|
||||
|
||||
import std;
|
||||
|
||||
|
||||
operator `+=`(a: var int, b: int) {
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
# Tests nested calls
|
||||
import std;
|
||||
|
||||
|
||||
fn outer: int {
|
||||
fn inner: int {
|
||||
return 69420;
|
||||
|
@ -14,5 +18,5 @@ fn outerTwo(n: int): int {
|
|||
}
|
||||
|
||||
|
||||
outerTwo(5);
|
||||
outer();
|
||||
print(outerTwo(5));
|
||||
print(outer());
|
|
@ -1,6 +1,10 @@
|
|||
operator `+`(a, b: int): int {
|
||||
#pragma[magic: "AddInt64", pure]
|
||||
# Tests the creation and use of custom operators
|
||||
import std;
|
||||
|
||||
|
||||
operator `sum`(a, b: int): int {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
|
||||
2 + 2; # Works! :D
|
||||
print(2 sum 2); # 4
|
|
@ -1,9 +0,0 @@
|
|||
fn outer: fn (n: int): int {
|
||||
fn inner(n: int): int {
|
||||
return n;
|
||||
}
|
||||
return inner;
|
||||
}
|
||||
|
||||
|
||||
outer()(1);
|
|
@ -1,3 +1,4 @@
|
|||
# Tests local scopes
|
||||
import std;
|
||||
|
||||
|
||||
|
|
50
tests/std.pn
50
tests/std.pn
|
@ -9,37 +9,37 @@
|
|||
|
||||
|
||||
operator `+`*(a, b: int): int {
|
||||
#pragma[magic: "AddInt64", pure]
|
||||
#pragma[magic: "SignedAdd", pure]
|
||||
}
|
||||
|
||||
|
||||
operator `+`*(a, b: uint64): uint64 {
|
||||
#pragma[magic: "AddUInt64", pure]
|
||||
#pragma[magic: "Add", pure]
|
||||
}
|
||||
|
||||
|
||||
operator `+`*(a, b: int32): int32 {
|
||||
#pragma[magic: "AddInt32", pure]
|
||||
#pragma[magic: "SignedAdd", pure]
|
||||
}
|
||||
|
||||
|
||||
operator `+`*(a, b: uint32): uint32 {
|
||||
#pragma[magic: "AddUInt32", pure]
|
||||
#pragma[magic: "Add", pure]
|
||||
}
|
||||
|
||||
|
||||
operator `+`*(a, b: int16): int16 {
|
||||
#pragma[magic: "AddInt16", pure]
|
||||
#pragma[magic: "SignedAdd", pure]
|
||||
}
|
||||
|
||||
|
||||
operator `+`*(a, b: uint16): uint16 {
|
||||
#pragma[magic: "AddUInt16", pure]
|
||||
#pragma[magic: "Add", pure]
|
||||
}
|
||||
|
||||
|
||||
operator `+`*(a, b: int8): int8 {
|
||||
#pragma[magic: "AddInt8", pure]
|
||||
#pragma[magic: "Add", pure]
|
||||
}
|
||||
|
||||
|
||||
|
@ -109,7 +109,7 @@ operator `-`*(a, b: float32): float32 {
|
|||
|
||||
|
||||
operator `*`*(a, b: int): int {
|
||||
#pragma[magic: "MulInt64", pure]
|
||||
#pragma[magic: "SignedMultiply", pure]
|
||||
}
|
||||
|
||||
|
||||
|
@ -119,7 +119,7 @@ operator `*`*(a, b: uint64): uint64 {
|
|||
|
||||
|
||||
operator `*`*(a, b: int32): int32 {
|
||||
#pragma[magic: "MulInt32", pure]
|
||||
#pragma[magic: "SignedMultiply", pure]
|
||||
}
|
||||
|
||||
|
||||
|
@ -129,7 +129,7 @@ operator `*`*(a, b: uint32): uint32 {
|
|||
|
||||
|
||||
operator `*`*(a, b: int16): int16 {
|
||||
#pragma[magic: "MulInt16", pure]
|
||||
#pragma[magic: "SignedMultiply", pure]
|
||||
}
|
||||
|
||||
|
||||
|
@ -139,7 +139,7 @@ operator `*`*(a, b: uint16): uint16 {
|
|||
|
||||
|
||||
operator `*`*(a, b: int8): int8 {
|
||||
#pragma[magic: "MulInt8", pure]
|
||||
#pragma[magic: "SignedMultiply", pure]
|
||||
}
|
||||
|
||||
|
||||
|
@ -208,6 +208,11 @@ operator `/`*(a, b: float32): float32 {
|
|||
}
|
||||
|
||||
|
||||
operator `**`*(a, b: int64): int64 {
|
||||
#pragma[magic: "PowInt64", pure]
|
||||
}
|
||||
|
||||
|
||||
# Comparison operators
|
||||
|
||||
operator `>`*(a, b: int): bool {
|
||||
|
@ -508,11 +513,22 @@ operator `<=`*(a, b: float32): bool {
|
|||
#pragma[magic: "LessOrEqualFloat32", pure]
|
||||
}
|
||||
|
||||
# Assignment operator
|
||||
# TODO
|
||||
#operator `=`[T](a: var T, b: T) {
|
||||
# #pragma[magic: "GenericAssign"]
|
||||
#}
|
||||
|
||||
operator `and`*(a, b: bool): bool {
|
||||
#pragma[magic: "LogicalAnd", pure]
|
||||
}
|
||||
|
||||
|
||||
operator `or`*(a, b: bool): bool {
|
||||
#pragma[magic: "LogicalOr", pure]
|
||||
}
|
||||
|
||||
|
||||
# Assignment operators
|
||||
|
||||
operator `=`[T: Any](a: var T, b: T) {
|
||||
#pragma[magic: "GenericAssign"]
|
||||
}
|
||||
|
||||
|
||||
# Some useful builtins
|
||||
|
@ -541,4 +557,4 @@ fn print*(x: string) {
|
|||
|
||||
fn print*(x: bool) {
|
||||
#pragma[magic: "GenericPrint"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
operator `+`(a, b: int): int {
|
||||
#pragma[magic: "AddInt64", pure]
|
||||
}
|
||||
|
||||
|
||||
fn print(x: int) {
|
||||
#pragma[magic: "GenericPrint"]
|
||||
}
|
||||
|
||||
|
||||
fn getAdder(a, b: int): fn: int64 {
|
||||
var x = a;
|
||||
var y = b;
|
||||
fn adder: int {
|
||||
return x + y;
|
||||
}
|
||||
return adder;
|
||||
}
|
||||
|
||||
|
||||
var a = 1;
|
||||
var b = 2;
|
||||
var adder = getAdder(a, b);
|
||||
print(adder());
|
Loading…
Reference in New Issue