Fixed issues with negative numbers and infinity. Variables can now accept function expressions as type arguments. Compiler.infer() now always returns a concrete type, minor bug fix in varDecl

This commit is contained in:
Mattia Giambirtone 2022-10-13 18:34:11 +02:00
parent 8667cbdceb
commit c7893fb14b
6 changed files with 58 additions and 35 deletions

View File

@ -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:

View File

@ -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) =

View File

@ -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,

View File

@ -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():

View File

@ -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()

View File

@ -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]
}