Compiler support for BuildList, BuildSet, BuildDict and BuildTuple

This commit is contained in:
nocturn9x 2021-11-13 19:48:17 +01:00
parent 15a2403120
commit 318e72b9e1
7 changed files with 72 additions and 28 deletions

View File

@ -16,10 +16,12 @@ import meta/ast
import meta/errors import meta/errors
import meta/bytecode import meta/bytecode
import ../config import ../config
import ../util/multibyte
import strformat import strformat
import parseutils import parseutils
import sequtils
export ast export ast
@ -124,7 +126,7 @@ proc emitByte(self: Compiler, byt: OpCode|uint8) =
## to the current chunk being compiled ## to the current chunk being compiled
when DEBUG_TRACE_COMPILER: when DEBUG_TRACE_COMPILER:
echo &"DEBUG - Compiler: Emitting {$byt}" echo &"DEBUG - Compiler: Emitting {$byt}"
self.chunk.write(uint8 byt, self.peek(-1).token.line) self.chunk.write(uint8 byt, self.peek().token.line)
proc emitBytes(self: Compiler, byt1: OpCode|uint8, byt2: OpCode|uint8) = proc emitBytes(self: Compiler, byt1: OpCode|uint8, byt2: OpCode|uint8) =
@ -156,7 +158,7 @@ proc emitConstant(self: Compiler, obj: ASTNode) =
self.emitBytes(self.makeConstant(obj)) self.emitBytes(self.makeConstant(obj))
proc literal(self: Compiler, node: LiteralExpr) = proc literal(self: Compiler, node: ASTNode) =
## Emits instructions for literals such ## Emits instructions for literals such
## as singletons, strings, numbers and ## as singletons, strings, numbers and
## collections ## collections
@ -188,7 +190,7 @@ proc literal(self: Compiler, node: LiteralExpr) =
# will collapse all these other literals # will collapse all these other literals
# to nodes of kind intExpr, that can be # to nodes of kind intExpr, that can be
# disabled. This also allows us to catch # disabled. This also allows us to catch
# overflow errors before running any code # basic overflow errors before running any code
of hexExpr: of hexExpr:
var x: int var x: int
var y = HexExpr(node) var y = HexExpr(node)
@ -221,6 +223,31 @@ proc literal(self: Compiler, node: LiteralExpr) =
except ValueError: except ValueError:
self.error("floating point value out of range") self.error("floating point value out of range")
self.emitConstant(y) self.emitConstant(y)
of listExpr:
var y = ListExpr(node)
self.emitByte(BuildList)
self.emitBytes(y.members.len().toTriple()) # 24-bit integer, meaning list literals can have up to 2^24 elements
for member in y.members:
self.expression(member)
of tupleExpr:
var y = TupleExpr(node)
self.emitByte(BuildTuple)
self.emitBytes(y.members.len().toTriple())
for member in y.members:
self.expression(member)
of setExpr:
var y = SetExpr(node)
self.emitByte(BuildSet)
self.emitBytes(y.members.len().toTriple())
for member in y.members:
self.expression(member)
of dictExpr:
var y = DictExpr(node)
self.emitByte(BuildDict)
self.emitBytes(y.keys.len().toTriple())
for (key, value) in zip(y.keys, y.values):
self.expression(key)
self.expression(value)
else: else:
self.error(&"invalid AST node of kind {node.kind} at literal(): {node} (This is an internal error and most likely a bug)") self.error(&"invalid AST node of kind {node.kind} at literal(): {node} (This is an internal error and most likely a bug)")
@ -289,9 +316,12 @@ proc expression(self: Compiler, node: ASTNode) =
self.unary(UnaryExpr(node)) self.unary(UnaryExpr(node))
of binaryExpr: of binaryExpr:
self.binary(BinaryExpr(node)) self.binary(BinaryExpr(node))
of intExpr, hexExpr, binExpr, octExpr, strExpr, falseExpr, trueExpr, infExpr, nanExpr, floatExpr, of intExpr, hexExpr, binExpr, octExpr, strExpr, falseExpr, trueExpr, infExpr, nanExpr, floatExpr:
tupleExpr, dictExpr, setExpr, listExpr:
self.literal(LiteralExpr(node)) self.literal(LiteralExpr(node))
of tupleExpr, setExpr, listExpr:
self.literal(ListExpr(node))
of dictExpr:
self.literal(DictExpr(node))
else: else:
self.error(&"invalid AST node of kind {node.kind} at expression(): {node} (This is an internal error and most likely a bug)") # TODO self.error(&"invalid AST node of kind {node.kind} at expression(): {node} (This is an internal error and most likely a bug)") # TODO

