From c7500f610e6e857884f193a73aab2dbb4aa87ac9 Mon Sep 17 00:00:00 2001 From: Mattia Giambirtone Date: Wed, 4 May 2022 14:27:15 +0200 Subject: [PATCH] Renamed all infer* functions to inferType, minor changes to signatures of declaration() and statement() --- src/frontend/compiler.nim | 101 +++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 55 deletions(-) diff --git a/src/frontend/compiler.nim b/src/frontend/compiler.nim index 36620d1..49439d9 100644 --- a/src/frontend/compiler.nim +++ b/src/frontend/compiler.nim @@ -35,12 +35,9 @@ export multibyte type Name = ref object ## A compile-time wrapper around - ## statically resolved names. - ## Depth indicates to which scope - ## the variable belongs, zero meaning - ## the global one + ## statically resolved names name: IdentExpr # Name of the identifier - owner: string # Owner of the identifier + owner: string # Owner of the identifier (module) depth: int # Scope depth isPrivate: bool # Is this name private? isConst: bool # Is this a constant? @@ -60,10 +57,11 @@ type ## statements start: int # Position in the bytecode where the loop starts depth: int # Scope depth where the loop is located - breakPos: seq[int] # List of positions where + breakPos: seq[int] # List of positions into our bytecode where we need to + # patch jumps. Used for break statements Compiler* = ref object - ## A wrapper around the compiler's state + ## A wrapper around the Peon compiler's state # The bytecode chunk where we write code to chunk: Chunk @@ -122,14 +120,14 @@ proc newCompiler*(enableOptimizations: bool = true): Compiler = ## Forward declarations -proc expression(self: Compiler, node: ASTNode) -proc statement(self: Compiler, node: ASTNode) -proc declaration(self: Compiler, node: ASTNode) +proc expression(self: Compiler, node: Expression) +proc statement(self: Compiler, node: Statement) +proc declaration(self: Compiler, node: Declaration) proc peek(self: Compiler, distance: int = 0): ASTNode proc identifier(self: Compiler, node: IdentExpr) proc varDecl(self: Compiler, node: VarDecl) -proc inferValueType(self: Compiler, node: ASTNode): Type -proc inferExprType(self: Compiler, node: ASTNode): Type +proc inferType(self: Compiler, node: LiteralExpr): Type +proc inferType(self: Compiler, node: Expression): Type ## End of forward declarations ## Public getter for nicer error formatting @@ -212,7 +210,7 @@ proc makeConstant(self: Compiler, val: LiteralExpr): array[3, uint8] = proc emitConstant(self: Compiler, obj: LiteralExpr) = ## Emits a LoadConstant instruction along ## with its operand - case self.inferExprType(obj).kind: + case self.inferType(obj).kind: of Int64: self.emitByte(LoadInt64) else: @@ -351,11 +349,10 @@ proc detectClosureVariable(self: Compiler, name: IdentExpr, depth: int = self.sc -proc inferValueType(self: Compiler, node: ASTNode): Type = +proc inferType(self: Compiler, node: LiteralExpr): Type = ## Infers the type of a given literal expression case node.kind: of intExpr, binExpr, octExpr, hexExpr: - let node = LiteralExpr(node) let size = node.token.lexeme.split("'") if len(size) notin 1..2: self.error("invalid state: inferValueType -> invalid size specifier (This is an internal error and most likely a bug!)") @@ -367,7 +364,6 @@ proc inferValueType(self: Compiler, node: ASTNode): Type = else: self.error(&"invalid type specifier '{size[1]}' for int") of floatExpr: - let node = LiteralExpr(node) let size = node.token.lexeme.split("'") if len(size) notin 1..2: self.error("invalid state: inferValueType -> invalid size specifier (This is an internal error and most likely a bug!)") @@ -392,33 +388,33 @@ proc inferValueType(self: Compiler, node: ASTNode): Type = discard # TODO -proc inferExprType(self: Compiler, node: ASTNode): Type = +proc inferType(self: Compiler, node: Expression): Type = ## Infers the type of a given expression and ## returns it case node.kind: of identExpr: - var node = IdentExpr(node) - var name = self.resolve(node) - return name.valueType + let name = self.resolve(IdentExpr(node)) + if name != nil: + return name.valueType of unaryExpr: - return self.inferValueType(UnaryExpr(node).a) + return self.inferType(UnaryExpr(node).a) of binaryExpr: - var node = BinaryExpr(node) - var a = self.inferExprType(node.a) - var b = self.inferExprType(node.b) - if a == nil or b == nil: + let node = BinaryExpr(node) + var a = self.inferType(node.a) + var b = self.inferType(node.b) + if a != b: return nil return a of {intExpr, hexExpr, binExpr, octExpr, strExpr, falseExpr, trueExpr, infExpr, nanExpr, floatExpr, nilExpr }: - return self.inferValueType(node) + return self.inferType(LiteralExpr(node)) else: discard # Unreachable -proc inferDeclType(self: Compiler, node: Declaration): Type = +proc inferType(self: Compiler, node: Declaration): Type = ## Infers the type of a given declaration if it's ## not already defined and returns it case node.kind: @@ -433,7 +429,7 @@ proc inferDeclType(self: Compiler, node: Declaration): Type = if resolved != nil: return resolved.valueType else: - return self.inferExprType(node.value) + return self.inferType(node.value) else: return # Unreachable @@ -453,7 +449,7 @@ proc typeToStr(self: Compiler, typ: Type): string = of funDecl: var node = FunDecl(typ.node) for i, argument in node.arguments: - result &= &"{argument.name.token.lexeme}: {self.typeToStr(self.inferExprType(argument.name))}" + result &= &"{argument.name.token.lexeme}: {self.typeToStr(self.inferType(argument.name))}" if i < node.arguments.len(): result &= ", " result &= ")" @@ -479,7 +475,7 @@ proc toIntrinsic(self: Compiler, typ: Expression): Type = of trueExpr, falseExpr, intExpr, floatExpr: return typ.token.lexeme.toIntrinsic() of identExpr: - let inferred = self.inferExprType(typ) + let inferred = self.inferType(typ) if inferred != nil: return else: @@ -686,7 +682,7 @@ proc declareName(self: Compiler, node: Declaration) = isPrivate: node.isPrivate, owner: self.currentModule, isConst: node.isConst, - valueType: Type(kind: self.inferExprType(node.value).kind, node: node), + valueType: Type(kind: self.inferType(node.value).kind, node: node), codePos: self.chunk.code.len(), isLet: node.isLet)) self.emitByte(StoreVar) @@ -696,8 +692,7 @@ proc declareName(self: Compiler, node: Declaration) = # Declares the function's name in the # current scope but no StoreVar is emitted # because a function's name is only useful - # at compile time and has no runtime meaning - # given that we implement functions with jumps + # at compile time self.names.add(Name(depth: self.scopeDepth, isPrivate: node.isPrivate, isConst: false, @@ -714,7 +709,7 @@ proc declareName(self: Compiler, node: Declaration) = owner: self.currentModule, isConst: false, name: argument.name, - valueType: self.inferExprType(argument.name), + valueType: self.inferType(argument.name), codePos: self.chunk.code.len(), isLet: false)) self.emitByte(StoreVar) @@ -759,15 +754,15 @@ proc assignment(self: Compiler, node: ASTNode) = ## Compiles assignment expressions case node.kind: of assignExpr: - var node = AssignExpr(node) + let node = AssignExpr(node) let name = IdentExpr(node.name) let r = self.resolve(name) if r == nil: - self.error(&"assignment to undeclared name '{node.name}'") + self.error(&"assignment to undeclared name '{name.token.lexeme}'") elif r.isConst: - self.error(&"cannot assign to '{node.name}'") + self.error(&"cannot assign to '{name.token.lexeme}'") elif r.isLet: - self.error(&"cannot reassign '{node.name}'") + self.error(&"cannot reassign '{name.token.lexeme}'") self.expression(node.value) let t = self.getStackPos(name) let index = t.pos @@ -800,11 +795,7 @@ proc assignment(self: Compiler, node: ASTNode) = # what values is set to a given # offset in a dynamic array, so we only # need to perform the operation as usual - # and then store it. We could combine - # everything into a single opcode, but - # that would require variants of each - # one for regular stack variables as - # well as closed-over ones + # and then store it again if index != -1: if not t.closedOver: self.emitByte(StoreVar) @@ -814,7 +805,7 @@ proc assignment(self: Compiler, node: ASTNode) = else: self.error(&"reference to undeclared name '{node.token.lexeme}'") of setItemExpr: - discard + let typ = self.inferType(SetItemExpr(node)) # TODO else: self.error(&"invalid AST node of kind {node.kind} at assignment(): {node} (This is an internal error and most likely a bug)") @@ -843,7 +834,7 @@ proc endScope(self: Compiler) = # we can emit a PopN instruction. This is true for # 99.99999% of the use cases of the language (who the # hell is going to use 65 THOUSAND local variables?), but - # if you'll ever use more then JAPL will emit a PopN instruction + # if you'll ever use more then Peon will emit a PopN instruction # for the first 65 thousand and change local variables and then # emit another batch of plain ol' Pop instructions for the rest if popped <= uint16.high().int(): @@ -922,9 +913,9 @@ proc whileStmt(self: Compiler, node: WhileStmt) = self.emitLoop(start) -proc expression(self: Compiler, node: ASTNode) = +proc expression(self: Compiler, node: Expression) = ## Compiles all expressions - if self.inferExprType(node) == nil: + if self.inferType(node) == nil: if node.kind != identExpr: # So we can raise a more appropriate # error in self.identifier() @@ -989,8 +980,8 @@ proc deferStmt(self: Compiler, node: DeferStmt) = proc returnStmt(self: Compiler, node: ReturnStmt) = ## Compiles return statements. An empty return ## implicitly returns nil - let returnType = self.inferExprType(node.value) - let typ = self.inferDeclType(self.currentFunction) + let returnType = self.inferType(node.value) + let typ = self.inferType(self.currentFunction) if returnType == nil and self.currentFunction.returnType != nil: self.error(&"expected return value of type '{self.currentFunction.returnType.token.lexeme}', but expression has no type") elif self.currentFunction.returnType == nil: @@ -1057,7 +1048,7 @@ proc assertStmt(self: Compiler, node: AssertStmt) = self.emitByte(OpCode.Assert) -proc statement(self: Compiler, node: ASTNode) = +proc statement(self: Compiler, node: Statement) = ## Compiles all statements case node.kind: of exprStmt: @@ -1100,13 +1091,13 @@ proc statement(self: Compiler, node: ASTNode) = of NodeKind.tryStmt: discard else: - self.expression(node) + self.expression(Expression(node)) proc varDecl(self: Compiler, node: VarDecl) = ## Compiles variable declarations let kind = self.toIntrinsic(node.valueType) - let typ = self.inferExprType(node.value) + let typ = self.inferType(node.value) if kind == nil and typ == nil: self.error(&"cannot determine the type of '{node.name.token.lexeme}'") elif typ != kind and kind != nil: @@ -1161,7 +1152,7 @@ proc funDecl(self: Compiler, node: FunDecl) = -proc declaration(self: Compiler, node: ASTNode) = +proc declaration(self: Compiler, node: Declaration) = ## Compiles all declarations case node.kind: of NodeKind.varDecl: @@ -1169,7 +1160,7 @@ proc declaration(self: Compiler, node: ASTNode) = of NodeKind.funDecl: self.funDecl(FunDecl(node)) else: - self.statement(node) + self.statement(Statement(node)) proc compile*(self: Compiler, ast: seq[ASTNode], file: string): Chunk = @@ -1184,7 +1175,7 @@ proc compile*(self: Compiler, ast: seq[ASTNode], file: string): Chunk = self.currentModule = self.file self.current = 0 while not self.done(): - self.declaration(self.step()) + self.declaration(Declaration(self.step())) if self.ast.len() > 0: # *Technically* an empty program is a valid program self.endScope()