Move towards unboxed types in the Peon VM

This commit is contained in:
Mattia Giambirtone 2022-08-17 17:31:15 +02:00
parent fc14cfec2d
commit 19a089f4a2
21 changed files with 663 additions and 971 deletions

View File

@ -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

View File

@ -11,46 +11,57 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
## The Peon runtime environment
{.push checks:off.} # The VM is a critical point where checks are deleterious
import std/monotimes
import std/math
import types
import ../config
import ../frontend/meta/bytecode
import ../util/multibyte
export types
import strutils
when DEBUG_TRACE_VM:
import strutils
import strformat
import std/strformat
type
PeonVM* = ref object
## The Peon Virtual Machine
calls: seq[PeonObject] # Our call stack
operands: seq[PeonObject] # Our operand stack
ip: uint32 # Instruction pointer
cache: array[6, PeonObject] # Singletons cache
## The Peon Virtual Machine.
## Note how the only data
## type we handle here is
## a 64-bit unsigned integer:
## This is to allow the use
## of unboxed primitive types.
## For more complex types, the
## value represents a pointer to
## some stack- or heap-allocated
## object. The VM has no concept
## of type by itself and it relies
## on the compiler to produce the
## correct results
ip: uint64 # Instruction pointer
chunk: Chunk # Piece of bytecode to execute
frames: seq[int] # Stores the bottom of stack frames
closedOver: seq[PeonObject] # Stores variables that do not have stack semantics
results: seq[PeonObject] # Stores function's results
calls: seq[uint64] # Our call stack
operands: seq[uint64] # Our operand stack
cache: array[6, uint64] # Singletons cache
frames: seq[uint64] # Stores the bottom of stack frames
closedOver: seq[uint64] # Stores variables that do not have stack semantics
results: seq[uint64] # Stores function's results (return values)
proc initCache*(self: PeonVM) =
## Initializes the VM's
## singletons cache
self.cache[0] = PeonObject(kind: Nil)
self.cache[1] = PeonObject(kind: Bool, boolean: true)
self.cache[2] = PeonObject(kind: Bool, boolean: false)
self.cache[3] = PeonObject(kind: ObjectKind.Inf, positive: true)
self.cache[4] = PeonObject(kind: ObjectKind.Inf, positive: false)
self.cache[5] = PeonObject(kind: ObjectKind.Nan)
self.cache[0] = 0x0 # Nil
self.cache[1] = 0x1 # True
self.cache[2] = 0x2 # False
self.cache[3] = 0x3 # Positive inf
self.cache[4] = 0x4 # Negative inf
self.cache[5] = 0x5 # NaN
proc newPeonVM*: PeonVM =
@ -59,26 +70,26 @@ proc newPeonVM*: PeonVM =
new(result)
result.ip = 0
result.frames = @[]
result.calls = newSeq[PeonObject]()
result.operands = newSeq[PeonObject]()
result.calls = newSeq[uint64]()
result.operands = newSeq[uint64]()
result.initCache()
# Getters for singleton types (they are cached!)
proc getNil*(self: PeonVM): PeonObject {.inline.} = self.cache[0]
proc getNil*(self: PeonVM): uint64 {.inline.} = self.cache[2]
proc getBool*(self: PeonVM, value: bool): PeonObject {.inline.} =
proc getBool*(self: PeonVM, value: bool): uint64 {.inline.} =
if value:
return self.cache[1]
return self.cache[2]
return self.cache[0]
proc getInf*(self: PeonVM, positive: bool): PeonObject {.inline.} =
proc getInf*(self: PeonVM, positive: bool): uint64 {.inline.} =
if positive:
return self.cache[3]
return self.cache[4]
proc getNan*(self: PeonVM): PeonObject {.inline.} = self.cache[5]
proc getNan*(self: PeonVM): uint64 {.inline.} = self.cache[5]
# Thanks to nim's *genius* idea of making x !> y a template
@ -96,58 +107,68 @@ proc `!>=`[T](a, b: T): auto {.inline, used.} =
b <= a
# Stack primitives. Note: all stack accessing that goes
# through the get(c)/set(c)/peek(c) wrappers is frame-relative,
# Stack primitives. Note: all accesses to the call stack
# that go through the getc/setc wrappers is frame-relative,
# meaning that the index is added to the current stack frame's
# bottom to obtain an absolute stack index.
proc push(self: PeonVM, obj: PeonObject) =
## Pushes a Peon object onto the
# bottom to obtain an absolute stack index
{.push inline.}
proc push(self: PeonVM, obj: uint64) =
## Pushes a value object onto the
## operand stack
self.operands.add(obj)
proc pop(self: PeonVM): PeonObject =
## Pops a Peon object off the
## operand stack. The object
## is returned
proc pop(self: PeonVM): uint64 =
## Pops a value off the
## operand stack and
## returns it
return self.operands.pop()
proc peek(self: PeonVM, distance: int = 0): PeonObject =
## Returns the Peon object at the
proc peekb(self: PeonVM, distance: BackwardsIndex = ^1): uint64 =
## Returns the value at the
## given (backwards) distance from the top of
## the operand stack without consuming it
return self.operands[distance]
proc peek(self: PeonVM, distance: int = 0): uint64 =
## Returns the value at the
## given distance from the top of
## the operand stack without consuming it
if distance < 0:
return self.peekb(^(-distance))
return self.operands[self.operands.high() + distance]
proc pushc(self: PeonVM, val: PeonObject) =
## Pushes a new object to the
proc pushc(self: PeonVM, val: uint64) =
## Pushes a value to the
## call stack
self.calls.add(val)
proc popc(self: PeonVM): PeonObject =
## Pops an object off the call
proc popc(self: PeonVM): uint64 =
## Pops a value off the call
## stack and returns it
return self.calls.pop()
proc peekc(self: PeonVM, distance: int = 0): PeonObject {.used.} =
## Returns the Peon object at the
proc peekc(self: PeonVM, distance: int = 0): uint64 {.used.} =
## Returns the value at the
## given distance from the top of
## the call stack without consuming it
return self.calls[self.calls.high() + distance]
proc getc(self: PeonVM, idx: int): PeonObject =
proc getc(self: PeonVM, idx: uint): uint64 =
## Accessor method that abstracts
## indexing our call stack through stack
## frames
return self.calls[idx + self.frames[^1]]
proc setc(self: PeonVM, idx: int, val: PeonObject) =
proc setc(self: PeonVM, idx: uint, val: uint64) =
## Setter method that abstracts
## indexing our call stack through stack
## frames
@ -156,7 +177,7 @@ proc setc(self: PeonVM, idx: int, val: PeonObject) =
# Byte-level primitives to read and decode
# bytecode
proc readByte(self: PeonVM): uint8 =
proc readByte(self: PeonVM): uint8 =
## Reads a single byte from the
## bytecode and returns it as an
## unsigned 8 bit integer
@ -164,7 +185,7 @@ proc readByte(self: PeonVM): uint8 =
return self.chunk.code[self.ip - 1]
proc readShort(self: PeonVM): uint16 =
proc readShort(self: PeonVM): uint16 =
## Reads two bytes from the
## bytecode and returns them
## as an unsigned 16 bit
@ -172,7 +193,7 @@ proc readShort(self: PeonVM): uint16 =
return [self.readByte(), self.readByte()].fromDouble()
proc readLong(self: PeonVM): uint32 =
proc readLong(self: PeonVM): uint32 =
## Reads three bytes from the
## bytecode and returns them
## as an unsigned 32 bit
@ -182,7 +203,7 @@ proc readLong(self: PeonVM): uint32 =
return uint32([self.readByte(), self.readByte(), self.readByte()].fromTriple())
proc readUInt(self: PeonVM): uint32 =
proc readUInt(self: PeonVM): uint32 =
## Reads three bytes from the
## bytecode and returns them
## as an unsigned 32 bit
@ -193,122 +214,100 @@ proc readUInt(self: PeonVM): uint32 =
# Functions to read primitives from the chunk's
# constants table
proc constReadInt64(self: PeonVM, idx: int): PeonObject =
proc constReadInt64(self: PeonVM, idx: int): int64 =
## Reads a constant from the
## chunk's constant table and
## returns a Peon object. Assumes
## the constant is an Int64
## returns it as an int64
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3],
self.chunk.consts[idx + 4], self.chunk.consts[idx + 5],
self.chunk.consts[idx + 6], self.chunk.consts[idx + 7],
]
result = PeonObject(kind: Int64)
copyMem(result.long.addr, arr.addr, sizeof(arr))
copyMem(result.addr, arr.addr, sizeof(arr))
proc constReadUInt64(self: PeonVM, idx: int): PeonObject =
proc constReadUInt64(self: PeonVM, idx: int): uint64 =
## Reads a constant from the
## chunk's constant table and
## returns a Peon object. Assumes
## the constant is an UInt64
## returns it as an uint64
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3],
self.chunk.consts[idx + 4], self.chunk.consts[idx + 5],
self.chunk.consts[idx + 6], self.chunk.consts[idx + 7],
]
result = PeonObject(kind: UInt64)
copyMem(result.uLong.addr, arr.addr, sizeof(arr))
copyMem(result.addr, arr.addr, sizeof(arr))
proc constReadUInt32(self: PeonVM, idx: int): PeonObject =
proc constReadUInt32(self: PeonVM, idx: int): uint32 =
## Reads a constant from the
## chunk's constant table and
## returns a Peon object. Assumes
## the constant is an UInt32
## returns it as an int32
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]]
result = PeonObject(kind: UInt32)
copyMem(result.uInt.addr, arr.addr, sizeof(arr))
copyMem(result.addr, arr.addr, sizeof(arr))
proc constReadInt32(self: PeonVM, idx: int): PeonObject =
proc constReadInt32(self: PeonVM, idx: int): int32 =
## Reads a constant from the
## chunk's constant table and
## returns a Peon object. Assumes
## the constant is an Int32
## returns it as an uint32
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]]
result = PeonObject(kind: Int32)
copyMem(result.`int`.addr, arr.addr, sizeof(arr))
copyMem(result.addr, arr.addr, sizeof(arr))
proc constReadInt16(self: PeonVM, idx: int): PeonObject =
proc constReadInt16(self: PeonVM, idx: int): int16 =
## Reads a constant from the
## chunk's constant table and
## returns a Peon object. Assumes
## the constant is an Int16
## returns it as an int16
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1]]
result = PeonObject(kind: Int16)
copyMem(result.short.addr, arr.addr, sizeof(arr))
copyMem(result.addr, arr.addr, sizeof(arr))
proc constReadUInt16(self: PeonVM, idx: int): PeonObject =
proc constReadUInt16(self: PeonVM, idx: int): uint16 =
## Reads a constant from the
## chunk's constant table and
## returns a Peon object. Assumes
## the constant is an UInt16
## returns it as an uint16
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1]]
result = PeonObject(kind: UInt16)
copyMem(result.uShort.addr, arr.addr, sizeof(arr))
copyMem(result.addr, arr.addr, sizeof(arr))
proc constReadInt8(self: PeonVM, idx: int): PeonObject =
proc constReadInt8(self: PeonVM, idx: int): int8 =
## Reads a constant from the
## chunk's constant table and
## returns a Peon object. Assumes
## the constant is an Int8
result = PeonObject(kind: Int8, tiny: int8(self.chunk.consts[idx]))
## returns it as an int8
result = int8(self.chunk.consts[idx])
proc constReadUInt8(self: PeonVM, idx: int): PeonObject =
proc constReadUInt8(self: PeonVM, idx: int): uint8 =
## Reads a constant from the
## chunk's constant table and
## returns a Peon object. Assumes
## the constant is an UInt8
result = PeonObject(kind: UInt8, uTiny: self.chunk.consts[idx])
## returns it as an uint8
result = self.chunk.consts[idx]
proc constReadString(self: PeonVM, size: int, idx: int): PeonObject =
proc constReadFloat32(self: PeonVM, idx: int): float32 =
## Reads a constant from the
## chunk's constant table and
## returns a Peon object. Assumes
## the constant is a string
result = PeonObject(kind: String, str: self.chunk.consts[idx..<idx + size].fromBytes())
proc constReadFloat32(self: PeonVM, idx: int): PeonObject =
## Reads a constant from the
## chunk's constant table and
## returns a Peon object. Assumes
## the constant is a Float32
## returns it as a float32
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]]
result = PeonObject(kind: Float32)
copyMem(result.halfFloat.addr, arr.addr, sizeof(arr))
copyMem(result.addr, arr.addr, sizeof(arr))
proc constReadFloat64(self: PeonVM, idx: int): PeonObject =
proc constReadFloat64(self: PeonVM, idx: int): float =
## Reads a constant from the
## chunk's constant table and
## returns a Peon object. Assumes
## the constant is a Float64
## returns it as a float
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3],
self.chunk.consts[idx + 4], self.chunk.consts[idx + 5],
self.chunk.consts[idx + 6], self.chunk.consts[idx + 7]]
result = PeonObject(kind: Float64)
copyMem(result.`float`.addr, arr.addr, sizeof(arr))
copyMem(result.addr, arr.addr, sizeof(arr))
{.pop.}
proc dispatch*(self: PeonVM) =
@ -345,62 +344,68 @@ proc dispatch*(self: PeonVM) =
of LoadInf:
self.push(self.getInf(true))
of LoadInt64:
self.push(self.constReadInt64(int(self.readLong())))
self.push(uint64(self.constReadInt64(int(self.readLong()))))
of LoadUInt64:
self.push(self.constReadUInt64(int(self.readLong())))
self.push(uint64(self.constReadUInt64(int(self.readLong()))))
of LoadUInt32:
self.push(self.constReadUInt32(int(self.readLong())))
self.push(uint64(self.constReadUInt32(int(self.readLong()))))
of LoadInt32:
self.push(self.constReadInt32(int(self.readLong())))
self.push(uint64(self.constReadInt32(int(self.readLong()))))
of LoadInt16:
self.push(self.constReadInt16(int(self.readLong())))
self.push(uint64(self.constReadInt16(int(self.readLong()))))
of LoadUInt16:
self.push(self.constReadUInt16(int(self.readLong())))
self.push(uint64(self.constReadUInt16(int(self.readLong()))))
of LoadInt8:
self.push(self.constReadInt8(int(self.readLong())))
self.push(uint64(self.constReadInt8(int(self.readLong()))))
of LoadUInt8:
self.push(self.constReadUInt8(int(self.readLong())))
self.push(uint64(self.constReadUInt8(int(self.readLong()))))
of LoadString:
self.push(self.constReadString(int(self.readLong()), int(self.readLong())))
# TODO: Use constReadString with own memory manager
# Strings are broken rn!!
let size = int(self.readLong())
let idx = int(self.readLong())
var str = self.chunk.consts[idx..<idx + size].fromBytes()
self.push(cast[uint64](str.addr))
# We cast instead of converting because, unlike with integers,
# we don't want nim to touch any of the bits of the underlying
# value!
of LoadFloat32:
self.push(self.constReadFloat32(int(self.readLong())))
self.push(cast[uint64](self.constReadFloat32(int(self.readLong()))))
of LoadFloat64:
self.push(self.constReadFloat64(int(self.readLong())))
self.push(cast[uint64](self.constReadFloat64(int(self.readLong()))))
of LoadFunction:
# Loads a function onto the operand stack by reading its
# instruction pointer
self.push(PeonObject(kind: Function, ip: self.readLong()))
# Loads a function address onto the operand stack
self.push(uint64(self.readLong()))
of LoadReturnAddress:
# Loads a 32-bit unsigned integer onto the operand stack.
# Used to load function return addresses
self.push(PeonObject(kind: UInt32, uInt: self.readUInt()))
self.push(uint64(self.readUInt()))
of Call:
# Calls a function. The calling convention for peon
# functions is pretty simple: the first item in the
# frame is a function object which contains the new
# instruction pointer to jump to, followed by a 32-bit
# return address. After that, all arguments and locals
# follow. Note that, due to how the stack works, all
# arguments before the call are in the reverse order in
# which they are passed to the function
# Calls a peon function. The calling convention here
# is pretty simple: the first value in the frame is
# the new instruction pointer to jump to, then a
# 32-bit return address follows. After that, all
# arguments and locals follow. Note that, due to
# how the stack works, all arguments before the call
# are in the reverse order in which they are passed
# to the function
var argc {.used.} = self.readLong().int
let retAddr = self.peek(-argc) # Return address
let fnObj = self.peek(-argc - 1) # Function object
self.ip = fnObj.ip
self.pushc(fnObj)
let retAddr = self.peek(-argc - 1) # Return address
let jmpAddr = self.peek(-argc - 2) # Function address
self.ip = jmpAddr
self.pushc(jmpAddr)
self.pushc(retAddr)
# Creates a new result slot for the
# function's return value
self.results.add(self.getNil())
# Creates a new call frame
self.frames.add(self.calls.len() - 2)
self.frames.add(uint64(self.calls.len() - 2))
# Loads the arguments onto the stack
for _ in 0..<argc:
self.pushc(self.pop())
# Pops the function object and
# return address off the operand
# stack since they're not needed
# there anymore
# Pops the function and return address
# off the operand stack since they're
# not needed there anymore
discard self.pop()
discard self.pop()
# TODO: Use the frame's initial size once
@ -416,12 +421,12 @@ proc dispatch*(self: PeonVM) =
# in a hidden function, so this
# will also exit the VM if we're
# at the end of the program
while self.calls.len() !> self.frames[^1] + 2:
while self.calls.len().uint64 !> self.frames[^1] + 2'u64:
# Discards the function's local variables,
# if there is any
discard self.popc()
let ret = self.popc() # Return address
discard self.popc() # Function object
discard self.popc() # Function address
if self.readByte() == 1:
# Function is non-void!
self.push(self.results.pop())
@ -444,8 +449,10 @@ proc dispatch*(self: PeonVM) =
of StoreVar:
# Stores the value at the top of the operand stack
# into the given call stack index
let idx = int(self.readLong())
if idx + self.frames[^1] <= self.calls.high():
let idx = self.readLong()
when DEBUG_TRACE_VM:
assert idx.int - self.calls.high() <= 1, "StoreVar index is bigger than the length of the call stack"
if idx + self.frames[^1] <= self.calls.high().uint:
self.setc(idx, self.pop())
else:
self.pushc(self.pop())
@ -466,7 +473,7 @@ proc dispatch*(self: PeonVM) =
of LoadVar:
# Pushes a variable onto the operand
# stack
self.push(self.getc(int(self.readLong())))
self.push(self.getc(self.readLong()))
of NoOp:
# Does nothing
continue
@ -488,257 +495,164 @@ proc dispatch*(self: PeonVM) =
discard self.pop()
continue
echo self.pop()
of GenericPrint:
# Prints the peon object at the top
# of the operand stack
echo self.pop()
of PopN:
# Pops N elements off the call stack
for _ in 0..<int(self.readShort()):
discard self.popc()
# Jump opcodes
of Jump:
# Absolute jump
self.ip = self.readLong()
of JumpForwards:
# Relative, forward-jump
self.ip += self.readLong()
of JumpBackwards:
# Relative, backward-jump
self.ip -= self.readLong()
of JumpIfFalse:
if not self.peek().boolean:
# Conditional positive jump
if not self.peek().bool:
self.ip += self.readLong()
of JumpIfTrue:
if self.peek().boolean:
# Conditional positive jump
if self.peek().bool:
self.ip += self.readLong()
of JumpIfFalsePop:
let ip = self.readLong()
if not self.peek().boolean:
if not self.peek().bool:
self.ip += ip
discard self.pop()
of JumpIfFalseOrPop:
if not self.peek().boolean:
if not self.peek().bool:
self.ip += self.readLong()
else:
discard self.pop()
# Built-in operations on primitive types
of AddInt64:
self.push(PeonObject(kind: Int64, long: self.pop().long + self.pop().long))
of SubInt64:
self.push(PeonObject(kind: Int64, long: self.pop().long - self.pop().long))
of MulInt64:
self.push(PeonObject(kind: Int64, long: self.pop().long * self.pop().long))
of DivInt64:
self.push(PeonObject(kind: Int64, long: self.pop().long div self.pop().long))
of AddUInt64:
self.push(PeonObject(kind: UInt64, uLong: self.pop().uLong + self.pop().uLong))
of SubUInt64:
self.push(PeonObject(kind: UInt64, uLong: self.pop().uLong - self.pop().uLong))
of MulUInt64:
self.push(PeonObject(kind: UInt64, uLong: self.pop().uLong * self.pop().uLong))
of DivUInt64:
self.push(PeonObject(kind: UInt64, uLong: self.pop().uLong div self.pop().uLong))
of AddInt32:
self.push(PeonObject(kind: Int32, `int`: self.pop().`int` + self.pop().`int`))
of SubInt32:
self.push(PeonObject(kind: Int32, `int`: self.pop().`int` - self.pop().`int`))
of MulInt32:
self.push(PeonObject(kind: Int32, `int`: self.pop().`int` * self.pop().`int`))
of DivInt32:
self.push(PeonObject(kind: Int32, `int`: self.pop().`int` div self.pop().`int`))
of AddUInt32:
self.push(PeonObject(kind: UInt32, uInt: self.pop().uInt + self.pop().uInt))
of SubUInt32:
self.push(PeonObject(kind: UInt32, uInt: self.pop().uInt - self.pop().uInt))
of MulUInt32:
self.push(PeonObject(kind: UInt32, uInt: self.pop().uInt * self.pop().uInt))
of DivUInt32:
self.push(PeonObject(kind: UInt32, uInt: self.pop().uInt div self.pop().uInt))
of AddInt16:
self.push(PeonObject(kind: Int16, short: self.pop().short + self.pop().short))
of SubInt16:
self.push(PeonObject(kind: Int16, short: self.pop().short - self.pop().short))
of MulInt16:
self.push(PeonObject(kind: Int16, short: self.pop().short * self.pop().short))
of DivInt16:
self.push(PeonObject(kind: Int16, short: self.pop().short div self.pop().short))
of AddUInt16:
self.push(PeonObject(kind: UInt16, uShort: self.pop().uShort + self.pop().uShort))
of SubUInt16:
self.push(PeonObject(kind: UInt16, uShort: self.pop().uShort - self.pop().uShort))
of MulUInt16:
self.push(PeonObject(kind: UInt16, uShort: self.pop().uShort * self.pop().uShort))
of DivUInt16:
self.push(PeonObject(kind: UInt16, uShort: self.pop().uShort div self.pop().uShort))
of AddInt8:
self.push(PeonObject(kind: Int8, tiny: self.pop().tiny + self.pop().tiny))
of SubInt8:
self.push(PeonObject(kind: Int8, tiny: self.pop().tiny - self.pop().tiny))
of MulInt8:
self.push(PeonObject(kind: Int8, tiny: self.pop().tiny * self.pop().tiny))
of DivInt8:
self.push(PeonObject(kind: Int8, tiny: self.pop().tiny div self.pop().tiny))
of AddUInt8:
self.push(PeonObject(kind: UInt8, uTiny: self.pop().uTiny + self.pop().uTiny))
of SubUInt8:
self.push(PeonObject(kind: UInt8, uTiny: self.pop().uTiny - self.pop().uTiny))
of MulUInt8:
self.push(PeonObject(kind: UInt8, uTiny: self.pop().uTiny * self.pop().uTiny))
of DivUInt8:
self.push(PeonObject(kind: UInt8, uTiny: self.pop().uTiny div self.pop().uTiny))
# Built-in operations on primitive types.
# Note: for operations where the order of
# the operands matters, we don't need to
# swap the order of the calls to pop: this
# is because operators are handled like peon
# functions, which means the arguments are
# already reversed on the stack when we
# execute the instruction
of Negate:
self.push(uint64(-int64(self.pop())))
of NegateFloat64:
self.push(cast[uint64](-cast[float](self.pop())))
of NegateFloat32:
self.push(cast[uint64](-cast[float32](self.pop())))
of Add:
self.push(self.pop() + self.pop())
of Subtract:
self.push(self.pop() - self.pop())
of Multiply:
self.push(self.pop() * self.pop())
of Divide:
self.push(self.pop() div self.pop())
of SignedDivide:
self.push(uint64(int64(self.pop()) div int64(self.pop())))
of AddFloat64:
self.push(PeonObject(kind: Float64, `float`: self.pop().`float` + self.pop().`float`))
of SubFloat64:
self.push(PeonObject(kind: Float64, `float`: self.pop().`float` - self.pop().`float`))
of MulFloat64:
self.push(PeonObject(kind: Float64, `float`: self.pop().`float` * self.pop().`float`))
of DivFloat64:
self.push(PeonObject(kind: Float64, `float`: self.pop().`float` / self.pop().`float`))
self.push(cast[uint64](cast[float](self.pop()) + cast[float](self.pop())))
of SubtractFloat64:
self.push(cast[uint64](cast[float](self.pop()) - cast[float](self.pop())))
of MultiplyFloat64:
self.push(cast[uint64](cast[float](self.pop()) * cast[float](self.pop())))
of DivideFloat64:
self.push(cast[uint64](cast[float](self.pop()) / cast[float](self.pop())))
of AddFloat32:
self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat + self.pop().halfFloat))
of SubFloat32:
self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat - self.pop().halfFloat))
of MulFloat32:
self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat * self.pop().halfFloat))
of DivFloat32:
self.push(PeonObject(kind: Float32, halfFloat: self.pop().halfFloat / self.pop().halfFloat))
of NegInt64:
self.push(PeonObject(kind: Int64, long: -self.pop().long))
of NegInt32:
self.push(PeonObject(kind: Int32, `int`: -self.pop().`int`))
of NegInt16:
self.push(PeonObject(kind: Int16, short: -self.pop().short))
of NegInt8:
self.push(PeonObject(kind: Int8, tiny: -self.pop().tiny))
of NegFloat64:
self.push(PeonObject(kind: Float64, `float`: -self.pop().`float`))
of NegFloat32:
self.push(PeonObject(kind: Float32, halfFloat: -self.pop().halfFloat))
of LessThanInt64:
self.push(PeonObject(kind: Bool, boolean: self.pop().long < self.pop().long))
of GreaterThanInt64:
self.push(PeonObject(kind: Bool, boolean: self.pop().long !> self.pop().long))
of EqualInt64:
self.push(PeonObject(kind: Bool, boolean: self.pop().long == self.pop().long))
of NotEqualInt64:
self.push(PeonObject(kind: Bool, boolean: self.pop().long != self.pop().long))
of LessThanUInt64:
self.push(PeonObject(kind: Bool, boolean: self.pop().uLong < self.pop().uLong))
of GreaterThanUInt64:
self.push(PeonObject(kind: Bool, boolean: self.pop().uLong !> self.pop().uLong))
of EqualUInt64:
self.push(PeonObject(kind: Bool, boolean: self.pop().uLong == self.pop().uLong))
of NotEqualUInt64:
self.push(PeonObject(kind: Bool, boolean: self.pop().uLong != self.pop().uLong))
of LessThanInt32:
self.push(PeonObject(kind: Bool, boolean: self.pop().`int` < self.pop().`int`))
of GreaterThanInt32:
self.push(PeonObject(kind: Bool, boolean: self.pop().`int` !> self.pop().`int`))
of EqualInt32:
self.push(PeonObject(kind: Bool, boolean: self.pop().`int` == self.pop().`int`))
of NotEqualInt32:
self.push(PeonObject(kind: Bool, boolean: self.pop().`int` != self.pop().`int`))
of LessThanUInt32:
self.push(PeonObject(kind: Bool, boolean: self.pop().uInt < self.pop().uInt))
of GreaterThanUInt32:
self.push(PeonObject(kind: Bool, boolean: self.pop().uInt !> self.pop().uInt))
of EqualUInt32:
self.push(PeonObject(kind: Bool, boolean: self.pop().uInt == self.pop().uInt))
of NotEqualUInt32:
self.push(PeonObject(kind: Bool, boolean: self.pop().uInt != self.pop().uInt))
of LessThanInt16:
self.push(PeonObject(kind: Bool, boolean: self.pop().short < self.pop().short))
of GreaterThanInt16:
self.push(PeonObject(kind: Bool, boolean: self.pop().short !> self.pop().short))
of EqualInt16:
self.push(PeonObject(kind: Bool, boolean: self.pop().short == self.pop().short))
of NotEqualInt16:
self.push(PeonObject(kind: Bool, boolean: self.pop().short != self.pop().short))
of LessThanUInt16:
self.push(PeonObject(kind: Bool, boolean: self.pop().uShort < self.pop().uShort))
of GreaterThanUInt16:
self.push(PeonObject(kind: Bool, boolean: self.pop().uShort !> self.pop().uShort))
of EqualUInt16:
self.push(PeonObject(kind: Bool, boolean: self.pop().uShort == self.pop().uShort))
of NotEqualUInt16:
self.push(PeonObject(kind: Bool, boolean: self.pop().uShort != self.pop().uShort))
of LessThanInt8:
self.push(PeonObject(kind: Bool, boolean: self.pop().tiny < self.pop().tiny))
of GreaterThanInt8:
self.push(PeonObject(kind: Bool, boolean: self.pop().tiny !> self.pop().tiny))
of EqualInt8:
self.push(PeonObject(kind: Bool, boolean: self.pop().tiny == self.pop().tiny))
of NotEqualInt8:
self.push(PeonObject(kind: Bool, boolean: self.pop().tiny != self.pop().tiny))
of LessThanUInt8:
self.push(PeonObject(kind: Bool, boolean: self.pop().uTiny < self.pop().uTiny))
of GreaterThanUInt8:
self.push(PeonObject(kind: Bool, boolean: self.pop().uTiny !> self.pop().uTiny))
of EqualUInt8:
self.push(PeonObject(kind: Bool, boolean: self.pop().uTiny == self.pop().uTiny))
of NotEqualUInt8:
self.push(PeonObject(kind: Bool, boolean: self.pop().uTiny != self.pop().uTiny))
of LessThanFloat64:
self.push(PeonObject(kind: Bool, boolean: self.pop().`float` < self.pop().`float`))
of GreaterThanFloat64:
self.push(PeonObject(kind: Bool, boolean: self.pop().`float` !> self.pop().`float`))
of EqualFloat64:
self.push(PeonObject(kind: Bool, boolean: self.pop().`float` == self.pop().`float`))
of NotEqualFLoat64:
self.push(PeonObject(kind: Bool, boolean: self.pop().`float` != self.pop().`float`))
of LessThanFloat32:
self.push(PeonObject(kind: Bool, boolean: self.pop().halfFloat < self.pop().halfFloat))
of GreaterThanFloat32:
self.push(PeonObject(kind: Bool, boolean: self.pop().halfFloat !> self.pop().halfFloat))
of EqualFloat32:
self.push(PeonObject(kind: Bool, boolean: self.pop().halfFloat == self.pop().halfFloat))
of NotEqualFloat32:
self.push(PeonObject(kind: Bool, boolean: self.pop().halfFloat != self.pop().halfFloat))
of GreaterOrEqualInt64:
self.push(PeonObject(kind: Bool, boolean: self.pop().long !>= self.pop().long))
of LessOrEqualInt64:
self.push(PeonObject(kind: Bool, boolean: self.pop().long <= self.pop().long))
of GreaterOrEqualUInt64:
self.push(PeonObject(kind: Bool, boolean: self.pop().uLong !>= self.pop().uLong))
of LessOrEqualUInt64:
self.push(PeonObject(kind: Bool, boolean: self.pop().uLong <= self.pop().uLong))
of GreaterOrEqualInt32:
self.push(PeonObject(kind: Bool, boolean: self.pop().`int` !>= self.pop().`int`))
of LessOrEqualInt32:
self.push(PeonObject(kind: Bool, boolean: self.pop().`int` <= self.pop().`int`))
of GreaterOrEqualUInt32:
self.push(PeonObject(kind: Bool, boolean: self.pop().uInt !>= self.pop().uInt))
of LessOrEqualUInt32:
self.push(PeonObject(kind: Bool, boolean: self.pop().uInt <= self.pop().uInt))
of GreaterOrEqualInt16:
self.push(PeonObject(kind: Bool, boolean: self.pop().short !>= self.pop().short))
of LessOrEqualInt16:
self.push(PeonObject(kind: Bool, boolean: self.pop().short <= self.pop().short))
of GreaterOrEqualUInt16:
self.push(PeonObject(kind: Bool, boolean: self.pop().uShort !>= self.pop().uShort))
of LessOrEqualUInt16:
self.push(PeonObject(kind: Bool, boolean: self.pop().uShort <= self.pop().uShort))
of GreaterOrEqualInt8:
self.push(PeonObject(kind: Bool, boolean: self.pop().tiny !>= self.pop().tiny))
of LessOrEqualInt8:
self.push(PeonObject(kind: Bool, boolean: self.pop().tiny <= self.pop().tiny))
of GreaterOrEqualUInt8:
self.push(PeonObject(kind: Bool, boolean: self.pop().uTiny !>= self.pop().uTiny))
of LessOrEqualUInt8:
self.push(PeonObject(kind: Bool, boolean: self.pop().uTiny <= self.pop().uTiny))
of GreaterOrEqualFloat64:
self.push(PeonObject(kind: Bool, boolean: self.pop().`float` !>= self.pop().`float`))
of LessOrEqualFloat64:
self.push(PeonObject(kind: Bool, boolean: self.pop().`float` <= self.pop().`float`))
of GreaterOrEqualFloat32:
self.push(PeonObject(kind: Bool, boolean: self.pop().halfFloat !>= self.pop().halfFloat))
of LessOrEqualFloat32:
self.push(PeonObject(kind: Bool, boolean: self.pop().halfFloat <= self.pop().halfFloat))
self.push(cast[uint64](cast[float32](self.pop()) + cast[float32](self.pop())))
of SubtractFloat32:
self.push(cast[uint64](cast[float32](self.pop()) - cast[float32](self.pop())))
of MultiplyFloat32:
self.push(cast[uint64](cast[float32](self.pop()) * cast[float32](self.pop())))
of DivideFloat32:
self.push(cast[uint64](cast[float32](self.pop()) / cast[float32](self.pop())))
of Pow:
self.push(uint64(self.pop() ^ self.pop()))
of SignedPow:
self.push(uint64(int64(self.pop()) ^ int64(self.pop())))
of PowFloat64:
self.push(cast[uint64](pow(cast[float](self.pop()), cast[float](self.pop()))))
of PowFloat32:
self.push(cast[uint64](pow(cast[float](self.pop()), cast[float](self.pop()))))
of Mod:
self.push(uint64(self.pop() mod self.pop()))
of SignedMod:
self.push(uint64(int64(self.pop()) mod int64(self.pop())))
of ModFloat64:
self.push(cast[uint64](floorMod(cast[float](self.pop()), cast[float](self.pop()))))
of ModFloat32:
self.push(cast[uint64](floorMod(cast[float](self.pop()), cast[float](self.pop()))))
of LShift:
self.push(self.pop() shl self.pop())
of RShift:
self.push(self.pop() shr self.pop())
of Xor:
self.push(self.pop() xor self.pop())
of Not:
self.push(not self.pop())
of And:
self.push(self.pop() and self.pop())
of Equal:
self.push(self.getBool(self.pop() == self.pop()))
of NotEqual:
self.push(self.getBool(self.pop() != self.pop()))
of GreaterThan:
self.push(self.getBool(self.pop() !> self.pop()))
of LessThan:
self.push(self.getBool(self.pop() < self.pop()))
of GreaterOrEqual:
self.push(self.getBool(self.pop() !>= self.pop()))
of LessOrEqual:
self.push(self.getBool(self.pop() <= self.pop()))
of PrintInt64:
# Prints the value at the top of the stack
# as an int64
echo int64(self.pop())
of PrintUInt64:
# Prints the value at the top of the stack
echo self.pop()
of PrintInt32:
echo int32(self.pop())
of PrintUInt32:
echo uint32(self.pop())
of PrintInt16:
echo int16(self.pop())
of PrintUInt16:
echo uint16(self.pop())
of PrintInt8:
echo int8(self.pop())
of PrintUInt8:
echo uint8(self.pop())
of PrintFloat32:
echo cast[float32](self.pop())
of PrintFloat64:
echo cast[float](self.pop())
of PrintHex:
# Prints the value at the top of the stack
# as a hexadecimal integer
echo "0x" & self.pop().toHex().strip(chars={'0'})
of PrintBool:
if self.pop().bool:
echo "true"
else:
echo "false"
of PrintInf:
if self.pop() == 0x3:
echo "-inf"
else:
echo "inf"
of PrintNan:
echo "nan"
of PrintString:
echo cast[ptr string](self.pop())[] # TODO
of SysClock64:
# Pushes the value of a monotonic clock
# as a 64 bit float onto the operand stack.
# Used to track system runtime accurately,
# but cannot be converted to a date. The number
# onto the operand stack. This can be used
# to track system time accurately, but it
# cannot be converted to a date. The number
# is in seconds
self.push(PeonObject(kind: Float64, `float`: getMonoTime().ticks().float() / 1_000_000_000))
self.push(cast[uint64](getMonoTime().ticks().float() / 1_000_000_000))
else:
discard
@ -751,3 +665,5 @@ proc run*(self: PeonVM, chunk: Chunk) =
self.operands = @[]
self.ip = 0
self.dispatch()
{.pop.}