View File

@ -312,6 +312,7 @@ proc isLiteral*(self: ASTNode): bool {.inline.} = self.isConst() or self.kind in
proc newIntExpr*(literal: Token): IntExpr = proc newIntExpr*(literal: Token): IntExpr =
result = IntExpr(kind: intExpr) result = IntExpr(kind: intExpr)
result.literal = literal result.literal = literal
result.token = literal
proc newOctExpr*(literal: Token): OctExpr = proc newOctExpr*(literal: Token): OctExpr =
@ -625,7 +626,7 @@ proc `$`*(self: ASTNode): string =
result &= &"SetItem(obj={self.obj}, name={self.value}, value={self.value})" result &= &"SetItem(obj={self.obj}, name={self.value}, value={self.value})"
of callExpr: of callExpr:
var self = CallExpr(self) var self = CallExpr(self)
result &= &"Call({self.callee}, arguments=(positionals=[{self.arguments.positionals.join(\", \")}], keyword=[{self.arguments.keyword.join(\", \")}]))" result &= &"""Call({self.callee}, arguments=(positionals=[{self.arguments.positionals.join(", ")}], keyword=[{self.arguments.keyword.join(", ")}]))"""
of unaryExpr: of unaryExpr:
var self = UnaryExpr(self) var self = UnaryExpr(self)
result &= &"Unary(Operator('{self.operator.lexeme}'), {self.a})" result &= &"Unary(Operator('{self.operator.lexeme}'), {self.a})"
@ -643,7 +644,7 @@ proc `$`*(self: ASTNode): string =
result &= &"Import({self.moduleName})" result &= &"Import({self.moduleName})"
of fromImportStmt: of fromImportStmt:
var self = FromImportStmt(self) var self = FromImportStmt(self)
result &= &"FromImport(fromModule={self.fromModule}, fromAttributes=[{self.fromAttributes.join(\", \")}])" result &= &"""FromImport(fromModule={self.fromModule}, fromAttributes=[{self.fromAttributes.join(", ")}])"""
of delStmt: of delStmt:
var self = DelStmt(self) var self = DelStmt(self)
result &= &"Del({self.name})" result &= &"Del({self.name})"
@ -655,7 +656,7 @@ proc `$`*(self: ASTNode): string =
result &= &"Raise({self.exception})" result &= &"Raise({self.exception})"
of blockStmt: of blockStmt:
var self = BlockStmt(self) var self = BlockStmt(self)
result &= &"Block([{self.code.join(\", \")}])" result &= &"""Block([{self.code.join(", ")}])"""
of whileStmt: of whileStmt:
var self = WhileStmt(self) var self = WhileStmt(self)
result &= &"While(condition={self.condition}, body={self.body})" result &= &"While(condition={self.condition}, body={self.body})"
@ -688,31 +689,31 @@ proc `$`*(self: ASTNode): string =
result &= &"Var(name={self.name}, value={self.value}, const={self.isConst}, static={self.isStatic}, private={self.isPrivate})" result &= &"Var(name={self.name}, value={self.value}, const={self.isConst}, static={self.isStatic}, private={self.isPrivate})"
of funDecl: of funDecl:
var self = FunDecl(self) var self = FunDecl(self)
result &= &"FunDecl(name={self.name}, body={self.body}, arguments=[{self.arguments.join(\", \")}], defaults=[{self.defaults.join(\", \")}], async={self.isAsync}, generator={self.isGenerator}, static={self.isStatic}, private={self.isPrivate})" result &= &"""FunDecl(name={self.name}, body={self.body}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], async={self.isAsync}, generator={self.isGenerator}, static={self.isStatic}, private={self.isPrivate})"""
of classDecl: of classDecl:
var self = ClassDecl(self) var self = ClassDecl(self)
result &= &"Class(name={self.name}, body={self.body}, parents=[{self.parents.join(\", \")}], static={self.isStatic}, private={self.isPrivate})" result &= &"""Class(name={self.name}, body={self.body}, parents=[{self.parents.join(", ")}], static={self.isStatic}, private={self.isPrivate})"""
of tupleExpr: of tupleExpr:
var self = TupleExpr(self) var self = TupleExpr(self)
result &= &"Tuple([{self.members.join(\", \")}])" result &= &"""Tuple([{self.members.join(", ")}])"""
of setExpr: of setExpr:
var self = SetExpr(self) var self = SetExpr(self)
result &= &"Set([{self.members.join(\", \")}])" result &= &"""Set([{self.members.join(", ")}])"""
of listExpr: of listExpr:
var self = ListExpr(self) var self = ListExpr(self)
result &= &"List([{self.members.join(\", \")}])" result &= &"""List([{self.members.join(", ")}])"""
of dictExpr: of dictExpr:
var self = DictExpr(self) var self = DictExpr(self)
result &= &"Dict(keys=[{self.keys.join(\", \")}], values=[{self.values.join(\", \")}])" result &= &"""Dict(keys=[{self.keys.join(", ")}], values=[{self.values.join(", ")}])"""
of lambdaExpr: of lambdaExpr:
var self = LambdaExpr(self) var self = LambdaExpr(self)
result &= &"Lambda(body={self.body}, arguments=[{self.arguments.join(\", \")}], defaults=[{self.defaults.join(\", \")}], generator={self.isGenerator})" result &= &"""Lambda(body={self.body}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generator={self.isGenerator})"""
of deferStmt: of deferStmt:
var self = DeferStmt(self) var self = DeferStmt(self)
result &= &"Defer({self.deferred})" result &= &"Defer({self.deferred})"
of sliceExpr: of sliceExpr:
var self = SliceExpr(self) var self = SliceExpr(self)
result &= &"Slice({self.slicee}, ends=[{self.ends.join(\", \")}])" result &= &"""Slice({self.slicee}, ends=[{self.ends.join(", ")}])"""
of tryStmt: of tryStmt:
var self = TryStmt(self) var self = TryStmt(self)
result &= &"TryStmt(body={self.body}, handlers={self.handlers}" result &= &"TryStmt(body={self.body}, handlers={self.handlers}"

