diff --git a/src/backend/types.nim b/src/backend/types.nim index 4a787d0..e15c93b 100644 --- a/src/backend/types.nim +++ b/src/backend/types.nim @@ -20,7 +20,7 @@ type Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Float32, Float64, Char, Byte, String, Function, CustomType, - Nil, Nan, Bool, Inf + Nil, Nan, Bool, Inf, Reference, Pointer PeonObject* = object ## A generic Peon object case kind*: ObjectKind: @@ -58,5 +58,7 @@ type `float`*: float of Function: ip*: uint32 + of Reference, Pointer: + value*: ptr PeonObject else: discard # TODO diff --git a/src/backend/vm.nim b/src/backend/vm.nim index b28b141..e5712bf 100644 --- a/src/backend/vm.nim +++ b/src/backend/vm.nim @@ -426,7 +426,7 @@ proc dispatch*(self: PeonVM) = # stack self.push(self.closedOver[self.readLong()]) of PopClosure: - # Pops a closed-over variable off the closure + # Removes a closed-over variable from the closure # array discard self.closedOver.pop() of LoadVar: @@ -505,7 +505,7 @@ proc dispatch*(self: PeonVM) = proc run*(self: PeonVM, chunk: Chunk) = - ## Executes a piece of Peon bytecode. + ## Executes a piece of Peon bytecode self.chunk = chunk self.frames = @[] self.calls = @[] diff --git a/src/frontend/compiler.nim b/src/frontend/compiler.nim index 2fff82f..753fd89 100644 --- a/src/frontend/compiler.nim +++ b/src/frontend/compiler.nim @@ -19,6 +19,7 @@ import ../util/multibyte import lexer import parser +import tables import strformat import algorithm import parseutils @@ -137,9 +138,10 @@ type # keep track of where to jump) currentLoop: Loop # Are we in REPL mode? If so, Pop instructions - # for expression statements emit a special - # PopRepl instruction that stores the value - # to be printed once the expression is evaluated + # for expression statements at the top level are + # swapped for a special PopRepl instruction that + # prints the result of the expression once it is + # evaluated replMode: bool # The current module being compiled # (used to restrict access to statically @@ -159,25 +161,10 @@ type deferred: seq[uint8] # List of closed-over variables closedOver: seq[Name] + # Keeps track of stack frames frames: seq[int] - - -proc `$`(self: Name): string = - result &= &"Name(name='{self.name.name.lexeme}', depth={self.depth}, codePos={self.codePos})" - - -proc newCompiler*(enableOptimizations: bool = true, replMode: bool = false): Compiler = - ## Initializes a new Compiler object - new(result) - result.ast = @[] - result.current = 0 - result.file = "" - result.names = @[] - result.scopeDepth = 0 - result.currentFunction = nil - result.enableOptimizations = enableOptimizations - result.replMode = replMode - result.currentModule = "" + # Compiler procedures called by pragmas + compilerProcs: TableRef[string, proc (self: Compiler, pragma: Pragma, node: ASTNode)] ## Forward declarations @@ -194,8 +181,29 @@ proc findByName(self: Compiler, name: string): seq[Name] proc findByType(self: Compiler, name: string, kind: Type): seq[Name] proc compareTypes(self: Compiler, a, b: Type): bool proc patchReturnAddress(self: Compiler, pos: int) +proc handleMagicPragma(self: Compiler, pragma: Pragma, node: ASTnode) +proc handlePurePragma(self: Compiler, pragma: Pragma, node: ASTnode) + ## End of forward declarations + +proc newCompiler*(enableOptimizations: bool = true, replMode: bool = false): Compiler = + ## Initializes a new Compiler object + new(result) + result.ast = @[] + result.current = 0 + result.file = "" + result.names = @[] + result.scopeDepth = 0 + result.currentFunction = nil + result.enableOptimizations = enableOptimizations + result.replMode = replMode + result.currentModule = "" + result.compilerProcs = newTable[string, proc (self: Compiler, pragma: Pragma, node: ASTNode)]() + result.compilerProcs["magic"] = handleMagicPragma + result.compilerProcs["pure"] = handlePurePragma + + ## Public getter for nicer error formatting proc getCurrentNode*(self: Compiler): ASTNode = (if self.current >= self.ast.len(): self.ast[^1] else: self.ast[self.current - 1]) @@ -211,25 +219,24 @@ proc peek(self: Compiler, distance: int = 0): ASTNode = ## AST node in the tree is returned. A negative ## distance may be used to retrieve previously ## consumed AST nodes - if self.ast.high() == -1 or self.current + distance > self.ast.high() or - self.current + distance < 0: + if self.ast.high() == -1 or self.current + distance > self.ast.high() or self.current + distance < 0: result = self.ast[^1] else: result = self.ast[self.current + distance] -proc done(self: Compiler): bool = +proc done(self: Compiler): bool {.inline.} = ## Returns true if the compiler is done ## compiling, false otherwise result = self.current > self.ast.high() -proc error(self: Compiler, message: string) {.raises: [CompileError].} = +proc error(self: Compiler, message: string) {.raises: [CompileError], inline.} = ## Raises a CompileError exception raise CompileError(msg: message, node: self.getCurrentNode(), file: self.file, module: self.currentModule) -proc step(self: Compiler): ASTNode = +proc step(self: Compiler): ASTNode {.inline.} = ## Steps to the next node and returns ## the consumed one result = self.peek() @@ -237,7 +244,7 @@ proc step(self: Compiler): ASTNode = self.current += 1 -proc emitByte(self: Compiler, byt: OpCode | uint8) = +proc emitByte(self: Compiler, byt: OpCode | uint8) {.inline.} = ## Emits a single byte, writing it to ## the current chunk being compiled when DEBUG_TRACE_COMPILER: @@ -245,7 +252,7 @@ proc emitByte(self: Compiler, byt: OpCode | uint8) = self.chunk.write(uint8 byt, self.peek().token.line) -proc emitBytes(self: Compiler, bytarr: openarray[OpCode | uint8]) = +proc emitBytes(self: Compiler, bytarr: openarray[OpCode | uint8]) {.inline.} = ## Handy helper method to write arbitrary bytes into ## the current chunk, calling emitByte on each of its ## elements @@ -403,7 +410,7 @@ proc detectClosureVariable(self: Compiler, name: Name, depth: int = self.scopeDe ## unpredictably or crash if name.isNil() or name.depth == 0: return - elif name.depth < depth and not name.isClosedOver: + elif name.depth < depth and not name.isClosedOver: # Ding! The given name is closed over: we need to # change the dummy Jump instruction that self.declareName # put in place for us into a StoreClosure. We also update @@ -1521,9 +1528,16 @@ proc typeDecl(self: Compiler, node: TypeDecl) = # TODO +proc handleMagicPragma(self: Compiler, pragma: Pragma, node: ASTNode) = + ## Handles the "magic" pragma + + +proc handlePurePragma(self: Compiler, pragma: Pragma, node: ASTNode) = + ## Handles the "pure" pragma + + proc funDecl(self: Compiler, node: FunDecl) = ## Compiles function declarations - var function = self.currentFunction self.declareName(node) self.frames.add(self.names.high()) @@ -1553,7 +1567,10 @@ proc funDecl(self: Compiler, node: FunDecl) = # TODO: Forward declarations if not node.body.isNil(): if BlockStmt(node.body).code.len() == 0: - self.error("cannot declare function with empty body") + if node.pragmas.len() > 0: + discard + else: + self.error("cannot declare function with empty body") let fnType = self.inferType(node) let impl = self.findByType(node.name.token.lexeme, fnType) if impl.len() > 1: diff --git a/src/frontend/lexer.nim b/src/frontend/lexer.nim index 7b5f71e..550a39a 100644 --- a/src/frontend/lexer.nim +++ b/src/frontend/lexer.nim @@ -51,6 +51,7 @@ type file: string lines: seq[tuple[start, stop: int]] lastLine: int + spaces: int proc newSymbolTable: SymbolTable = @@ -173,6 +174,7 @@ proc newLexer*(self: Lexer = nil): Lexer = result.lines = @[] result.lastLine = 0 result.symbols = newSymbolTable() + result.spaces = 0 proc done(self: Lexer): bool = @@ -282,6 +284,8 @@ proc createToken(self: Lexer, tokenType: TokenType) = tok.kind = tokenType tok.lexeme = self.source[self.start..