From 318e72b9e14bae5735c630f58bd0430ff04cb55c Mon Sep 17 00:00:00 2001 From: nocturn9x Date: Sat, 13 Nov 2021 19:48:17 +0100 Subject: [PATCH] Compiler support for BuildList, BuildSet, BuildDict and BuildTuple --- src/backend/compiler.nim | 42 ++++++++++++++++++++++++++++++----- src/backend/meta/ast.nim | 23 ++++++++++--------- src/backend/meta/bytecode.nim | 21 +++++++++++++----- src/backend/parser.nim | 1 + src/backend/serializer.nim | 2 +- src/config.nim | 2 +- src/main.nim | 9 +++++--- 7 files changed, 72 insertions(+), 28 deletions(-) diff --git a/src/backend/compiler.nim b/src/backend/compiler.nim index 81d4a55..39098c7 100644 --- a/src/backend/compiler.nim +++ b/src/backend/compiler.nim @@ -16,10 +16,12 @@ import meta/ast import meta/errors import meta/bytecode import ../config +import ../util/multibyte import strformat import parseutils +import sequtils export ast @@ -124,7 +126,7 @@ proc emitByte(self: Compiler, byt: OpCode|uint8) = ## to the current chunk being compiled when DEBUG_TRACE_COMPILER: 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) = @@ -156,7 +158,7 @@ proc emitConstant(self: Compiler, obj: ASTNode) = self.emitBytes(self.makeConstant(obj)) -proc literal(self: Compiler, node: LiteralExpr) = +proc literal(self: Compiler, node: ASTNode) = ## Emits instructions for literals such ## as singletons, strings, numbers and ## collections @@ -188,7 +190,7 @@ proc literal(self: Compiler, node: LiteralExpr) = # will collapse all these other literals # to nodes of kind intExpr, that can be # disabled. This also allows us to catch - # overflow errors before running any code + # basic overflow errors before running any code of hexExpr: var x: int var y = HexExpr(node) @@ -220,7 +222,32 @@ proc literal(self: Compiler, node: LiteralExpr) = assert parseFloat(y.literal.lexeme, x) == len(y.literal.lexeme) except ValueError: 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: 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)) of binaryExpr: self.binary(BinaryExpr(node)) - of intExpr, hexExpr, binExpr, octExpr, strExpr, falseExpr, trueExpr, infExpr, nanExpr, floatExpr, - tupleExpr, dictExpr, setExpr, listExpr: + of intExpr, hexExpr, binExpr, octExpr, strExpr, falseExpr, trueExpr, infExpr, nanExpr, floatExpr: self.literal(LiteralExpr(node)) + of tupleExpr, setExpr, listExpr: + self.literal(ListExpr(node)) + of dictExpr: + self.literal(DictExpr(node)) else: self.error(&"invalid AST node of kind {node.kind} at expression(): {node} (This is an internal error and most likely a bug)") # TODO diff --git a/src/backend/meta/ast.nim b/src/backend/meta/ast.nim index 9132110..06d2860 100644 --- a/src/backend/meta/ast.nim +++ b/src/backend/meta/ast.nim @@ -312,6 +312,7 @@ proc isLiteral*(self: ASTNode): bool {.inline.} = self.isConst() or self.kind in proc newIntExpr*(literal: Token): IntExpr = result = IntExpr(kind: intExpr) result.literal = literal + result.token = literal proc newOctExpr*(literal: Token): OctExpr = @@ -625,7 +626,7 @@ proc `$`*(self: ASTNode): string = result &= &"SetItem(obj={self.obj}, name={self.value}, value={self.value})" of callExpr: 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: var self = UnaryExpr(self) result &= &"Unary(Operator('{self.operator.lexeme}'), {self.a})" @@ -643,7 +644,7 @@ proc `$`*(self: ASTNode): string = result &= &"Import({self.moduleName})" of fromImportStmt: var self = FromImportStmt(self) - result &= &"FromImport(fromModule={self.fromModule}, fromAttributes=[{self.fromAttributes.join(\", \")}])" + result &= &"""FromImport(fromModule={self.fromModule}, fromAttributes=[{self.fromAttributes.join(", ")}])""" of delStmt: var self = DelStmt(self) result &= &"Del({self.name})" @@ -655,7 +656,7 @@ proc `$`*(self: ASTNode): string = result &= &"Raise({self.exception})" of blockStmt: var self = BlockStmt(self) - result &= &"Block([{self.code.join(\", \")}])" + result &= &"""Block([{self.code.join(", ")}])""" of whileStmt: var self = WhileStmt(self) 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})" of funDecl: 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: 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: var self = TupleExpr(self) - result &= &"Tuple([{self.members.join(\", \")}])" + result &= &"""Tuple([{self.members.join(", ")}])""" of setExpr: var self = SetExpr(self) - result &= &"Set([{self.members.join(\", \")}])" + result &= &"""Set([{self.members.join(", ")}])""" of listExpr: var self = ListExpr(self) - result &= &"List([{self.members.join(\", \")}])" + result &= &"""List([{self.members.join(", ")}])""" of dictExpr: 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: 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: var self = DeferStmt(self) result &= &"Defer({self.deferred})" of sliceExpr: var self = SliceExpr(self) - result &= &"Slice({self.slicee}, ends=[{self.ends.join(\", \")}])" + result &= &"""Slice({self.slicee}, ends=[{self.ends.join(", ")}])""" of tryStmt: var self = TryStmt(self) result &= &"TryStmt(body={self.body}, handlers={self.handlers}" diff --git a/src/backend/meta/bytecode.nim b/src/backend/meta/bytecode.nim index 37ea66d..994ae21 100644 --- a/src/backend/meta/bytecode.nim +++ b/src/backend/meta/bytecode.nim @@ -15,6 +15,10 @@ import ast import ../../util/multibyte +import strutils +import strformat + + export ast @@ -47,7 +51,10 @@ type # argument to unary opcodes, while # a and b represent arguments to binary # 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 # Binary operators UnaryNegate, # Pushes the result of -x onto the stack @@ -132,8 +139,6 @@ type BuildTuple - - const simpleInstructions* = {Return, BinaryAdd, BinaryMultiply, BinaryDivide, BinarySubtract, BinaryMod, BinaryPow, Nil, @@ -147,14 +152,14 @@ const simpleInstructions* = {Return, BinaryAdd, BinaryMultiply, UnaryNot, InPlaceAdd, InPlaceDivide, InPlaceFloorDiv, InPlaceMod, InPlaceMultiply, InPlaceSubtract, BinaryFloorDiv, BinaryOf, Raise, - ReRaise, BeginTry, FinishTry, - Yield, Await} + ReRaise, BeginTry, FinishTry, Yield, Await} const constantInstructions* = {LoadConstant, DeclareName, LoadName, UpdateName, DeleteName} const byteInstructions* = {UpdateNameFast, LoadNameFast, DeleteNameFast, Call} const jumpInstructions* = {JumpIfFalse, Jump} +const collectionInstructions* = {BuildList, BuildDict, BuildSet, BuildTuple} proc newChunk*(): Chunk = @@ -162,9 +167,13 @@ proc newChunk*(): Chunk = 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) = ## Adds the given instruction at the provided line number ## to the given chunk object + assert line > 0 if self.lines.high() >= 1 and self.lines[^2] == line: self.lines[^1] += 1 else: @@ -213,4 +222,4 @@ proc addConstant*(self: Chunk, constant: ASTNode): array[3, uint8] = ## Writes a constant to a chunk. Returns its index casted to a 3-byte ## sequence (array) self.consts.add(constant) - result = self.consts.high().toTriple() + result = self.consts.high().toTriple() \ No newline at end of file diff --git a/src/backend/parser.nim b/src/backend/parser.nim index 107c9ca..ff68bb2 100644 --- a/src/backend/parser.nim +++ b/src/backend/parser.nim @@ -213,6 +213,7 @@ proc primary(self: Parser): ASTNode = result = newFloatExpr(self.step()) of Integer: result = newIntExpr(self.step()) + echo result.token of Identifier: result = newIdentExpr(self.step()) of LeftParen: diff --git a/src/backend/serializer.nim b/src/backend/serializer.nim index 031d49e..703395e 100644 --- a/src/backend/serializer.nim +++ b/src/backend/serializer.nim @@ -106,7 +106,7 @@ proc dumpBytes*(self: Serializer, chunk: Chunk, file, filename: string): seq[byt result.extend(self.toBytes(computeSHA256(file))) for constant in chunk.consts: case constant.kind: - of intExpr: + of intExpr, floatExpr: result.add(0x1) result.add(byte(len(constant.token.lexeme))) result.extend(self.toBytes(constant.token.lexeme)) diff --git a/src/config.nim b/src/config.nim index 749aaae..dbf6ce5 100644 --- a/src/config.nim +++ b/src/config.nim @@ -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 JAPL_VERSION* = (major: 0, minor: 4, patch: 0) const JAPL_RELEASE* = "alpha" -const JAPL_COMMIT_HASH* = "b252749d0e5448b8fef64150299d8318362bc08c" +const JAPL_COMMIT_HASH* = "5f33af5b2c909293bc4d0530b4d3d69645410153" const JAPL_BRANCH* = "master" const DEBUG_TRACE_VM* = false # Traces VM execution const SKIP_STDLIB_INIT* = false # Skips stdlib initialization (can be imported manually) diff --git a/src/main.nim b/src/main.nim index 339547a..ed29e59 100644 --- a/src/main.nim +++ b/src/main.nim @@ -93,14 +93,16 @@ proc main() = compiled = compiler.compile(optimized.tree, filename) echo "Compilation step:" - echo &"\tRaw byte stream: [{compiled.code.join(\", \")}]" - echo "\tBytecode disassembler output below:\n" + stdout.write("\t") + echo &"""Raw byte stream: [{compiled.code.join(", ")}]""" + echo "\n\nBytecode disassembler output below:\n" disassembleChunk(compiled, filename) echo "" serializedRaw = serializer.dumpBytes(compiled, source, filename) 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 "" serialized = serializer.loadBytes(serializedRaw) @@ -111,6 +113,7 @@ proc main() = compileDate = fromUnix(serialized.compileDate).format("d/M/yyyy H:mm:ss") echo &"\t\t- Compilation date & time: {compileDate}" except: + raise echo &"A Nim runtime exception occurred: {getCurrentExceptionMsg()}" continue