View File

@ -21,10 +21,10 @@ when HEAP_GROW_FACTOR <= 1:
{.fatal: "Heap growth factor must be > 1".}
const PEON_VERSION* = (major: 0, minor: 1, patch: 0)
const PEON_RELEASE* = "alpha"
const PEON_COMMIT_HASH* = "e90ac2f4eb7ea5b95467bae5439cd21d2e89a812"
const PEON_COMMIT_HASH* = "b273cd744883458a4a6354a0cc5f4f5d0f560c31"
when len(PEON_COMMIT_HASH) != 40:
{.fatal: "The git commit hash must be exactly 40 characters long".}
const PEON_BRANCH* = "master"
const PEON_BRANCH* = "unboxed-types"
when len(PEON_BRANCH) > 255:
{.fatal: "The git branch name's length must be less than or equal to 255 characters".}
const DEBUG_TRACE_VM* = debugVM # Traces VM execution

View File

@ -183,6 +183,8 @@ type
source: string
# Currently imported modules
modules: HashSet[string]
# TODO
scopes: seq[Type]
CompileError* = ref object of PeonException
compiler*: Compiler
node*: ASTNode
@ -372,6 +374,9 @@ proc patchJump(self: Compiler, offset: int) =
var jump: int = self.chunk.code.len() - offset
if jump > 16777215:
self.error("cannot jump more than 16777215 instructions")
# We subtract 4 because that's the size of our jump instruction
# which the caller of patchJump doesn't take into account (and
# that's by design)
let offsetArray = (jump - 4).toTriple()
self.chunk.code[offset + 1] = offsetArray[0]
self.chunk.code[offset + 2] = offsetArray[1]
@ -395,13 +400,13 @@ proc resolve(self: Compiler, name: IdentExpr,
return nil
proc getStackPos(self: Compiler, name: Name, depth: int = self.scopeDepth): int =
proc getStackPos(self: Compiler, name: Name): int =
## Returns the predicted call stack position of a given name, relative
## to the current frame
var found = false
result = 2
for variable in self.names:
if variable.isFunDecl:
if variable.isFunDecl or variable.valueType.kind in {CustomType, Generic}:
continue
if name == variable:
found = true
@ -455,12 +460,13 @@ proc detectClosureVariable(self: Compiler, name: var Name, depth: int = self.sco
## unpredictably or crash
if name.isNil() or name.depth == 0 or name.isClosedOver:
return
elif name.depth < depth:
# Ding! The given name is closed over: we need to
# change the dummy Jump instruction that self.declareName
elif name.depth < depth and self.scopes[name.depth - 1] != self.scopes[depth - 1]:
# Ding! The given name is closed over in another function:
# we need to change the Jump instruction that self.declareName
# put in place for us into a StoreClosure. We also update
# the name's isClosedOver field so that self.identifier()
# can emit a LoadClosure instruction instead of a LoadVar
# once this name is referenced in the future
if not name.isFunctionArgument:
# We handle closed-over function arguments later
self.closedOver.add(name)
@ -535,13 +541,13 @@ proc toIntrinsic(name: string): Type =
## otherwise
if name in ["int", "int64", "i64"]:
return Type(kind: Int64)
elif name in ["uint64", "u64"]:
elif name in ["uint64", "u64", "uint"]:
return Type(kind: UInt64)
elif name in ["int32", "i32"]:
return Type(kind: Int32)
elif name in ["uint32", "u32"]:
return Type(kind: UInt32)
elif name in ["int16", "i16"]:
elif name in ["int16", "i16", "short"]:
return Type(kind: Int16)
elif name in ["uint16", "u16"]:
return Type(kind: UInt16)
@ -553,9 +559,9 @@ proc toIntrinsic(name: string): Type =
return Type(kind: Float64)
elif name in ["f32", "float32"]:
return Type(kind: Float32)
elif name == "byte":
elif name in ["byte", "b"]:
return Type(kind: Byte)
elif name == "char":
elif name in ["char", "c"]:
return Type(kind: Char)
elif name == "nan":
return Type(kind: Nan)
@ -802,12 +808,12 @@ proc check(self: Compiler, term: Expression, kind: Type) =
let k = self.inferType(term)
if k.isNil():
if term.kind == identExpr:
self.error(&"reference to undeclared name '{term.token.lexeme}'")
self.error(&"reference to undeclared name '{term.token.lexeme}'", term)
elif term.kind == callExpr and CallExpr(term).callee.kind == identExpr:
self.error(&"call to undeclared function '{CallExpr(term).callee.token.lexeme}'")
self.error(&"expecting value of type '{self.typeToStr(kind)}', but expression has no type")
self.error(&"call to undeclared function '{CallExpr(term).callee.token.lexeme}'", term)
self.error(&"expecting value of type '{self.typeToStr(kind)}', but expression has no type", term)
elif not self.compareTypes(k, kind):
self.error(&"expecting value of type '{self.typeToStr(kind)}', got '{self.typeToStr(k)}' instead")
self.error(&"expecting value of type '{self.typeToStr(kind)}', got '{self.typeToStr(k)}' instead", term)
@ -921,101 +927,65 @@ proc handleBuiltinFunction(self: Compiler, fn: Name, args: seq[Expression]) =
self.expression(args[0])
elif len(args) == 1:
self.expression(args[0])
const codes: Table[string, OpCode] = {"Negate": Negate,
"NegateFloat32": NegateFloat32,
"NegateFloat64": NegateFloat64,
"Add": Add,
"Subtract": Subtract,
"Divide": Divide,
"Multiply": Multiply,
"SignedDivide": SignedDivide,
"AddFloat64": AddFloat64,
"SubtractFloat64": SubtractFloat64,
"DivideFloat64": DivideFloat64,
"MultiplyFloat64": MultiplyFloat64,
"AddFloat32": AddFloat32,
"SubtractFloat32": SubtractFloat32,
"DivideFloat32": DivideFloat32,
"MultiplyFloat32": MultiplyFloat32,
"Pow": Pow,
"SignedPow": SignedPow,
"PowFloat32": PowFloat32,
"PowFloat64": PowFloat64,
"Mod": Mod,
"SignedMod": SignedMod,
"ModFloat32": ModFloat32,
"ModFloat64": ModFloat64,
"Or": Or,
"And": And,
"Xor": Xor,
"Not": Not,
"LShift": LShift,
"RShift": RShift,
"Equal": Equal,
"NotEqual": NotEqual,
"LessThan": LessThan,
"GreaterThan": GreaterThan,
"LessOrEqual": LessOrEqual,
"GreaterOrEqual": GreaterOrEqual,
"PrintInt64": PrintInt64,
"PrintUInt64": PrintUInt64,
"PrintInt32": PrintInt32,
"PrintUInt32": PrintUInt32,
"PrintInt16": PrintInt16,
"PrintUInt16": PrintUInt16,
"PrintInt8": PrintInt8,
"PrintUInt8": PrintUInt8,
"PrintFloat64": PrintFloat64,
"PrintFloat32": PrintFloat32,
"PrintHex": PrintHex,
"PrintBool": PrintBool,
"PrintNan": PrintNan,
"PrintInf": PrintInf,
"PrintString": PrintString,
"SysClock64": SysClock64
}.to_table()
if fn.valueType.builtinOp in codes:
self.emitByte(codes[fn.valueType.builtinOp])
return
# Some builtin operations are slightly more complex
# so we handle them separately
case fn.valueType.builtinOp:
of "GenericPrint":
self.emitByte(GenericPrint)
of "AddInt64":
self.emitByte(AddInt64)
of "SubInt64":
self.emitByte(SubInt64)
of "DivInt64":
self.emitByte(DivInt64)
of "MulInt64":
self.emitByte(MulInt64)
of "AddInt32":
self.emitByte(AddInt32)
of "SubInt32":
self.emitByte(SubInt32)
of "DivInt32":
self.emitByte(DivInt32)
of "MulInt32":
self.emitByte(MulInt32)
of "AddInt16":
self.emitByte(AddInt16)
of "SubInt16":
self.emitByte(SubInt16)
of "DivInt16":
self.emitByte(DivInt16)
of "MulInt16":
self.emitByte(MulInt16)
of "AddInt8":
self.emitByte(AddInt8)
of "SubInt8":
self.emitByte(SubInt8)
of "DivInt8":
self.emitByte(DivInt8)
of "MulInt8":
self.emitByte(MulInt8)
of "AddUInt64":
self.emitByte(AddUInt64)
of "SubUInt64":
self.emitByte(SubUInt64)
of "DivUInt64":
self.emitByte(DivUInt64)
of "MulUInt64":
self.emitByte(MulUInt64)
of "AddUInt32":
self.emitByte(AddUInt32)
of "SubUInt32":
self.emitByte(SubUInt32)
of "DivUInt32":
self.emitByte(DivUInt32)
of "MulUInt32":
self.emitByte(MulUInt32)
of "AddUInt16":
self.emitByte(AddUInt16)
of "SubUInt16":
self.emitByte(SubUInt16)
of "DivUInt16":
self.emitByte(DivUInt16)
of "MulUInt16":
self.emitByte(MulUInt16)
of "AddUInt8":
self.emitByte(AddUInt8)
of "SubUInt8":
self.emitByte(SubUInt8)
of "DivUInt8":
self.emitByte(DivUInt8)
of "MulUInt8":
self.emitByte(MulUInt8)
of "AddFloat64":
self.emitByte(AddFloat64)
of "SubFloat64":
self.emitByte(SubFloat64)
of "DivFloat64":
self.emitByte(DivFloat64)
of "MulFloat64":
self.emitByte(MulFloat64)
of "AddFloat32":
self.emitByte(AddFloat32)
of "SubFloat32":
self.emitByte(SubFloat32)
of "DivFloat32":
self.emitByte(DivFloat32)
of "MulFloat32":
self.emitByte(MulFloat32)
of "NegInt64":
self.emitByte(NegInt64)
of "NegInt32":
self.emitByte(NegInt32)
of "NegInt16":
self.emitByte(NegInt16)
of "NegInt8":
self.emitByte(NegInt8)
of "NegFloat64":
self.emitByte(NegFloat64)
of "NegFloat32":
self.emitByte(NegFloat32)
of "LogicalOr":
self.expression(args[0])
let jump = self.emitJump(JumpIfTrue)
@ -1026,130 +996,8 @@ proc handleBuiltinFunction(self: Compiler, fn: Name, args: seq[Expression]) =
var jump = self.emitJump(JumpIfFalseOrPop)
self.expression(args[1])
self.patchJump(jump)
of "LessThanInt64":
self.emitByte(LessThanInt64)
of "GreaterThanInt64":
self.emitByte(GreaterThanInt64)
of "EqualInt64":
self.emitByte(EqualInt64)
of "NotEqualInt64":
self.emitByte(NotEqualInt64)
of "LessThanUInt64":
self.emitByte(LessThanUInt64)
of "GreaterThanUInt64":
self.emitByte(GreaterThanUInt64)
of "EqualUInt64":
self.emitByte(EqualUInt64)
of "NotEqualUInt64":
self.emitByte(NotEqualUInt64)
of "LessThanInt32":
self.emitByte(LessThanInt32)
of "GreaterThanInt32":
self.emitByte(GreaterThanInt32)
of "EqualInt32":
self.emitByte(EqualInt32)
of "NotEqualInt32":
self.emitByte(NotEqualInt32)
of "LessThanUInt32":
self.emitByte(LessThanUInt32)
of "GreaterThanUInt32":
self.emitByte(GreaterThanUInt32)
of "EqualUInt32":
self.emitByte(EqualUInt32)
of "NotEqualUInt32":
self.emitByte(NotEqualUInt32)
of "LessThanInt16":
self.emitByte(LessThanInt16)
of "GreaterThanInt16":
self.emitByte(GreaterThanInt16)
of "EqualInt16":
self.emitByte(EqualInt16)
of "NotEqualInt16":
self.emitByte(NotEqualInt16)
of "LessThanUInt16":
self.emitByte(LessThanUInt16)
of "GreaterThanUInt16":
self.emitByte(GreaterThanUInt16)
of "EqualUInt16":
self.emitByte(EqualUInt16)
of "NotEqualUInt16":
self.emitByte(NotEqualUInt16)
of "LessThanInt8":
self.emitByte(LessThanInt8)
of "GreaterThanInt8":
self.emitByte(GreaterThanInt8)
of "EqualInt8":
self.emitByte(EqualInt8)
of "NotEqualInt8":
self.emitByte(NotEqualInt8)
of "LessThanUInt8":
self.emitByte(LessThanUInt8)
of "GreaterThanUInt8":
self.emitByte(GreaterThanUInt8)
of "EqualUInt8":
self.emitByte(EqualUInt8)
of "NotEqualUInt8":
self.emitByte(NotEqualUInt8)
of "LessThanFloat64":
self.emitByte(LessThanFloat64)
of "GreaterThanFloat64":
self.emitByte(GreaterThanFloat64)
of "EqualFloat64":
self.emitByte(EqualFloat64)
of "NotEqualFloat64":
self.emitByte(NotEqualFloat64)
of "LessThanFloat32":
self.emitByte(LessThanFloat32)
of "GreaterThanFloat32":
self.emitByte(GreaterThanFloat32)
of "EqualFloat32":
self.emitByte(EqualFloat32)
of "NotEqualFloat32":
self.emitByte(NotEqualFloat32)
of "GreaterOrEqualInt64":
self.emitByte(GreaterOrEqualInt64)
of "LessOrEqualInt64":
self.emitByte(LessOrEqualInt64)
of "GreaterOrEqualUInt64":
self.emitByte(GreaterOrEqualUInt64)
of "LessOrEqualUInt64":
self.emitByte(LessOrEqualUInt64)
of "GreaterOrEqualInt32":
self.emitByte(GreaterOrEqualInt32)
of "LessOrEqualInt32":
self.emitByte(LessOrEqualInt32)
of "GreaterOrEqualUInt32":
self.emitByte(GreaterOrEqualUInt32)
of "LessOrEqualUInt32":
self.emitByte(LessOrEqualUInt32)
of "GreaterOrEqualInt16":
self.emitByte(GreaterOrEqualInt16)
of "LessOrEqualInt16":
self.emitByte(LessOrEqualInt16)
of "GreaterOrEqualUInt16":
self.emitByte(GreaterOrEqualUInt16)
of "LessOrEqualUInt16":
self.emitByte(LessOrEqualUInt16)
of "GreaterOrEqualInt8":
self.emitByte(GreaterOrEqualInt8)
of "LessOrEqualInt8":
self.emitByte(LessOrEqualInt8)
of "GreaterOrEqualUInt8":
self.emitByte(GreaterOrEqualUInt8)
of "LessOrEqualUInt8":
self.emitByte(LessOrEqualUInt8)
of "GreaterOrEqualFloat64":
self.emitByte(GreaterOrEqualFloat64)
of "LessOrEqualFloat64":
self.emitByte(LessOrEqualFloat64)
of "GreaterOrEqualFloat32":
self.emitByte(GreaterOrEqualFloat32)
of "LessOrEqualFloat32":
self.emitByte(LessOrEqualFloat32)
of "SysClock64":
self.emitByte(SysClock64)
else:
self.error(&"unknown built-in: '{fn.valueType.builtinOp}'")
self.error(&"unknown built-in: '{fn.valueType.builtinOp}'", fn.valueType.fun)
proc generateCall(self: Compiler, fn: Name, args: seq[Expression], onStack: bool = false) =
@ -1349,11 +1197,11 @@ proc assignment(self: Compiler, node: ASTNode) =
let name = IdentExpr(node.name)
var r = self.resolve(name)
if r.isNil():
self.error(&"assignment to undeclared name '{name.token.lexeme}'")
self.error(&"assignment to undeclared name '{name.token.lexeme}'", name)
elif r.isConst:
self.error(&"cannot assign to '{name.token.lexeme}' (constant)")
self.error(&"cannot assign to '{name.token.lexeme}' (constant)", name)
elif r.isLet:
self.error(&"cannot reassign '{name.token.lexeme}'")
self.error(&"cannot reassign '{name.token.lexeme}'", name)
self.expression(node.value)
self.detectClosureVariable(r)
if not r.isClosedOver:
@ -1379,6 +1227,7 @@ proc beginScope(self: Compiler) =
## Begins a new local scope by incrementing the current
## scope's depth
inc(self.scopeDepth)
self.scopes.add(self.currentFunction)
proc endScope(self: Compiler) =
@ -1386,6 +1235,8 @@ proc endScope(self: Compiler) =
if self.scopeDepth < 0:
self.error("cannot call endScope with scopeDepth < 0 (This is an internal error and most likely a bug)")
dec(self.scopeDepth)
if self.scopeDepth > 0:
discard self.scopes.pop()
var names: seq[Name] = @[]
var popCount = 0
for name in self.names:
@ -1450,7 +1301,7 @@ proc ifStmt(self: Compiler, node: IfStmt) =
proc emitLoop(self: Compiler, begin: int) =
## Emits a JumpBackwards instruction with the correct
## jump offset
let offset = self.chunk.code.len() - begin + 4
let offset = self.chunk.code.high() - begin + 4
if offset > 16777215:
self.error("cannot jump more than 16777215 bytecode instructions")
self.emitByte(JumpBackwards)
@ -1461,8 +1312,8 @@ proc whileStmt(self: Compiler, node: WhileStmt) =
## Compiles C-style while loops and
## desugared C-style for loops
self.check(node.condition, Type(kind: Bool))
let start = self.chunk.code.high()
self.expression(node.condition)
let start = self.chunk.code.len()
let jump = self.emitJump(JumpIfFalsePop)
self.statement(node.body)
self.patchJump(jump)
@ -1666,14 +1517,45 @@ proc statement(self: Compiler, node: Statement) =
## Compiles all statements
case node.kind:
of exprStmt:
var expression = ExprStmt(node).expression
let expression = ExprStmt(node).expression
let kind = self.inferType(expression)
self.expression(expression)
if expression.kind == callExpr and self.inferType(CallExpr(expression).callee).returnType.isNil():
# The expression has no type, so we don't have to
# pop anything
if kind.isNil():
# The expression has no type and produces no value,
# so we don't have to pop anything
discard
elif self.replMode:
self.emitByte(PopRepl)
case kind.kind:
of Int64:
self.emitByte(PrintInt64)
of UInt64:
self.emitByte(PrintUInt64)
of Int32:
self.emitByte(PrintInt32)
of UInt32:
self.emitByte(PrintInt32)
of Int16:
self.emitByte(PrintInt16)
of UInt16:
self.emitByte(PrintUInt16)
of Int8:
self.emitByte(PrintInt8)
of UInt8:
self.emitByte(PrintUInt8)
of Float64:
self.emitByte(PrintFloat64)
of Float32:
self.emitByte(PrintFloat32)
of Bool:
self.emitByte(PrintBool)
of Nan:
self.emitByte(PrintNan)
of Inf:
self.emitByte(PrintInf)
of String:
self.emitByte(PrintString)
else:
self.emitByte(PrintHex)
else:
self.emitByte(Pop)
of NodeKind.ifStmt:
@ -1734,7 +1616,7 @@ proc varDecl(self: Compiler, node: VarDecl) =
self.expression(node.value)
self.declareName(node, mutable=node.token.kind == TokenType.Var)
self.emitByte(StoreVar)
self.emitBytes((self.getStackPos(self.names[^1]) + 1).toTriple())
self.emitBytes(self.getStackPos(self.names[^1]).toTriple())
proc typeDecl(self: Compiler, node: TypeDecl) =
@ -2047,4 +1929,3 @@ proc compileModule(self: Compiler, filename: string) =
self.names.add(name)
self.modules.incl(path)
self.closedOver &= compiler.closedOver
compiler.endScope()

View File

@ -68,7 +68,7 @@ type
# or 24 bit numbers that are defined statically
# at compilation time into the bytecode
# These push a constant at position x in the
# These push a constant at position x in the
# constant table onto the stack
LoadInt64 = 0u8,
LoadUInt64,
@ -90,114 +90,58 @@ type
LoadNan,
LoadInf,
## Operations on primitive types
GenericPrint,
NegInt64, # No unsigned variants (how would you negate something that has no sign?)
NegInt32,
NegInt16,
NegInt8,
NegFloat32,
NegFloat64,
AddInt64,
AddUInt64,
AddInt32,
AddUInt32
AddInt16,
AddUInt16,
AddInt8,
AddUInt8,
SubInt64,
SubUInt64,
SubInt32,
SubUInt32,
SubInt16,
SubUInt16,
SubInt8,
SubUInt8,
MulInt64,
MulUInt64,
MulInt32,
MulUInt32,
MulInt16,
MulUInt16,
MulInt8,
MulUInt8,
DivInt64,
DivUInt64,
DivInt32,
DivUInt32,
DivInt16,
DivUInt16,
DivInt8,
DivUInt8,
Negate,
NegateFloat64,
NegateFloat32,
Add,
Subtract,
Multiply,
Divide,
SignedDivide,
AddFloat64,
SubFloat64,
DivFloat64,
MulFloat64,
SubtractFloat64,
MultiplyFloat64,
DivideFloat64,
AddFloat32,
SubFloat32,
DivFloat32,
MulFloat32,
LessThanInt64,
GreaterThanInt64,
EqualInt64,
NotEqualInt64,
LessThanUInt64,
GreaterThanUInt64,
EqualUInt64,
NotEqualUInt64,
LessThanInt32,
GreaterThanInt32,
EqualInt32,
NotEqualInt32,
LessThanUInt32,
GreaterThanUInt32,
EqualUInt32,
NotEqualUInt32,
LessThanInt16,
GreaterThanInt16,
EqualInt16,
NotEqualInt16,
LessThanUInt16,
GreaterThanUInt16,
EqualUInt16,
NotEqualUInt16,
LessThanInt8,
GreaterThanInt8,
EqualInt8,
NotEqualInt8,
LessThanUInt8,
GreaterThanUInt8,
EqualUInt8,
NotEqualUInt8,
LessThanFloat64,
GreaterThanFloat64,
EqualFloat64,
NotEqualFloat64,
LessThanFloat32,
GreaterThanFloat32,
EqualFloat32,
NotEqualFloat32,
GreaterOrEqualInt64,
LessOrEqualInt64,
GreaterOrEqualUInt64,
LessOrEqualUInt64,
GreaterOrEqualInt32,
LessOrEqualInt32,
GreaterOrEqualUInt32,
LessOrEqualUInt32,
GreaterOrEqualInt16,
LessOrEqualInt16,
GreaterOrEqualUInt16,
LessOrEqualUInt16,
GreaterOrEqualInt8,
LessOrEqualInt8,
GreaterOrEqualUInt8,
LessOrEqualUInt8,
GreaterOrEqualFloat64,
LessOrEqualFloat64,
GreaterOrEqualFloat32,
LessOrEqualFloat32,
SysClock64,
SubtractFloat32,
MultiplyFloat32,
DivideFloat32,
Pow,
SignedPow,
Mod,
SignedMod,
PowFloat64,
PowFloat32,
ModFloat64,
ModFloat32,
LShift,
RSHift,
Xor,
Or,
And,
Not,
Equal,
NotEqual,
GreaterThan,
LessThan,
GreaterOrEqual,
LessOrEqual,
## Print opcodes
PrintInt64,
PrintUInt64,
PrintInt32,
PrintUInt32,
PrintInt16,
PrintUint16,
PrintInt8,
PrintUInt8,
PrintFloat64,
PrintFloat32,
PrintHex,
PrintBool,
PrintNan,
PrintInf,
PrintString,
## Basic stack operations
Pop, # Pops an element off the stack and discards it
PopRepl, # Same as Pop, but also prints the value of what's popped (used in REPL mode)
@ -229,10 +173,11 @@ type
## Coroutines
Await, # Calls an asynchronous function
## Misc
Assert, # Raises an AssertionFailed exception if x is false
NoOp, # Just a no-op
PopC, # Pop off the call stack onto the operand stack
PushC # Pop off the operand stack onto the call stack
Assert, # Raises an AssertionFailed exception if x is false
NoOp, # Just a no-op
PopC, # Pop off the call stack onto the operand stack
PushC, # Pop off the operand stack onto the call stack
SysClock64 # Pushes the output of a monotonic clock on the stack
# We group instructions by their operation/operand types for easier handling when debugging
@ -241,60 +186,62 @@ type
const simpleInstructions* = {Return, LoadNil,
LoadTrue, LoadFalse,
LoadNan, LoadInf,
Pop, PopRepl, Raise,
Pop, Raise,
BeginTry, FinishTry, Yield,
Await, NoOp, SetResult,
PopC, PushC,
AddInt64, AddUInt64, AddInt32,
AddUInt32, AddInt16, AddUInt16,
AddInt8, AddUInt8, SubInt64,
SubUInt64, SubInt32, SubUInt32,
SubInt16, SubUInt16, SubInt8,
SubUInt8, MulInt64, MulUInt64,
MulInt32, MulUInt32, MulInt16,
MulUInt16, MulInt8, MulUInt8,
DivInt64, DivUInt64, DivInt32,
DivUInt32, DivInt16, DivUInt16,
DivInt8, DivUInt8, AddFloat64,
SubFloat64, DivFloat64, MulFloat64,
AddFloat32, SubFloat32, DivFloat32,
MulFloat32, NegFloat32, NegFloat64,
LessThanInt64, SysClock64, GenericPrint,
GreaterThanInt64, EqualInt64, NotEqualInt64,
LessThanUInt64, GreaterThanUInt64, EqualUInt64,
NotEqualUInt64, LessThanInt32, GreaterThanInt32,
EqualInt32, NotEqualInt32, LessThanUInt32,
GreaterThanUInt32, EqualUInt32, NotEqualUInt32,
LessThanInt16, GreaterThanInt16, EqualInt16,
NotEqualInt16, LessThanUInt16, GreaterThanUInt16,
EqualUInt16, NotEqualUInt16, LessThanInt8,
GreaterThanInt8,EqualInt8, NotEqualInt8,
LessThanUInt8, GreaterThanUInt8, EqualUInt8,
NotEqualUInt8, LessThanFloat64, GreaterThanFloat64,
EqualFloat64,NotEqualFloat64, LessThanFloat32,
GreaterThanFloat32, EqualFloat32, NotEqualFloat32,
GreaterOrEqualInt64,
LessOrEqualInt64,
GreaterOrEqualUInt64,
LessOrEqualUInt64,
GreaterOrEqualInt32,
LessOrEqualInt32,
GreaterOrEqualUInt32,
LessOrEqualUInt32,
GreaterOrEqualInt16,
LessOrEqualInt16,
GreaterOrEqualUInt16,
LessOrEqualUInt16,
GreaterOrEqualInt8,
LessOrEqualInt8,
GreaterOrEqualUInt8,
LessOrEqualUInt8,
GreaterOrEqualFloat64,
LessOrEqualFloat64,
GreaterOrEqualFloat32,
LessOrEqualFloat32,
SysClock64, NegInt16
}
PopC, PushC, SysClock64,
Negate,
NegateFloat64,
NegateFloat32,
Add,
Subtract,
Multiply,
Divide,
SignedDivide,
AddFloat64,
SubtractFloat64,
MultiplyFloat64,
DivideFloat64,
AddFloat32,
SubtractFloat32,
MultiplyFloat32,
DivideFloat32,
Pow,
SignedPow,
Mod,
SignedMod,
PowFloat64,
PowFloat32,
ModFloat64,
ModFloat32,
LShift,
RSHift,
Xor,
Or,
And,
Not,
Equal,
NotEqual,
GreaterThan,
LessThan,
GreaterOrEqual,
LessOrEqual,
PrintInt64,
PrintUInt64,
PrintInt32,
PrintUInt32,
PrintInt16,
PrintUint16,
PrintInt8,
PrintUInt8,
PrintFloat64,
PrintFloat32,
PrintHex,
PrintBool,
PrintNan,
PrintInf,
PrintString,
}
# Constant instructions are instructions that operate on the bytecode constant table
const constantInstructions* = {LoadInt64, LoadUInt64,

View File

@ -36,7 +36,7 @@ const debugRuntime {.booldefine.} = false
proc repl(vm: PeonVM = newPeonVM()) =
proc repl =
styledEcho fgMagenta, "Welcome into the peon REPL!"
var
keep = true
@ -47,6 +47,7 @@ proc repl(vm: PeonVM = newPeonVM()) =
tokenizer = newLexer()
parser = newParser()
compiler = newCompiler(replMode=true)
vm = newPeonVM()
debugger = newDebugger()
serializer = newSerializer()
editor = getLineEditor()
@ -90,7 +91,7 @@ proc repl(vm: PeonVM = newPeonVM()) =
break
styledEcho fgGreen, "\t", $token
echo ""
tree = parser.parse(tokens, "stdin", tokenizer.getLines(), input)
tree = parser.parse(tokens, "stdin", tokenizer.getLines(), input, persist=true)
if tree.len() == 0:
continue
when debugParser:
@ -135,7 +136,9 @@ proc repl(vm: PeonVM = newPeonVM()) =
vm.run(serialized.chunk)
except LexingError:
input = ""
let exc = LexingError(getCurrentException())
var exc = LexingError(getCurrentException())
if exc.lexeme == "":
exc.line -= 1
let relPos = exc.lexer.getRelPos(exc.line)
let line = exc.lexer.getSource().splitLines()[exc.line - 1].strip(chars={'\n'})
stderr.styledWriteLine(fgRed, "A fatal error occurred while parsing ", fgYellow, &"'{exc.file}'", fgRed, ", module ",
@ -147,7 +150,9 @@ proc repl(vm: PeonVM = newPeonVM()) =
input = ""
let exc = ParseError(getCurrentException())
let lexeme = exc.token.lexeme
let lineNo = exc.token.line
var lineNo = exc.token.line
if exc.token.kind == EndOfFile:
lineNo -= 1
let relPos = exc.parser.getRelPos(lineNo)
let fn = parser.getCurrentFunction()
let line = exc.parser.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
@ -162,7 +167,9 @@ proc repl(vm: PeonVM = newPeonVM()) =
except CompileError:
let exc = CompileError(getCurrentException())
let lexeme = exc.node.token.lexeme
let lineNo = exc.node.token.line
var lineNo = exc.node.token.line
if exc.node.token.kind == EndOfFile:
lineNo -= 1
let relPos = exc.compiler.getRelPos(lineNo)
let line = exc.compiler.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
var fn = exc.compiler.getCurrentFunction()
@ -263,8 +270,9 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
styledEcho fgCyan, "\n\nExecution step: "
vm.run(serialized.chunk)
except LexingError:
input = ""
let exc = LexingError(getCurrentException())
var exc = LexingError(getCurrentException())
if exc.lexeme == "":
exc.line -= 1
let relPos = exc.lexer.getRelPos(exc.line)
let line = exc.lexer.getSource().splitLines()[exc.line - 1].strip(chars={'\n'})
stderr.styledWriteLine(fgRed, "A fatal error occurred while parsing ", fgYellow, &"'{exc.file}'", fgRed, ", module ",
@ -273,10 +281,11 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
styledEcho fgBlue, "Source line: " , fgDefault, line
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
except ParseError:
input = ""
let exc = ParseError(getCurrentException())
let lexeme = exc.token.lexeme
let lineNo = exc.token.line
var lineNo = exc.token.line
if exc.token.kind == EndOfFile:
lineNo -= 1
let relPos = exc.parser.getRelPos(lineNo)
let fn = parser.getCurrentFunction()
let line = exc.parser.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
@ -291,7 +300,9 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
except CompileError:
let exc = CompileError(getCurrentException())
let lexeme = exc.node.token.lexeme
let lineNo = exc.node.token.line
var lineNo = exc.node.token.line
if exc.node.token.kind == EndOfFile:
lineNo -= 1
let relPos = exc.compiler.getRelPos(lineNo)
let line = exc.compiler.getSource().splitLines()[lineNo - 1].strip(chars={'\n'})
var fn = exc.compiler.getCurrentFunction()
@ -310,8 +321,7 @@ proc runFile(f: string, interactive: bool = false, fromString: bool = false) =
stderr.styledWriteLine(fgRed, "An error occurred while trying to read ", fgYellow, &"'{f}'", fgGreen, &": {getCurrentExceptionMsg()}")
except OSError:
stderr.styledWriteLine(fgRed, "An error occurred while trying to read ", fgYellow, &"'{f}'", fgGreen, &": {osErrorMsg(osLastError())} [errno {osLastError()}]")
if interactive:
repl(vm)
when isMainModule:

View File

@ -1,4 +0,0 @@
import std;
import b;
print("a");

View File

@ -1,3 +0,0 @@
import std;
print("b");

View File

@ -1,3 +1,7 @@
# Tests simple calls
import std;
fn noReturn(n: int) {
var n = n;
var `17` = 17;
@ -12,4 +16,4 @@ fn fooBar(a, b: int): int {
noReturn(1);
fooBar(1, 3);
print(fooBar(1, 3)); # 1

View File

@ -1,3 +1,7 @@
# Tests closures
import std;
fn makeClosure(n: int): fn: int {
let n = n; # Workaround
fn inner: int {
@ -8,4 +12,5 @@ fn makeClosure(n: int): fn: int {
var closure = makeClosure(1)();
closure;
print(closure); # 1
print(makeClosure(2)()); # 2

View File

@ -1,4 +1,7 @@
# Tests that comparisons work
import std;
# int64
print(3 > 2); # true
print(2 < 3); # true

View File

@ -1,3 +1,6 @@
# Tests operator dispatching
operator `+`(a: int): int {
return a;
}

View File

@ -1,4 +1,40 @@
import std;
operator `<`(a, b: int): bool {
#pragma[magic: "LessThan", pure]
}
operator `+`(a, b: int): int {
#pragma[magic: "Add", pure]
}
operator `-`(a, b: int): int {
#pragma[magic: "Subtract", pure]
}
operator `-`(a, b: float): float {
#pragma[magic: "SubtractFloat64", pure]
}
fn clock: float {
#pragma[magic: "SysClock64"]
}
fn print(x: int) {
#pragma[magic: "PrintInt64"]
}
fn print(x: float) {
#pragma[magic: "PrintFloat64"]
}
fn print(x: string) {
#pragma[magic: "PrintString"]
}
fn fib(n: int): int {
@ -9,8 +45,8 @@ fn fib(n: int): int {
}
print("Computing the value of fib(25)");
print("Computing the value of fib(30)");
var x = clock();
# print(fib(25));
print(fib(30));
print(clock() - x);
print("Done!");

View File

@ -1,4 +1,9 @@
fn getFunction: fn (n: int): int {
# Tests first class functions
import std;
fn outer: fn (n: int): int {
fn inner(n: int): int {
return n;
}
@ -6,11 +11,19 @@ fn getFunction: fn (n: int): int {
}
operator `+`(a, b: int): int {
#pragma[magic: "AddInt64"]
fn getAdder(a, b: int): fn: int64 {
var x = a;
var y = b;
fn adder: int {
return x + y;
}
return adder;
}
getFunction()(5);
var x = getFunction;
x()(3);
print(outer()(1)); # 1
var a = 1;
var b = 2;
var adder = getAdder(a, b);
print(adder()); # 3

View File

@ -1,5 +1,6 @@
import std;
# Tests var parameters
import std;
operator `+=`(a: var int, b: int) {

View File

@ -1,3 +1,7 @@
# Tests nested calls
import std;
fn outer: int {
fn inner: int {
return 69420;
@ -14,5 +18,5 @@ fn outerTwo(n: int): int {
}
outerTwo(5);
outer();
print(outerTwo(5));
print(outer());

View File

@ -1,6 +1,10 @@
operator `+`(a, b: int): int {
#pragma[magic: "AddInt64", pure]
# Tests the creation and use of custom operators
import std;
operator `sum`(a, b: int): int {
return a + b;
}
2 + 2; # Works! :D
print(2 sum 2); # 4

View File

@ -1,9 +0,0 @@
fn outer: fn (n: int): int {
fn inner(n: int): int {
return n;
}
return inner;
}
outer()(1);

View File

@ -1,3 +1,4 @@
# Tests local scopes
import std;

View File

@ -9,37 +9,37 @@
operator `+`*(a, b: int): int {
#pragma[magic: "AddInt64", pure]
#pragma[magic: "SignedAdd", pure]
}
operator `+`*(a, b: uint64): uint64 {
#pragma[magic: "AddUInt64", pure]
#pragma[magic: "Add", pure]
}
operator `+`*(a, b: int32): int32 {
#pragma[magic: "AddInt32", pure]
#pragma[magic: "SignedAdd", pure]
}
operator `+`*(a, b: uint32): uint32 {
#pragma[magic: "AddUInt32", pure]
#pragma[magic: "Add", pure]
}
operator `+`*(a, b: int16): int16 {
#pragma[magic: "AddInt16", pure]
#pragma[magic: "SignedAdd", pure]
}
operator `+`*(a, b: uint16): uint16 {
#pragma[magic: "AddUInt16", pure]
#pragma[magic: "Add", pure]
}
operator `+`*(a, b: int8): int8 {
#pragma[magic: "AddInt8", pure]
#pragma[magic: "Add", pure]
}
@ -109,7 +109,7 @@ operator `-`*(a, b: float32): float32 {
operator `*`*(a, b: int): int {
#pragma[magic: "MulInt64", pure]
#pragma[magic: "SignedMultiply", pure]
}
@ -119,7 +119,7 @@ operator `*`*(a, b: uint64): uint64 {
operator `*`*(a, b: int32): int32 {
#pragma[magic: "MulInt32", pure]
#pragma[magic: "SignedMultiply", pure]
}
@ -129,7 +129,7 @@ operator `*`*(a, b: uint32): uint32 {
operator `*`*(a, b: int16): int16 {
#pragma[magic: "MulInt16", pure]
#pragma[magic: "SignedMultiply", pure]
}
@ -139,7 +139,7 @@ operator `*`*(a, b: uint16): uint16 {
operator `*`*(a, b: int8): int8 {
#pragma[magic: "MulInt8", pure]
#pragma[magic: "SignedMultiply", pure]
}
@ -208,6 +208,11 @@ operator `/`*(a, b: float32): float32 {
}
operator `**`*(a, b: int64): int64 {
#pragma[magic: "PowInt64", pure]
}
# Comparison operators
operator `>`*(a, b: int): bool {
@ -508,11 +513,22 @@ operator `<=`*(a, b: float32): bool {
#pragma[magic: "LessOrEqualFloat32", pure]
}
# Assignment operator
# TODO
#operator `=`[T](a: var T, b: T) {
# #pragma[magic: "GenericAssign"]
#}
operator `and`*(a, b: bool): bool {
#pragma[magic: "LogicalAnd", pure]
}
operator `or`*(a, b: bool): bool {
#pragma[magic: "LogicalOr", pure]
}
# Assignment operators
operator `=`[T: Any](a: var T, b: T) {
#pragma[magic: "GenericAssign"]
}
# Some useful builtins
@ -541,4 +557,4 @@ fn print*(x: string) {
fn print*(x: bool) {
#pragma[magic: "GenericPrint"]
}
}

View File

@ -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());