View File

@ -15,6 +15,10 @@ import ast
import ../../util/multibyte import ../../util/multibyte
import strutils
import strformat
export ast export ast
@ -47,7 +51,10 @@ type
# argument to unary opcodes, while # argument to unary opcodes, while
# a and b represent arguments to binary # a and b represent arguments to binary
# opcodes. Other variable names may be # opcodes. Other variable names may be
# used for more complex opcodes # used for more complex opcodes. All
# arguments to opcodes (if they take
# arguments) come from popping off the
# stack
LoadConstant = 0u8, # Pushes constant at position x in the constant table onto the stack LoadConstant = 0u8, # Pushes constant at position x in the constant table onto the stack
# Binary operators # Binary operators
UnaryNegate, # Pushes the result of -x onto the stack UnaryNegate, # Pushes the result of -x onto the stack
@ -132,8 +139,6 @@ type
BuildTuple BuildTuple
const simpleInstructions* = {Return, BinaryAdd, BinaryMultiply, const simpleInstructions* = {Return, BinaryAdd, BinaryMultiply,
BinaryDivide, BinarySubtract, BinaryDivide, BinarySubtract,
BinaryMod, BinaryPow, Nil, BinaryMod, BinaryPow, Nil,
@ -147,14 +152,14 @@ const simpleInstructions* = {Return, BinaryAdd, BinaryMultiply,
UnaryNot, InPlaceAdd, InPlaceDivide, UnaryNot, InPlaceAdd, InPlaceDivide,
InPlaceFloorDiv, InPlaceMod, InPlaceMultiply, InPlaceFloorDiv, InPlaceMod, InPlaceMultiply,
InPlaceSubtract, BinaryFloorDiv, BinaryOf, Raise, InPlaceSubtract, BinaryFloorDiv, BinaryOf, Raise,
ReRaise, BeginTry, FinishTry, ReRaise, BeginTry, FinishTry, Yield, Await}
Yield, Await}
const constantInstructions* = {LoadConstant, DeclareName, const constantInstructions* = {LoadConstant, DeclareName,
LoadName, UpdateName, LoadName, UpdateName,
DeleteName} DeleteName}
const byteInstructions* = {UpdateNameFast, LoadNameFast, const byteInstructions* = {UpdateNameFast, LoadNameFast,
DeleteNameFast, Call} DeleteNameFast, Call}
const jumpInstructions* = {JumpIfFalse, Jump} const jumpInstructions* = {JumpIfFalse, Jump}
const collectionInstructions* = {BuildList, BuildDict, BuildSet, BuildTuple}
proc newChunk*(): Chunk = proc newChunk*(): Chunk =
@ -162,9 +167,13 @@ proc newChunk*(): Chunk =
result = Chunk(consts: @[], code: @[], lines: @[]) result = Chunk(consts: @[], code: @[], lines: @[])
proc `$`*(self: Chunk): string = &"""Chunk(consts=[{self.consts.join(", ")}], code=[{self.code.join(", ")}], lines=[{self.lines.join(", ")}])"""
proc write*(self: Chunk, newByte: uint8, line: int) = proc write*(self: Chunk, newByte: uint8, line: int) =
## Adds the given instruction at the provided line number ## Adds the given instruction at the provided line number
## to the given chunk object ## to the given chunk object
assert line > 0
if self.lines.high() >= 1 and self.lines[^2] == line: if self.lines.high() >= 1 and self.lines[^2] == line:
self.lines[^1] += 1 self.lines[^1] += 1
else: else:

View File

@ -213,6 +213,7 @@ proc primary(self: Parser): ASTNode =
result = newFloatExpr(self.step()) result = newFloatExpr(self.step())
of Integer: of Integer:
result = newIntExpr(self.step()) result = newIntExpr(self.step())
echo result.token
of Identifier: of Identifier:
result = newIdentExpr(self.step()) result = newIdentExpr(self.step())
of LeftParen: of LeftParen:

View File

@ -106,7 +106,7 @@ proc dumpBytes*(self: Serializer, chunk: Chunk, file, filename: string): seq[byt
result.extend(self.toBytes(computeSHA256(file))) result.extend(self.toBytes(computeSHA256(file)))
for constant in chunk.consts: for constant in chunk.consts:
case constant.kind: case constant.kind:
of intExpr: of intExpr, floatExpr:
result.add(0x1) result.add(0x1)
result.add(byte(len(constant.token.lexeme))) result.add(byte(len(constant.token.lexeme)))
result.extend(self.toBytes(constant.token.lexeme)) result.extend(self.toBytes(constant.token.lexeme))

View File

@ -20,7 +20,7 @@ const HEAP_GROW_FACTOR* = 2 # How much extra memory to allocate for dynamic ar
const MAX_STACK_FRAMES* = 800 # The maximum number of stack frames at any one time. Acts as a recursion limiter (1 frame = 1 call) const MAX_STACK_FRAMES* = 800 # The maximum number of stack frames at any one time. Acts as a recursion limiter (1 frame = 1 call)
const JAPL_VERSION* = (major: 0, minor: 4, patch: 0) const JAPL_VERSION* = (major: 0, minor: 4, patch: 0)
const JAPL_RELEASE* = "alpha" const JAPL_RELEASE* = "alpha"
const JAPL_COMMIT_HASH* = "b252749d0e5448b8fef64150299d8318362bc08c" const JAPL_COMMIT_HASH* = "5f33af5b2c909293bc4d0530b4d3d69645410153"
const JAPL_BRANCH* = "master" const JAPL_BRANCH* = "master"
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)

View File

@ -93,14 +93,16 @@ proc main() =
compiled = compiler.compile(optimized.tree, filename) compiled = compiler.compile(optimized.tree, filename)
echo "Compilation step:" echo "Compilation step:"
echo &"\tRaw byte stream: [{compiled.code.join(\", \")}]" stdout.write("\t")
echo "\tBytecode disassembler output below:\n" echo &"""Raw byte stream: [{compiled.code.join(", ")}]"""
echo "\n\nBytecode disassembler output below:\n"
disassembleChunk(compiled, filename) disassembleChunk(compiled, filename)
echo "" echo ""
serializedRaw = serializer.dumpBytes(compiled, source, filename) serializedRaw = serializer.dumpBytes(compiled, source, filename)
echo "Serialization step: " echo "Serialization step: "
echo &"\tRaw hex output: {serializedRaw.mapIt(toHex(it)).join(\"\").toLowerAscii()}" stdout.write("\t")
echo &"""Raw hex output: {serializedRaw.mapIt(toHex(it)).join("").toLowerAscii()}"""
echo "" echo ""
serialized = serializer.loadBytes(serializedRaw) serialized = serializer.loadBytes(serializedRaw)
@ -111,6 +113,7 @@ proc main() =
compileDate = fromUnix(serialized.compileDate).format("d/M/yyyy H:mm:ss") compileDate = fromUnix(serialized.compileDate).format("d/M/yyyy H:mm:ss")
echo &"\t\t- Compilation date & time: {compileDate}" echo &"\t\t- Compilation date & time: {compileDate}"
except: except:
raise
echo &"A Nim runtime exception occurred: {getCurrentExceptionMsg()}" echo &"A Nim runtime exception occurred: {getCurrentExceptionMsg()}"
continue continue