diff --git a/src/backend/vm.nim b/src/backend/vm.nim index 5987eb1..3601868 100644 --- a/src/backend/vm.nim +++ b/src/backend/vm.nim @@ -695,6 +695,8 @@ proc dispatch*(self: PeonVM) = self.push(self.getNil()) of LoadInf: self.push(self.getInf(true)) + of LoadNInf: + self.push(self.getInf(false)) of LoadInt64: self.push(uint64(self.constReadInt64(int(self.readLong())))) of LoadUInt64: @@ -907,7 +909,7 @@ proc dispatch*(self: PeonVM) = # types, we don't need specialized instructions # to operate on them of Negate: - self.push(uint64(-int64(self.pop()))) + self.push(cast[uint64](-int64(self.pop()))) of NegateFloat64: self.push(cast[uint64](-cast[float](self.pop()))) of NegateFloat32: @@ -979,19 +981,19 @@ proc dispatch*(self: PeonVM) = self.push(self.getBool(self.pop() <= self.pop())) # Print opcodes of PrintInt64: - echo int64(self.pop()) + echo cast[int64](self.pop()) of PrintUInt64: echo self.pop() of PrintInt32: - echo int32(self.pop()) + echo cast[int32](self.pop()) of PrintUInt32: echo uint32(self.pop()) of PrintInt16: - echo int16(self.pop()) + echo cast[int16](self.pop()) of PrintUInt16: echo uint16(self.pop()) of PrintInt8: - echo int8(self.pop()) + echo cast[int8](self.pop()) of PrintUInt8: echo uint8(self.pop()) of PrintFloat32: @@ -1007,9 +1009,9 @@ proc dispatch*(self: PeonVM) = echo "false" of PrintInf: if self.pop() == 0x3: - echo "-inf" - else: echo "inf" + else: + echo "-inf" of PrintNan: echo "nan" of PrintString: diff --git a/src/frontend/compiler.nim b/src/frontend/compiler.nim index 27b3723..bd338ee 100644 --- a/src/frontend/compiler.nim +++ b/src/frontend/compiler.nim @@ -219,6 +219,7 @@ 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, name: Name) +proc specializeGeneric(self: Compiler, fn: Name, args: seq[Expression]): Name proc matchImpl(self: Compiler, name: string, kind: Type, node: ASTNode = nil): Name proc infer(self: Compiler, node: LiteralExpr): Type proc infer(self: Compiler, node: Expression): Type @@ -754,7 +755,7 @@ proc infer(self: Compiler, node: LiteralExpr): Type = proc infer(self: Compiler, node: Expression): Type = ## Infers the type of a given expression and ## returns it (if the node is nil, nil is - ## returned) + ## returned). Always returns a concrete type if node.isNil(): return nil case node.kind: @@ -767,24 +768,30 @@ proc infer(self: Compiler, node: Expression): Type = if name.belongsTo.isNil(): name = self.resolve(result.name) if not name.isNil(): - return name.valueType + result = name.valueType else: for arg in name.belongsTo.valueType.args: if node.token.lexeme == arg.name: - return arg.kind + result = arg.kind else: result = node.name.lexeme.toIntrinsic() of unaryExpr: let node = UnaryExpr(node) - return self.matchImpl(node.operator.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: @[("", self.infer(node.a))]), node).valueType.returnType + let impl = self.matchImpl(node.operator.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: @[("", self.infer(node.a))]), node) + result = impl.valueType.returnType + if result.kind == Generic: + result = self.specializeGeneric(impl, @[node.a]).valueType.returnType of binaryExpr: let node = BinaryExpr(node) - return self.matchImpl(node.operator.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: @[("", self.infer(node.a)), ("", self.infer(node.b))]), node).valueType.returnType + let impl = self.matchImpl(node.operator.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: @[("", self.infer(node.a)), ("", self.infer(node.b))]), node) + result = impl.valueType.returnType + if result.kind == Generic: + result = self.specializeGeneric(impl, @[node.a, node.b]).valueType.returnType of {intExpr, hexExpr, binExpr, octExpr, strExpr, falseExpr, trueExpr, infExpr, nanExpr, floatExpr, nilExpr }: - return self.infer(LiteralExpr(node)) + result = self.infer(LiteralExpr(node)) of lambdaExpr: var node = LambdaExpr(node) result = Type(kind: Function, returnType: nil, args: @[], isLambda: true) @@ -820,6 +827,7 @@ proc infer(self: Compiler, node: Expression): Type = result = self.infer(GroupingExpr(node).expression) else: discard # Unreachable + proc typeToStr(self: Compiler, typ: Type): string = @@ -1004,7 +1012,8 @@ proc handleBuiltinFunction(self: Compiler, fn: Type, args: seq[Expression], line "PrintInf": PrintInf, "PrintString": PrintString, "SysClock64": SysClock64, - "LogicalNot": LogicalNot + "LogicalNot": LogicalNot, + "NegInf": LoadNInf }.to_table() if fn.builtinOp in codes: self.emitByte(codes[fn.builtinOp], line) @@ -1915,8 +1924,6 @@ proc printRepl(self: Compiler, typ: Type, node: Expression) = ## Emits instruction to print ## peon types in REPL mode case typ.kind: - of Generic: - discard # TODO of Int64: self.emitByte(PrintInt64, node.token.line) of UInt64: @@ -2010,25 +2017,21 @@ proc varDecl(self: Compiler, node: VarDecl, name: Name) = if expected.isNil() and actual.isNil(): if node.value.kind == identExpr or node.value.kind == callExpr and CallExpr(node.value).callee.kind == identExpr: var name = node.value.token.lexeme - if node.value.kind == callExpr: + if node.value.kind == callExpr and CallExpr(node.value).callee.kind == identExpr: name = CallExpr(node.value).callee.token.lexeme if self.resolve(name).isNil(): self.error(&"reference to undeclared name '{name}'") self.error(&"'{node.name.token.lexeme}' has no type") - elif not expected.isNil() and expected.mutable: # I mean, variables *are* already mutable (some of them anyway) + if not expected.isNil() and expected.mutable: # I mean, variables *are* already mutable (some of them anyway) self.error(&"invalid type '{self.typeToStr(expected)}' for var") elif not self.compare(expected, actual): if not expected.isNil(): self.error(&"expected value of type '{self.typeToStr(expected)}', but '{node.name.token.lexeme}' is of type '{self.typeToStr(actual)}'") if expected.isNil(): name.valueType = actual - if actual.kind == Generic: - # We matched a generic type, but we - # need the concrete one - name.valueType = expected self.expression(node.value) self.emitByte(StoreVar, node.token.line) - self.emitBytes(self.getStackPos(self.names[^1]).toTriple(), node.token.line) + self.emitBytes(self.getStackPos(name).toTriple(), node.token.line) proc typeDecl(self: Compiler, node: TypeDecl, name: Name) = diff --git a/src/frontend/meta/bytecode.nim b/src/frontend/meta/bytecode.nim index bd52344..db4f8cf 100644 --- a/src/frontend/meta/bytecode.nim +++ b/src/frontend/meta/bytecode.nim @@ -84,6 +84,7 @@ type LoadFalse, LoadNan, LoadInf, + LoadNInf, ## Operations on primitive types Negate, NegateFloat64, @@ -184,7 +185,7 @@ type const simpleInstructions* = {Return, LoadNil, LoadTrue, LoadFalse, LoadNan, LoadInf, - Pop, Raise, + Pop, Raise, LoadNInf, BeginTry, FinishTry, Yield, Await, NoOp, SetResult, PopC, PushC, SysClock64, diff --git a/src/frontend/parser.nim b/src/frontend/parser.nim index 9225137..3cce51c 100644 --- a/src/frontend/parser.nim +++ b/src/frontend/parser.nim @@ -297,6 +297,7 @@ proc expressionStatement(self: Parser): Statement proc statement(self: Parser): Statement proc varDecl(self: Parser, isLet: bool = false, isConst: bool = false): Declaration +proc parseFunExpr(self: Parser): LambdaExpr proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false, isLambda: bool = false, isOperator: bool = false): Declaration proc declaration(self: Parser): Declaration @@ -890,13 +891,14 @@ proc varDecl(self: Parser, isLet: bool = false, valueType = newIdentExpr(self.peek(-1), self.scopeDepth) if self.match("="): hasInit = true - value = self.expression() + if self.match([Function, Coroutine, Generator]): + value = self.parseFunExpr() + else: + value = self.expression() if isConst and not value.isConst(): self.error("constant initializer is not a constant") - else: - if tok.kind != TokenType.Var: - self.error(&"{tok.lexeme} declaration requires an initializer") - value = newNilExpr(Token(lexeme: "nil")) + elif tok.kind != TokenType.Var: + self.error(&"{tok.lexeme} declaration requires an initializer") self.expect(Semicolon, "expecting semicolon after declaration") if self.match(TokenType.Pragma): for pragma in self.parsePragmas(): diff --git a/src/main.nim b/src/main.nim index 02042b6..7ab5bc0 100644 --- a/src/main.nim +++ b/src/main.nim @@ -147,8 +147,6 @@ proc repl = except LexingError: input = "" var exc = LexingError(getCurrentException()) - if exc.lexeme == "": - exc.line -= 1 let relPos = exc.lexer.getRelPos(exc.line) let line = exc.lexer.getSource().splitLines()[exc.line - 1].strip(chars={'\n'}) stderr.styledWriteLine(fgRed, "A fatal error occurred while parsing ", fgYellow, &"'{exc.file}'", fgRed, ", module ", @@ -157,12 +155,11 @@ proc repl = styledEcho fgBlue, "Source line: " , fgDefault, line styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start) except ParseError: + echo getCurrentExceptionMsg() input = "" let exc = ParseError(getCurrentException()) let lexeme = exc.token.lexeme var lineNo = exc.token.line - if exc.token.kind == EndOfFile: - lineNo -= 1 let relPos = exc.parser.getRelPos(lineNo) let fn = parser.getCurrentFunction() let line = exc.parser.getSource().splitLines()[lineNo - 1].strip(chars={'\n'}) @@ -178,8 +175,6 @@ proc repl = let exc = CompileError(getCurrentException()) let lexeme = exc.node.token.lexeme var lineNo = exc.node.token.line - if exc.node.token.kind == EndOfFile: - lineNo -= 1 let relPos = exc.compiler.getRelPos(lineNo) let line = exc.compiler.getSource().splitLines()[lineNo - 1].strip(chars={'\n'}) var fn = exc.compiler.getCurrentFunction() diff --git a/tests/std.pn b/tests/std.pn index 5a3ee50..cae8bb1 100644 --- a/tests/std.pn +++ b/tests/std.pn @@ -38,6 +38,26 @@ operator `-`*(a, b: float32): float32 { } +operator `-`*[T: int | int32 | int16 | int8](a: T): T { + #pragma[magic: "Negate", pure] +} + + +operator `-`*(a: float64): float64 { + #pragma[magic: "NegateFloat64", pure] +} + + +operator `-`*(a: float32): float32 { + #pragma[magic: "NegateFloat32", pure] +} + + +operator `-`*(a: inf): inf { + #pragma[magic: "NegInf", pure] +} + + operator `*`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): T { #pragma[magic: "Multiply", pure] }