diff --git a/src/frontend/compiler.nim b/src/frontend/compiler.nim index 7da4d32..f65675b 100644 --- a/src/frontend/compiler.nim +++ b/src/frontend/compiler.nim @@ -30,6 +30,7 @@ export ast export token export multibyte + type TypeKind* = enum ## An enumeration of compile-time @@ -44,7 +45,7 @@ type node*: ASTNode case kind*: TypeKind: of Function: - args*: seq[tuple[name: IdentExpr, kind: Type]] + args*: seq[Type] returnType*: Type else: discard @@ -163,6 +164,8 @@ proc inferType(self: Compiler, node: LiteralExpr): Type proc inferType(self: Compiler, node: Expression): Type 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 + ## End of forward declarations ## Public getter for nicer error formatting @@ -225,15 +228,17 @@ proc emitBytes(self: Compiler, bytarr: openarray[OpCode | uint8]) = proc makeConstant(self: Compiler, val: Expression, typ: Type): array[3, uint8] = ## Adds a constant to the current chunk's constant table ## and returns its index as a 3-byte array of uint8s + var v: int + discard parseInt(val.token.lexeme, v) case typ.kind: of UInt8, Int8: - result = self.chunk.writeConstant([uint8(parseInt(val.token.lexeme))]) + result = self.chunk.writeConstant([uint8(v)]) of Int16, UInt16: - result = self.chunk.writeConstant(parseInt(val.token.lexeme).toDouble()) + result = self.chunk.writeConstant(v.toDouble()) of Int32, UInt32: - result = self.chunk.writeConstant(parseInt(val.token.lexeme).toQuad()) + result = self.chunk.writeConstant(v.toQuad()) of Int64, UInt64: - result = self.chunk.writeConstant(parseInt(val.token.lexeme).toLong()) + result = self.chunk.writeConstant(v.toLong()) else: discard @@ -380,7 +385,6 @@ proc detectClosureVariable(self: Compiler, name: IdentExpr, self.chunk.code[entry.codePos + 2] = idx[1] self.chunk.code[entry.codePos + 3] = idx[2] -proc compareTypes(self: Compiler, a, b: Type): bool proc compareTypesWithNullNode(self: Compiler, a, b: Type): bool = ## Compares two types without using information from @@ -398,7 +402,7 @@ proc compareTypesWithNullNode(self: Compiler, a, b: Type): bool = elif not self.compareTypes(a.returnType, b.returnType): return false for (argA, argB) in zip(a.args, b.args): - if not self.compareTypes(argA.kind, argB.kind): + if not self.compareTypes(argA, argB): return false return true else: @@ -717,7 +721,7 @@ proc unary(self: Compiler, node: UnaryExpr) = ## and bitwise negation self.expression(node.a) # Pushes the operand onto the stack let valueType = self.inferType(node.a) - let impl = self.findByType(node.token.lexeme, Type(kind: Function, returnType: valueType, node: nil)) + let impl = self.findByType(node.token.lexeme, Type(kind: Function, returnType: valueType, node: nil, args: @[valueType])) if impl.len() == 0: self.error(&"cannot find a suitable implementation for '{node.token.lexeme}'") @@ -808,7 +812,7 @@ proc declareName(self: Compiler, node: Declaration) = isLet: false)) self.names[^1].valueType = self.inferType(argument.valueType) self.names[^1].valueType.node = argument.name - fn.valueType.args.add((node.name, self.names[^1].valueType)) + fn.valueType.args.add(self.names[^1].valueType) self.emitByte(LoadVar) self.emitBytes(self.names.high().toTriple()) else: @@ -1204,11 +1208,12 @@ proc varDecl(self: Compiler, node: VarDecl) = proc funDecl(self: Compiler, node: FunDecl) = ## Compiles function declarations - # A function's code is just compiled linearly # and then jumped over let jmp = self.emitJump(Jump) + var function = self.currentFunction self.declareName(node) + # TODO: Forward declarations if node.body != nil: if BlockStmt(node.body).code.len() == 0: self.error("Cannot declare function with empty body") @@ -1223,50 +1228,49 @@ proc funDecl(self: Compiler, node: FunDecl) = discard self.typeToStr(fn.valueType) msg &= &"- '{node.name.token.lexeme}' at line {node.token.line} of type {self.typeToStr(fn.valueType)}\n" self.error(msg) - # We store the current function - var function = self.currentFunction - self.currentFunction = node + # We store the current function + self.currentFunction = node - # Since the deferred array is a linear - # sequence of instructions and we want - # to keep track to whose function's each - # set of deferred instruction belongs, - # we record the length of the deferred - # array before compiling the function - # and use this info later to compile - # the try/finally block with the deferred - # code - var deferStart = self.deferred.len() + # Since the deferred array is a linear + # sequence of instructions and we want + # to keep track to whose function's each + # set of deferred instruction belongs, + # we record the length of the deferred + # array before compiling the function + # and use this info later to compile + # the try/finally block with the deferred + # code + var deferStart = self.deferred.len() - self.blockStmt(BlockStmt(node.body)) - # Yup, we're done. That was easy, huh? - # But after all functions are just named - # scopes, and we compile them just like that: - # we declare their name and arguments (before - # their body so recursion works) and then just - # handle them as a block statement (which takes - # care of incrementing self.scopeDepth so locals - # are resolved properly). There's a need for a bit - # of boilerplate code to make closures work, but - # that's about it - case self.currentFunction.kind: - of NodeKind.funDecl: - if not self.currentFunction.hasExplicitReturn: - let typ = self.inferType(self.currentFunction) - if self.currentFunction.returnType == nil and typ != nil: - self.error("non-empty return statement is not allowed in functions without an explicit return type") - if self.currentFunction.returnType != nil: - self.error("function has an explicit return type, but no explicit return statement was found") - self.emitByte(OpCode.Return) - of NodeKind.lambdaExpr: - if not LambdaExpr(Declaration(self.currentFunction)).hasExplicitReturn: - self.emitByte(OpCode.Return) - else: - discard # Unreachable - # Currently defer is not functional so we - # just pop the instructions - for i in countup(deferStart, self.deferred.len() - 1, 1): - self.deferred.delete(i) + self.blockStmt(BlockStmt(node.body)) + # Yup, we're done. That was easy, huh? + # But after all functions are just named + # scopes, and we compile them just like that: + # we declare their name and arguments (before + # their body so recursion works) and then just + # handle them as a block statement (which takes + # care of incrementing self.scopeDepth so locals + # are resolved properly). There's a need for a bit + # of boilerplate code to make closures work, but + # that's about it + case self.currentFunction.kind: + of NodeKind.funDecl: + if not self.currentFunction.hasExplicitReturn: + let typ = self.inferType(self.currentFunction) + if self.currentFunction.returnType == nil and typ != nil: + self.error("non-empty return statement is not allowed in void functions") + if self.currentFunction.returnType != nil: + self.error("function has an explicit return type, but no explicit return statement was found") + self.emitByte(OpCode.Return) + of NodeKind.lambdaExpr: + if not LambdaExpr(Declaration(self.currentFunction)).hasExplicitReturn: + self.emitByte(OpCode.Return) + else: + discard # Unreachable + # Currently defer is not functional so we + # just pop the instructions + for i in countup(deferStart, self.deferred.len() - 1, 1): + self.deferred.delete(i) self.patchJump(jmp) # This makes us compile nested functions correctly diff --git a/src/frontend/meta/ast.nim b/src/frontend/meta/ast.nim index 52ee498..1cbfe52 100644 --- a/src/frontend/meta/ast.nim +++ b/src/frontend/meta/ast.nim @@ -371,7 +371,8 @@ proc newGroupingExpr*(expression: Expression, token: Token): GroupingExpr = proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]], defaults: seq[Expression], body: Statement, isGenerator: bool, isAsync: bool, token: Token, - returnType: Expression, pragmas: seq[Pragma]): LambdaExpr = + returnType: Expression, pragmas: seq[Pragma], + generics: seq[tuple[name: IdentExpr, cond: Expression]]): LambdaExpr = result = LambdaExpr(kind: lambdaExpr) result.body = body result.arguments = arguments @@ -382,6 +383,7 @@ proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression, result.returnType = returnType result.isPure = false result.pragmas = pragmas + result.generics = generics proc newGetItemExpr*(obj: Expression, name: IdentExpr, @@ -571,7 +573,7 @@ proc newVarDecl*(name: IdentExpr, value: Expression, isConst: bool = false, proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool]], defaults: seq[Expression], body: Statement, isAsync, isGenerator: bool, isPrivate: bool, token: Token, pragmas: seq[Pragma], - returnType: Expression): FunDecl = + returnType: Expression, generics: seq[tuple[name: IdentExpr, cond: Expression]]): FunDecl = result = FunDecl(kind: funDecl) result.name = name result.arguments = arguments @@ -584,7 +586,7 @@ proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueTyp result.pragmas = pragmas result.returnType = returnType result.isPure = false - + result.generics = generics proc `$`*(self: ASTNode): string = diff --git a/src/frontend/parser.nim b/src/frontend/parser.nim index 2f2a193..0ed38ed 100644 --- a/src/frontend/parser.nim +++ b/src/frontend/parser.nim @@ -501,8 +501,7 @@ proc parseAssign(self: Parser): Expression = of identExpr, sliceExpr: result = newAssignExpr(result, value, tok) of getItemExpr: - result = newSetItemExpr(GetItemExpr(result).obj, GetItemExpr( - result).name, value, tok) + result = newSetItemExpr(GetItemExpr(result).obj, GetItemExpr(result).name, value, tok) else: self.error("invalid assignment target") @@ -875,6 +874,27 @@ proc parseDeclArguments(self: Parser, arguments: var seq[tuple[name: IdentExpr, self.error(&"missing type declaration for '{argument.name.token.lexeme}' in function declaration") +proc parseFunExpr(self: Parser): LambdaExpr = + ## Parses the return value of a function + ## when it is another function. Works + ## recursively + var arguments: seq[tuple[name: IdentExpr, valueType: Expression, + mutable: bool, isRef: bool, isPtr: bool]] = @[] + var defaults: seq[Expression] = @[] + result = newLambdaExpr(arguments, defaults, nil, isGenerator = self.peek(-1).kind == Generator, + isAsync = self.peek(-1).kind == Coroutine, token = self.peek(-1), + returnType = nil, pragmas = (@[]), generics=(@[])) + var parameter: tuple[name: IdentExpr, valueType: Expression, + mutable: bool, isRef: bool, isPtr: bool] + if self.match(LeftParen): + self.parseDeclArguments(arguments, parameter, defaults) + if self.match(":"): + if self.match([Function, Coroutine, Generator]): + result.returnType = self.parseFunExpr() + else: + result.returnType = self.expression() + + proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isLambda: bool = false, isOperator: bool = false): Declaration = # Can't use just FunDecl because it can also return LambdaExpr! ## Parses all types of functions, coroutines, generators and operators @@ -897,7 +917,8 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isGenerator = isGenerator, isPrivate = true, token = tok, pragmas = (@[]), - returnType = nil) + returnType = nil, + generics=(@[])) if self.match("*"): FunDecl(self.currentFunction).isPrivate = false self.checkDecl(FunDecl(self.currentFunction).isPrivate) @@ -916,7 +937,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, return result elif isLambda: self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator = isGenerator, isAsync = isAsync, token = tok, - returnType = nil, pragmas = (@[])) + returnType = nil, pragmas = (@[]), generics=(@[])) if self.match(":"): # Function has explicit return type if self.match([Function, Coroutine, Generator]): @@ -925,68 +946,34 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, # the type declaration for a function lacks # the braces that would qualify it as an # expression - var arguments: seq[tuple[name: IdentExpr, valueType: Expression, - mutable: bool, isRef: bool, isPtr: bool]] = @[] - var defaults: seq[Expression] = @[] - returnType = newLambdaExpr(arguments, defaults, nil, isGenerator = self.peek(-1).kind == Generator, - isAsync = self.peek( - -1).kind == - Coroutine, - token = self.peek( - -1), - returnType = nil, - pragmas = ( - @[])) - var parameter: tuple[name: IdentExpr, valueType: Expression, - mutable: bool, isRef: bool, isPtr: bool] - if self.match(LeftParen): - self.parseDeclArguments(arguments, parameter, defaults) - if self.match(":"): - LambdaExpr(returnType).returnType = self.expression() + returnType = self.parseFunExpr() else: returnType = self.expression() - if not self.match(LeftBrace): - self.expect(LeftParen) + if self.match(LeftParen): var parameter: tuple[name: IdentExpr, valueType: Expression, mutable: bool, isRef: bool, isPtr: bool] self.parseDeclArguments(arguments, parameter, defaults) if self.match(":"): # Function's return type if self.match([Function, Coroutine, Generator]): - var arguments: seq[tuple[name: IdentExpr, valueType: Expression, - mutable: bool, isRef: bool, isPtr: bool]] = @[] - var defaults: seq[Expression] = @[] - returnType = newLambdaExpr(arguments, defaults, nil, isGenerator = self.peek(-1).kind == Generator, - isAsync = self.peek( - -1).kind == - Coroutine, - token = self.peek( - -1), - returnType = nil, - pragmas = ( - @[])) - var parameter: tuple[name: IdentExpr, valueType: Expression, - mutable: bool, isRef: bool, isPtr: bool] - if self.match(LeftParen): - self.parseDeclArguments(arguments, parameter, defaults) - if self.match(":"): - LambdaExpr(returnType).returnType = self.expression() + returnType = self.parseFunExpr() else: returnType = self.expression() - self.expect(LeftBrace) if self.currentFunction.kind == funDecl: if not self.match(Semicolon): # If we don't find a semicolon, # it's not a forward declaration + self.expect(LeftBrace) FunDecl(self.currentFunction).body = self.blockStmt() else: - # This is a forward declaration so we explicitly + # This is a forward declaration, so we explicitly # nullify the function's body to tell the compiler # to look for it elsewhere in the file later FunDecl(self.currentFunction).body = nil FunDecl(self.currentFunction).arguments = arguments FunDecl(self.currentFunction).returnType = returnType else: + self.expect(LeftBrace) LambdaExpr(Expression(self.currentFunction)).body = self.blockStmt() LambdaExpr(Expression(self.currentFunction)).arguments = arguments LambdaExpr(Expression(self.currentFunction)).returnType = returnType @@ -1089,6 +1076,7 @@ proc parsePragma(self: Parser): Pragma = break else: var decl = self.currentFunction + # TODO proc declaration(self: Parser): Declaration = diff --git a/src/main.nim b/src/main.nim index e35fb2e..fad0e34 100644 --- a/src/main.nim +++ b/src/main.nim @@ -131,6 +131,7 @@ proc repl = styledEcho fgBlue, "Source line: " , fgDefault, line styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start) except ParseError: + raise let exc = ParseError(getCurrentException()) let lexeme = exc.token.lexeme let lineNo = exc.token.line diff --git a/src/tests.pn b/src/tests.pn index b994a99..0352b29 100644 --- a/src/tests.pn +++ b/src/tests.pn @@ -2,4 +2,5 @@ operator `+`(a: int): int { return a; } -+1; ++1; # Works: defined for int64 ++1'u8; # No definition for int8: error!