Minor style changes, removed findImpl from compiler, made the VM use the multibyte utilities, bytecode chunks now no longer store AST node objects and use a stream of bytes instead, fixed issues with endScope() in the compiler which would not pop properly from self.names, fixed issues with blockStmt in parser, added more multibyte utilities
This commit is contained in:
parent
77bd0c8b6f
commit
1a0587d08b
|
@ -16,6 +16,7 @@
|
||||||
import types
|
import types
|
||||||
import ../config
|
import ../config
|
||||||
import ../frontend/meta/bytecode
|
import ../frontend/meta/bytecode
|
||||||
|
import ../util/multibyte
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -106,8 +107,7 @@ proc readShort(self: PeonVM): uint16 =
|
||||||
## bytecode and returns them
|
## bytecode and returns them
|
||||||
## as an unsigned 16 bit
|
## as an unsigned 16 bit
|
||||||
## integer
|
## integer
|
||||||
var arr: array[2, uint8] = [self.readByte(), self.readByte()]
|
return [self.readByte(), self.readByte()].fromDouble()
|
||||||
copyMem(result.addr, unsafeAddr(arr), sizeof(arr))
|
|
||||||
|
|
||||||
|
|
||||||
proc readLong(self: PeonVM): uint32 =
|
proc readLong(self: PeonVM): uint32 =
|
||||||
|
@ -117,8 +117,7 @@ proc readLong(self: PeonVM): uint32 =
|
||||||
## integer. Note however that
|
## integer. Note however that
|
||||||
## the boundary is capped at
|
## the boundary is capped at
|
||||||
## 24 bits instead of 32
|
## 24 bits instead of 32
|
||||||
var arr: array[3, uint8] = [self.readByte(), self.readByte(), self.readByte()]
|
return uint32([self.readByte(), self.readByte(), self.readByte()].fromTriple())
|
||||||
copyMem(result.addr, unsafeAddr(arr), sizeof(arr))
|
|
||||||
|
|
||||||
|
|
||||||
proc readInt64(self: PeonVM, idx: int): PeonObject =
|
proc readInt64(self: PeonVM, idx: int): PeonObject =
|
||||||
|
@ -126,8 +125,8 @@ proc readInt64(self: PeonVM, idx: int): PeonObject =
|
||||||
## chunk's constant table and
|
## chunk's constant table and
|
||||||
## returns a Peon object. Assumes
|
## returns a Peon object. Assumes
|
||||||
## the constant is an Int64
|
## the constant is an Int64
|
||||||
var arr = [self.chunk.byteConsts[idx], self.chunk.byteConsts[idx + 1],
|
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
||||||
self.chunk.byteConsts[idx + 2], self.chunk.byteConsts[idx + 3]]
|
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]]
|
||||||
result = PeonObject(kind: Int64)
|
result = PeonObject(kind: Int64)
|
||||||
copyMem(result.long.addr, arr.addr, sizeof(arr))
|
copyMem(result.long.addr, arr.addr, sizeof(arr))
|
||||||
|
|
||||||
|
@ -137,8 +136,8 @@ proc readUInt64(self: PeonVM, idx: int): PeonObject =
|
||||||
## chunk's constant table and
|
## chunk's constant table and
|
||||||
## returns a Peon object. Assumes
|
## returns a Peon object. Assumes
|
||||||
## the constant is an UInt64
|
## the constant is an UInt64
|
||||||
var arr = [self.chunk.byteConsts[idx], self.chunk.byteConsts[idx + 1],
|
var arr = [self.chunk.consts[idx], self.chunk.consts[idx + 1],
|
||||||
self.chunk.byteConsts[idx + 2], self.chunk.byteConsts[idx + 3]]
|
self.chunk.consts[idx + 2], self.chunk.consts[idx + 3]]
|
||||||
result = PeonObject(kind: UInt64)
|
result = PeonObject(kind: UInt64)
|
||||||
copyMem(result.uLong.addr, arr.addr, sizeof(arr))
|
copyMem(result.uLong.addr, arr.addr, sizeof(arr))
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ const PEON_COMMIT_HASH* = "ed79385e2a93100331697f26a4a90157e60ad27a"
|
||||||
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* = "master"
|
||||||
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* = false # Traces VM execution
|
const DEBUG_TRACE_VM* = false # Traces VM execution
|
||||||
const SKIP_STDLIB_INIT* = false # Skips stdlib initialization (can be imported manually)
|
const SKIP_STDLIB_INIT* = false # Skips stdlib initialization (can be imported manually)
|
||||||
|
@ -48,7 +48,7 @@ Basic usage
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
$ peon Opens an interactive session (REPL)
|
$ peon Opens an interactive session (REPL)
|
||||||
$ peon file.pe Runs the given Peon source file
|
$ peon file.pn Runs the given Peon source file
|
||||||
|
|
||||||
Command-line options
|
Command-line options
|
||||||
--------------------
|
--------------------
|
||||||
|
|
|
@ -128,7 +128,7 @@ type
|
||||||
# inside an implicit try/finally block
|
# inside an implicit try/finally block
|
||||||
# and add this code in the finally branch.
|
# and add this code in the finally branch.
|
||||||
# This sequence is emptied each time a
|
# This sequence is emptied each time a
|
||||||
# fun declaration is compiled and stores only
|
# function declaration is compiled and stores only
|
||||||
# deferred code for the current function (may
|
# deferred code for the current function (may
|
||||||
# be empty)
|
# be empty)
|
||||||
deferred: seq[uint8]
|
deferred: seq[uint8]
|
||||||
|
@ -185,8 +185,7 @@ proc done(self: Compiler): bool =
|
||||||
result = self.current > self.ast.high()
|
result = self.current > self.ast.high()
|
||||||
|
|
||||||
|
|
||||||
proc error(self: Compiler, message: string) {.raises: [CompileError,
|
proc error(self: Compiler, message: string) {.raises: [CompileError, ValueError].} =
|
||||||
ValueError].} =
|
|
||||||
## Raises a formatted CompileError exception
|
## Raises a formatted CompileError exception
|
||||||
var tok = self.getCurrentNode().token
|
var tok = self.getCurrentNode().token
|
||||||
raise newException(CompileError, &"A fatal error occurred while compiling '{self.file}', module '{self.currentModule}' line {tok.line} at '{tok.lexeme}' -> {message}")
|
raise newException(CompileError, &"A fatal error occurred while compiling '{self.file}', module '{self.currentModule}' line {tok.line} at '{tok.lexeme}' -> {message}")
|
||||||
|
@ -231,10 +230,20 @@ proc emitBytes(self: Compiler, bytarr: openarray[uint8]) =
|
||||||
self.emitByte(b)
|
self.emitByte(b)
|
||||||
|
|
||||||
|
|
||||||
proc makeConstant(self: Compiler, val: Expression, kind: Type): array[3, uint8] =
|
proc makeConstant(self: Compiler, val: Expression, typ: Type): array[3, uint8] =
|
||||||
## Adds a constant to the current chunk's constant table
|
## Adds a constant to the current chunk's constant table
|
||||||
## and returns its index as a 3-byte array of uint8s
|
## and returns its index as a 3-byte array of uint8s
|
||||||
result = self.chunk.addConstant(val, kind)
|
case typ.kind:
|
||||||
|
of UInt8, Int8:
|
||||||
|
result = self.chunk.writeConstant([uint8(parseInt(val.token.lexeme))])
|
||||||
|
of Int16, UInt16:
|
||||||
|
result = self.chunk.writeConstant(parseInt(val.token.lexeme).toDouble())
|
||||||
|
of Int32, UInt32:
|
||||||
|
result = self.chunk.writeConstant(parseInt(val.token.lexeme).toQuad())
|
||||||
|
of Int64, UInt64:
|
||||||
|
result = self.chunk.writeConstant(parseInt(val.token.lexeme).toLong())
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
|
|
||||||
proc emitConstant(self: Compiler, obj: Expression, kind: Type) =
|
proc emitConstant(self: Compiler, obj: Expression, kind: Type) =
|
||||||
|
@ -816,23 +825,9 @@ proc identifier(self: Compiler, node: IdentExpr) =
|
||||||
self.emitBytes(self.closedOver.high().toTriple())
|
self.emitBytes(self.closedOver.high().toTriple())
|
||||||
|
|
||||||
|
|
||||||
proc findImpl(self: Compiler, node: FunDecl): seq[Name] =
|
|
||||||
## Looks for functions matching the given declaration
|
|
||||||
## in the code that has been compiled so far.
|
|
||||||
## Returns a list of each matching name object
|
|
||||||
for obj in reversed(self.names):
|
|
||||||
# Scopes are indexed backwards!
|
|
||||||
case obj.valueType.kind:
|
|
||||||
of Function:
|
|
||||||
if self.compareTypes(obj.valueType, self.inferType(node)):
|
|
||||||
result.add(obj)
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
|
||||||
proc findByName(self: Compiler, name: string): seq[Name] =
|
proc findByName(self: Compiler, name: string): seq[Name] =
|
||||||
## Looks for objects that have been already declared
|
## Looks for objects that have been already declared
|
||||||
## with the given name
|
## with the given name. Returns all objects that apply
|
||||||
for obj in reversed(self.names):
|
for obj in reversed(self.names):
|
||||||
if obj.name.token.lexeme == name:
|
if obj.name.token.lexeme == name:
|
||||||
result.add(obj)
|
result.add(obj)
|
||||||
|
@ -888,12 +883,14 @@ proc beginScope(self: Compiler) =
|
||||||
|
|
||||||
proc endScope(self: Compiler) =
|
proc endScope(self: Compiler) =
|
||||||
## Ends the current local scope
|
## Ends the current local scope
|
||||||
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)
|
||||||
var popped: int = 0
|
var popped: int = 0
|
||||||
for ident in reversed(self.names):
|
for i, ident in reversed(self.names):
|
||||||
if ident.depth > self.scopeDepth:
|
if ident.depth > self.scopeDepth:
|
||||||
inc(popped)
|
inc(popped)
|
||||||
|
self.names.delete(self.names.len() - i)
|
||||||
if not self.enableOptimizations:
|
if not self.enableOptimizations:
|
||||||
# All variables with a scope depth larger than the current one
|
# All variables with a scope depth larger than the current one
|
||||||
# are now out of scope. Begone, you're now homeless!
|
# are now out of scope. Begone, you're now homeless!
|
||||||
|
@ -918,9 +915,6 @@ proc endScope(self: Compiler) =
|
||||||
elif popped == 1:
|
elif popped == 1:
|
||||||
# We only emit PopN if we're popping more than one value
|
# We only emit PopN if we're popping more than one value
|
||||||
self.emitByte(Pop)
|
self.emitByte(Pop)
|
||||||
for _ in countup(0, popped - 1):
|
|
||||||
discard self.names.pop()
|
|
||||||
dec(self.scopeDepth)
|
|
||||||
|
|
||||||
|
|
||||||
proc blockStmt(self: Compiler, node: BlockStmt) =
|
proc blockStmt(self: Compiler, node: BlockStmt) =
|
||||||
|
@ -1273,8 +1267,7 @@ proc compile*(self: Compiler, ast: seq[Declaration], file: string): Chunk =
|
||||||
self.declaration(Declaration(self.step()))
|
self.declaration(Declaration(self.step()))
|
||||||
if self.ast.len() > 0:
|
if self.ast.len() > 0:
|
||||||
# *Technically* an empty program is a valid program
|
# *Technically* an empty program is a valid program
|
||||||
self.endScope()
|
|
||||||
self.emitByte(OpCode.Return) # Exits the VM's main loop when used at the global scope
|
self.emitByte(OpCode.Return) # Exits the VM's main loop when used at the global scope
|
||||||
result = self.chunk
|
result = self.chunk
|
||||||
if self.ast.len() > 0 and self.scopeDepth != -1:
|
if self.ast.len() > 0 and self.scopeDepth != 0:
|
||||||
self.error(&"invalid state: invalid scopeDepth value (expected -1, got {self.scopeDepth}), did you forget to call endScope/beginScope?")
|
self.error(&"invalid state: invalid scopeDepth value (expected 0, got {self.scopeDepth}), did you forget to call endScope/beginScope?")
|
||||||
|
|
|
@ -13,24 +13,16 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
## Low level bytecode implementation details
|
## Low level bytecode implementation details
|
||||||
import ast
|
|
||||||
import errors
|
|
||||||
|
|
||||||
import strutils
|
import strutils
|
||||||
import strformat
|
import strformat
|
||||||
|
|
||||||
import ../../util/multibyte
|
import ../../util/multibyte
|
||||||
import ../compiler
|
|
||||||
|
|
||||||
export ast
|
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Chunk* = ref object
|
Chunk* = ref object
|
||||||
## A piece of bytecode.
|
## A piece of bytecode.
|
||||||
## consts represents the high-level constants table the code is
|
|
||||||
## referring to and is only meaningful at compile time (not stored
|
|
||||||
## in bytecode dumps!).
|
|
||||||
## byteConsts is used when serializing to/from a bytecode stream.
|
## byteConsts is used when serializing to/from a bytecode stream.
|
||||||
## code is the linear sequence of compiled bytecode instructions.
|
## code is the linear sequence of compiled bytecode instructions.
|
||||||
## lines maps bytecode instructions to line numbers using Run
|
## lines maps bytecode instructions to line numbers using Run
|
||||||
|
@ -46,8 +38,7 @@ type
|
||||||
## are 3 and 4"
|
## are 3 and 4"
|
||||||
## This is more efficient than using the naive approach, which would encode
|
## This is more efficient than using the naive approach, which would encode
|
||||||
## the same line number multiple times and waste considerable amounts of space.
|
## the same line number multiple times and waste considerable amounts of space.
|
||||||
consts*: seq[Expression]
|
consts*: seq[uint8]
|
||||||
byteConsts*: seq[uint8]
|
|
||||||
code*: seq[uint8]
|
code*: seq[uint8]
|
||||||
lines*: seq[int]
|
lines*: seq[int]
|
||||||
reuseConsts*: bool
|
reuseConsts*: bool
|
||||||
|
@ -223,42 +214,10 @@ proc getLine*(self: Chunk, idx: int): int =
|
||||||
raise newException(IndexDefect, "index out of range")
|
raise newException(IndexDefect, "index out of range")
|
||||||
|
|
||||||
|
|
||||||
proc findOrAddConstant(self: Chunk, constant: Expression, kind: Type): int =
|
proc writeConstant*(self: Chunk, data: openarray[uint8]): array[3, uint8] =
|
||||||
## Small optimization function that reuses the same constant
|
## Writes a series of bytes to the chunk's constant
|
||||||
## if it's already been written before (only if self.reuseConsts
|
## table and returns the index of the first byte as
|
||||||
## equals true)
|
## an array of 3 bytes
|
||||||
if not self.reuseConsts:
|
result = self.consts.len().toTriple()
|
||||||
return
|
for b in data:
|
||||||
for i, c in self.consts:
|
self.consts.add(b)
|
||||||
# We cannot use simple equality because the nodes likely have
|
|
||||||
# different token objects with different values
|
|
||||||
if c.kind != constant.kind:
|
|
||||||
continue
|
|
||||||
if constant.isConst():
|
|
||||||
if LiteralExpr(c).literal.lexeme == LiteralExpr(
|
|
||||||
constant).literal.lexeme:
|
|
||||||
# This wouldn't work for stuff like 2e3 and 2000.0, but those
|
|
||||||
# forms are collapsed in the compiler before being written
|
|
||||||
# to the constants table
|
|
||||||
return i
|
|
||||||
elif constant.kind == identExpr:
|
|
||||||
if IdentExpr(c).name.lexeme == IdentExpr(constant).name.lexeme:
|
|
||||||
return i
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
self.consts.add(constant)
|
|
||||||
result = self.consts.high()
|
|
||||||
|
|
||||||
|
|
||||||
proc addConstant*(self: Chunk, constant: Expression, kind: Type): array[3, uint8] =
|
|
||||||
## Writes a constant of the given type in the chunk's constant
|
|
||||||
## table. Returns its index as an array of 3 unsigned 8 bit integers.
|
|
||||||
## Constant indexes are reused if a constant is used more than once
|
|
||||||
## and self.reuseConsts equals true
|
|
||||||
if self.consts.high() == 16777215:
|
|
||||||
# The constant index is a 24 bit unsigned integer, so that's as far
|
|
||||||
# as we can index into the constant table (the same applies
|
|
||||||
# to our stack by the way). Not that anyone's ever gonna hit this
|
|
||||||
# limit in the real world, but you know, just in case
|
|
||||||
raise newException(CompileError, "cannot encode more than 16777216 constants")
|
|
||||||
result = self.findOrAddConstant(constant, kind).toTriple()
|
|
||||||
|
|
|
@ -547,8 +547,8 @@ proc blockStmt(self: Parser): Statement =
|
||||||
var code: seq[Declaration] = @[]
|
var code: seq[Declaration] = @[]
|
||||||
while not self.check(RightBrace) and not self.done():
|
while not self.check(RightBrace) and not self.done():
|
||||||
code.add(self.declaration())
|
code.add(self.declaration())
|
||||||
if self.tree[^1] == nil:
|
if code[^1] == nil:
|
||||||
self.tree.delete(self.tree.high())
|
code.delete(code.high())
|
||||||
self.expect(RightBrace, "expecting '}'")
|
self.expect(RightBrace, "expecting '}'")
|
||||||
result = newBlockStmt(code, tok)
|
result = newBlockStmt(code, tok)
|
||||||
self.endScope()
|
self.endScope()
|
||||||
|
@ -1140,4 +1140,4 @@ proc parse*(self: Parser, tokens: seq[Token], file: string): seq[Declaration] =
|
||||||
self.tree.add(self.declaration())
|
self.tree.add(self.declaration())
|
||||||
if self.tree[^1] == nil:
|
if self.tree[^1] == nil:
|
||||||
self.tree.delete(self.tree.high())
|
self.tree.delete(self.tree.high())
|
||||||
result = self.tree
|
result = self.tree
|
10
src/test.nim
10
src/test.nim
|
@ -23,11 +23,11 @@ proc fillSymbolTable(tokenizer: Lexer)
|
||||||
proc getLineEditor: LineEditor
|
proc getLineEditor: LineEditor
|
||||||
|
|
||||||
# Handy dandy compile-time constants
|
# Handy dandy compile-time constants
|
||||||
const debugLexer = false
|
const debugLexer = true
|
||||||
const debugParser = true
|
const debugParser = true
|
||||||
const debugCompiler = true
|
const debugCompiler = true
|
||||||
const debugSerializer = false
|
const debugSerializer = true
|
||||||
const debugRuntime = false
|
const debugRuntime = true
|
||||||
|
|
||||||
when debugSerializer:
|
when debugSerializer:
|
||||||
import nimSHA2
|
import nimSHA2
|
||||||
|
@ -113,7 +113,7 @@ when isMainModule:
|
||||||
stdout.write(e)
|
stdout.write(e)
|
||||||
if i < len(serialized.chunk.consts) - 1:
|
if i < len(serialized.chunk.consts) - 1:
|
||||||
stdout.write(", ")
|
stdout.write(", ")
|
||||||
stdout.write("]\n")
|
stdout.write(&"] (matches: {serialized.chunk.consts == compiled.consts})\n")
|
||||||
stdout.write(&"\t- Reconstructed bytecode: [")
|
stdout.write(&"\t- Reconstructed bytecode: [")
|
||||||
for i, e in serialized.chunk.code:
|
for i, e in serialized.chunk.code:
|
||||||
stdout.write($e)
|
stdout.write($e)
|
||||||
|
@ -175,7 +175,7 @@ proc fillSymbolTable(tokenizer: Lexer) =
|
||||||
tokenizer.symbols.addKeyword("case", Case)
|
tokenizer.symbols.addKeyword("case", Case)
|
||||||
tokenizer.symbols.addKeyword("operator", Operator)
|
tokenizer.symbols.addKeyword("operator", Operator)
|
||||||
tokenizer.symbols.addKeyword("generator", Generator)
|
tokenizer.symbols.addKeyword("generator", Generator)
|
||||||
tokenizer.symbols.addKeyword("function", TokenType.Function)
|
tokenizer.symbols.addKeyword("fn", TokenType.Function)
|
||||||
tokenizer.symbols.addKeyword("coroutine", Coroutine)
|
tokenizer.symbols.addKeyword("coroutine", Coroutine)
|
||||||
tokenizer.symbols.addKeyword("break", TokenType.Break)
|
tokenizer.symbols.addKeyword("break", TokenType.Break)
|
||||||
tokenizer.symbols.addKeyword("continue", Continue)
|
tokenizer.symbols.addKeyword("continue", Continue)
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import ../frontend/meta/bytecode
|
import ../frontend/meta/bytecode
|
||||||
import ../frontend/meta/ast
|
|
||||||
import multibyte
|
import multibyte
|
||||||
|
|
||||||
|
|
||||||
|
@ -104,9 +103,6 @@ proc constantInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
||||||
setForegroundColor(fgYellow)
|
setForegroundColor(fgYellow)
|
||||||
stdout.write(&"{obj}\n")
|
stdout.write(&"{obj}\n")
|
||||||
setForegroundColor(fgGreen)
|
setForegroundColor(fgGreen)
|
||||||
printDebug("Value kind: ")
|
|
||||||
setForegroundColor(fgYellow)
|
|
||||||
stdout.write(&"{obj.kind}\n")
|
|
||||||
return offset + 4
|
return offset + 4
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,17 +17,26 @@
|
||||||
|
|
||||||
|
|
||||||
proc toDouble*(input: int | uint | uint16): array[2, uint8] =
|
proc toDouble*(input: int | uint | uint16): array[2, uint8] =
|
||||||
## Converts an int (either int, uint or uint16)
|
## Converts an unsigned integer
|
||||||
## to an array[2, uint8]
|
## to an array[2, uint8]
|
||||||
result = cast[array[2, uint8]](uint16(input))
|
result = cast[array[2, uint8]](uint16(input))
|
||||||
|
|
||||||
|
|
||||||
proc toTriple*(input: uint | int): array[3, uint8] =
|
proc toTriple*(input: uint | int): array[3, uint8] =
|
||||||
## Converts an unsigned integer (int is converted
|
## Converts an unsigned integer to an array[3, uint8]
|
||||||
## to an uint and sign is lost!) to an array[3, uint8]
|
|
||||||
result = cast[array[3, uint8]](uint(input))
|
result = cast[array[3, uint8]](uint(input))
|
||||||
|
|
||||||
|
|
||||||
|
proc toQuad*(input: int | uint | uint16 | uint32): array[4, uint8] =
|
||||||
|
## Converts an unsigned integer to an array[4, uint8]
|
||||||
|
result = cast[array[4, uint8]](uint(input))
|
||||||
|
|
||||||
|
|
||||||
|
proc toLong*(input: int | uint | uint16 | uint32 | uint64): array[8, uint8] =
|
||||||
|
## Converts an unsigned integer to an array[8, uint8]
|
||||||
|
result = cast[array[8, uint8]](uint(input))
|
||||||
|
|
||||||
|
|
||||||
proc fromDouble*(input: array[2, uint8]): uint16 =
|
proc fromDouble*(input: array[2, uint8]): uint16 =
|
||||||
## Rebuilds the output of toDouble into
|
## Rebuilds the output of toDouble into
|
||||||
## an uint16
|
## an uint16
|
||||||
|
@ -38,3 +47,15 @@ proc fromTriple*(input: array[3, uint8]): uint =
|
||||||
## Rebuilds the output of toTriple into
|
## Rebuilds the output of toTriple into
|
||||||
## an uint
|
## an uint
|
||||||
copyMem(result.addr, unsafeAddr(input), sizeof(uint8) * 3)
|
copyMem(result.addr, unsafeAddr(input), sizeof(uint8) * 3)
|
||||||
|
|
||||||
|
|
||||||
|
proc fromQuad*(input: array[4, uint8]): uint =
|
||||||
|
## Rebuilts the output of toQuad into
|
||||||
|
## an uint
|
||||||
|
copyMem(result.addr, unsafeAddr(input), sizeof(uint32))
|
||||||
|
|
||||||
|
|
||||||
|
proc fromLong*(input: array[8, uint8]): uint =
|
||||||
|
## Rebuilts the output of toQuad into
|
||||||
|
## an uint
|
||||||
|
copyMem(result.addr, unsafeAddr(input), sizeof(uint64))
|
|
@ -11,10 +11,8 @@
|
||||||
# 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.
|
||||||
import ../frontend/meta/ast
|
|
||||||
import ../frontend/meta/errors
|
import ../frontend/meta/errors
|
||||||
import ../frontend/meta/bytecode
|
import ../frontend/meta/bytecode
|
||||||
import ../frontend/meta/token
|
|
||||||
import ../config
|
import ../config
|
||||||
import multibyte
|
import multibyte
|
||||||
import ../frontend/compiler
|
import ../frontend/compiler
|
||||||
|
@ -85,14 +83,6 @@ proc bytesToString(self: Serializer, input: seq[byte]): string =
|
||||||
result.add(char(b))
|
result.add(char(b))
|
||||||
|
|
||||||
|
|
||||||
proc bytesToInt(self: Serializer, input: array[8, byte]): int =
|
|
||||||
copyMem(result.addr, input.unsafeAddr, sizeof(int))
|
|
||||||
|
|
||||||
|
|
||||||
proc bytesToInt(self: Serializer, input: array[3, byte]): int =
|
|
||||||
copyMem(result.addr, input.unsafeAddr, sizeof(byte) * 3)
|
|
||||||
|
|
||||||
|
|
||||||
proc extend[T](s: var seq[T], a: openarray[T]) =
|
proc extend[T](s: var seq[T], a: openarray[T]) =
|
||||||
## Extends s with the elements of a
|
## Extends s with the elements of a
|
||||||
for e in a:
|
for e in a:
|
||||||
|
@ -107,122 +97,65 @@ proc writeHeaders(self: Serializer, stream: var seq[byte], file: string) =
|
||||||
stream.add(byte(PEON_VERSION.patch))
|
stream.add(byte(PEON_VERSION.patch))
|
||||||
stream.add(byte(len(PEON_BRANCH)))
|
stream.add(byte(len(PEON_BRANCH)))
|
||||||
stream.extend(self.toBytes(PEON_BRANCH))
|
stream.extend(self.toBytes(PEON_BRANCH))
|
||||||
if len(PEON_COMMIT_HASH) != 40:
|
|
||||||
self.error("the commit hash must be exactly 40 characters long")
|
|
||||||
stream.extend(self.toBytes(PEON_COMMIT_HASH))
|
stream.extend(self.toBytes(PEON_COMMIT_HASH))
|
||||||
stream.extend(self.toBytes(getTime().toUnixFloat().int()))
|
stream.extend(self.toBytes(getTime().toUnixFloat().int()))
|
||||||
stream.extend(self.toBytes(computeSHA256(file)))
|
stream.extend(self.toBytes(computeSHA256(file)))
|
||||||
|
|
||||||
|
|
||||||
proc writeConstants(self: Serializer, stream: var seq[byte]) =
|
proc writeConstants(self: Serializer, stream: var seq[byte]) =
|
||||||
## Writes the constants table in-place into the given stream
|
## Writes the constants table in-place into the
|
||||||
|
## given stream
|
||||||
|
stream.extend(self.chunk.consts.len().toQuad())
|
||||||
for constant in self.chunk.consts:
|
for constant in self.chunk.consts:
|
||||||
case constant.kind:
|
stream.add(constant)
|
||||||
of intExpr, floatExpr:
|
|
||||||
stream.add(0x1)
|
|
||||||
stream.extend(len(constant.token.lexeme).toTriple())
|
|
||||||
stream.extend(self.toBytes(constant.token.lexeme))
|
|
||||||
of strExpr:
|
|
||||||
stream.add(0x2)
|
|
||||||
var temp: byte
|
|
||||||
var strip: int = 2
|
|
||||||
var offset: int = 1
|
|
||||||
case constant.token.lexeme[0]:
|
|
||||||
of 'f':
|
|
||||||
strip = 3
|
|
||||||
inc(offset)
|
|
||||||
temp = 0x2
|
|
||||||
of 'b':
|
|
||||||
strip = 3
|
|
||||||
inc(offset)
|
|
||||||
temp = 0x1
|
|
||||||
else:
|
|
||||||
strip = 2
|
|
||||||
temp = 0x0
|
|
||||||
stream.extend((len(constant.token.lexeme) - strip).toTriple()) # Removes the quotes from the length count as they're not written
|
|
||||||
stream.add(temp)
|
|
||||||
stream.add(self.toBytes(constant.token.lexeme[offset..^2]))
|
|
||||||
of identExpr:
|
|
||||||
stream.add(0x0)
|
|
||||||
stream.extend(len(constant.token.lexeme).toTriple())
|
|
||||||
stream.add(self.toBytes(constant.token.lexeme))
|
|
||||||
else:
|
|
||||||
self.error(&"unknown constant kind in chunk table ({constant.kind})")
|
|
||||||
stream.add(0x59) # End marker
|
|
||||||
|
|
||||||
|
|
||||||
proc readConstants(self: Serializer, stream: seq[byte]): int =
|
|
||||||
## Reads the constant table from the given stream and
|
|
||||||
## adds each constant to the chunk object.
|
|
||||||
## Returns the number of bytes that were processed in
|
|
||||||
## the stream
|
|
||||||
var stream = stream
|
|
||||||
var count: int = 0
|
|
||||||
while true:
|
|
||||||
case stream[0]:
|
|
||||||
of 0x59:
|
|
||||||
inc(count)
|
|
||||||
break
|
|
||||||
of 0x2:
|
|
||||||
stream = stream[1..^1]
|
|
||||||
let size = self.bytesToInt([stream[0], stream[1], stream[2]])
|
|
||||||
stream = stream[3..^1]
|
|
||||||
var s = newStrExpr(Token(lexeme: ""))
|
|
||||||
case stream[0]:
|
|
||||||
of 0x0:
|
|
||||||
discard
|
|
||||||
of 0x1:
|
|
||||||
s.token.lexeme.add("b")
|
|
||||||
of 0x2:
|
|
||||||
s.token.lexeme.add("f")
|
|
||||||
else:
|
|
||||||
self.error(&"unknown string modifier in chunk table (0x{stream[0].toHex()}")
|
|
||||||
stream = stream[1..^1]
|
|
||||||
s.token.lexeme.add("\"")
|
|
||||||
for i in countup(0, size - 1):
|
|
||||||
s.token.lexeme.add(cast[char](stream[i]))
|
|
||||||
s.token.lexeme.add("\"")
|
|
||||||
stream = stream[size..^1]
|
|
||||||
self.chunk.consts.add(s)
|
|
||||||
inc(count, size + 5)
|
|
||||||
of 0x1:
|
|
||||||
stream = stream[1..^1]
|
|
||||||
inc(count)
|
|
||||||
let size = self.bytesToInt([stream[0], stream[1], stream[2]])
|
|
||||||
stream = stream[3..^1]
|
|
||||||
inc(count, 3)
|
|
||||||
var tok: Token = new(Token)
|
|
||||||
tok.lexeme = self.bytesToString(stream[0..<size])
|
|
||||||
if "." in tok.lexeme:
|
|
||||||
tok.kind = Float
|
|
||||||
self.chunk.consts.add(newFloatExpr(tok))
|
|
||||||
else:
|
|
||||||
tok.kind = Integer
|
|
||||||
self.chunk.consts.add(newIntExpr(tok))
|
|
||||||
stream = stream[size..^1]
|
|
||||||
inc(count, size)
|
|
||||||
of 0x0:
|
|
||||||
stream = stream[1..^1]
|
|
||||||
let size = self.bytesToInt([stream[0], stream[1], stream[2]])
|
|
||||||
stream = stream[3..^1]
|
|
||||||
self.chunk.consts.add(newIdentExpr(Token(
|
|
||||||
lexeme: self.bytesToString(stream[0..<size]))))
|
|
||||||
# TODO
|
|
||||||
# discard self.chunk.addConstant(newIdentExpr(Token(lexeme: self.bytesToString(stream[0..<size]))))
|
|
||||||
stream = stream[size..^1]
|
|
||||||
inc(count, size + 4)
|
|
||||||
else:
|
|
||||||
self.error(&"unknown constant kind in chunk table (0x{stream[0].toHex()})")
|
|
||||||
result = count
|
|
||||||
|
|
||||||
|
|
||||||
proc writeCode(self: Serializer, stream: var seq[byte]) =
|
proc writeCode(self: Serializer, stream: var seq[byte]) =
|
||||||
## Writes the bytecode from the given chunk to the given source
|
## Writes the bytecode from the given chunk to the
|
||||||
## stream
|
## given source stream
|
||||||
stream.extend(self.chunk.code.len.toTriple())
|
stream.extend(self.chunk.code.len.toTriple())
|
||||||
stream.extend(self.chunk.code)
|
stream.extend(self.chunk.code)
|
||||||
|
|
||||||
|
|
||||||
|
proc readHeaders(self: Serializer, stream: seq[byte], serialized: Serialized): int =
|
||||||
|
## Reads the bytecode headers from a given stream
|
||||||
|
## of bytes
|
||||||
|
var stream = stream
|
||||||
|
if stream[0..<len(BYTECODE_MARKER)] != self.toBytes(BYTECODE_MARKER):
|
||||||
|
self.error("malformed bytecode marker")
|
||||||
|
result += len(BYTECODE_MARKER)
|
||||||
|
stream = stream[len(BYTECODE_MARKER)..^1]
|
||||||
|
serialized.peonVer = (major: int(stream[0]), minor: int(stream[1]), patch: int(stream[2]))
|
||||||
|
stream = stream[3..^1]
|
||||||
|
result += 3
|
||||||
|
let branchLength = stream[0]
|
||||||
|
stream = stream[1..^1]
|
||||||
|
result += 1
|
||||||
|
serialized.peonBranch = self.bytesToString(stream[0..<branchLength])
|
||||||
|
stream = stream[branchLength..^1]
|
||||||
|
result += int(branchLength)
|
||||||
|
serialized.commitHash = self.bytesToString(stream[0..<40]).toLowerAscii()
|
||||||
|
stream = stream[40..^1]
|
||||||
|
result += 40
|
||||||
|
serialized.compileDate = int(fromLong([stream[0], stream[1], stream[2],
|
||||||
|
stream[3], stream[4], stream[5], stream[6], stream[7]]))
|
||||||
|
stream = stream[8..^1]
|
||||||
|
result += 8
|
||||||
|
serialized.fileHash = self.bytesToString(stream[0..<32]).toHex().toLowerAscii()
|
||||||
|
result += 32
|
||||||
|
|
||||||
|
|
||||||
|
proc readConstants(self: Serializer, stream: seq[byte]): int =
|
||||||
|
## Reads the constant table from the given stream
|
||||||
|
## of bytes
|
||||||
|
let size = [stream[0], stream[1], stream[2], stream[3]].fromQuad()
|
||||||
|
result += 4
|
||||||
|
var stream = stream[4..^1]
|
||||||
|
for i in countup(0, int(size) - 1):
|
||||||
|
self.chunk.consts.add(stream[i])
|
||||||
|
inc(result)
|
||||||
|
|
||||||
|
|
||||||
proc readCode(self: Serializer, stream: seq[byte]): int =
|
proc readCode(self: Serializer, stream: seq[byte]): int =
|
||||||
## Reads the bytecode from a given stream and writes
|
## Reads the bytecode from a given stream and writes
|
||||||
## it into the given chunk
|
## it into the given chunk
|
||||||
|
@ -262,23 +195,7 @@ proc loadBytes*(self: Serializer, stream: seq[byte]): Serialized =
|
||||||
self.chunk = result.chunk
|
self.chunk = result.chunk
|
||||||
var stream = stream
|
var stream = stream
|
||||||
try:
|
try:
|
||||||
if stream[0..<len(BYTECODE_MARKER)] != self.toBytes(BYTECODE_MARKER):
|
stream = stream[self.readHeaders(stream, result)..^1]
|
||||||
self.error("malformed bytecode marker")
|
|
||||||
stream = stream[len(BYTECODE_MARKER)..^1]
|
|
||||||
result.peonVer = (major: int(stream[0]), minor: int(stream[1]),
|
|
||||||
patch: int(stream[2]))
|
|
||||||
stream = stream[3..^1]
|
|
||||||
let branchLength = stream[0]
|
|
||||||
stream = stream[1..^1]
|
|
||||||
result.peonBranch = self.bytesToString(stream[0..<branchLength])
|
|
||||||
stream = stream[branchLength..^1]
|
|
||||||
result.commitHash = self.bytesToString(stream[0..<40]).toLowerAscii()
|
|
||||||
stream = stream[40..^1]
|
|
||||||
result.compileDate = self.bytesToInt([stream[0], stream[1], stream[2],
|
|
||||||
stream[3], stream[4], stream[5], stream[6], stream[7]])
|
|
||||||
stream = stream[8..^1]
|
|
||||||
result.fileHash = self.bytesToString(stream[0..<32]).toHex().toLowerAscii()
|
|
||||||
stream = stream[32..^1]
|
|
||||||
stream = stream[self.readConstants(stream)..^1]
|
stream = stream[self.readConstants(stream)..^1]
|
||||||
stream = stream[self.readCode(stream)..^1]
|
stream = stream[self.readCode(stream)..^1]
|
||||||
except IndexDefect:
|
except IndexDefect:
|
||||||
|
|
Loading…
Reference in New Issue