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