Updated config.nim and changed mechanism for finding operators (also added binary operator lookup)

This commit is contained in:
Mattia Giambirtone 2022-05-24 09:55:08 +02:00
parent b02e2c3d02
commit 15f412bcac
2 changed files with 85 additions and 36 deletions

View File

@ -19,15 +19,15 @@ const BYTECODE_MARKER* = "PEON_BYTECODE"
const HEAP_GROW_FACTOR* = 2 # How much extra memory to allocate for dynamic arrays and garbage collection when resizing
when HEAP_GROW_FACTOR <= 1:
{.fatal: "Heap growth factor must be > 1".}
const PEON_VERSION* = (major: 0, minor: 4, patch: 0)
const PEON_VERSION* = (major: 0, minor: 1, patch: 0)
const PEON_RELEASE* = "alpha"
const PEON_COMMIT_HASH* = "ed79385e2a93100331697f26a4a90157e60ad27a"
const PEON_COMMIT_HASH* = "231b7012a17e98c7bcb2b475289cfcdddaf4a7d8"
when len(PEON_COMMIT_HASH) != 40:
{.fatal: "The git commit hash must be exactly 40 characters long".}
const PEON_BRANCH* = "master"
when len(PEON_BRANCH) > 255:
{.fatal: "The git branch name's length must be less than or equal to 255 characters".}
const DEBUG_TRACE_VM* = true # Traces VM execution
const DEBUG_TRACE_VM* = false # Traces VM execution
const DEBUG_TRACE_GC* = false # Traces the garbage collector (TODO)
const DEBUG_TRACE_ALLOCATION* = false # Traces memory allocation/deallocation
const DEBUG_TRACE_COMPILER* = false # Traces the compiler

View File

@ -38,7 +38,12 @@ type
Int8, UInt8, Int16, UInt16, Int32,
UInt32, Int64, UInt64, Float32, Float64,
Char, Byte, String, Function, CustomType,
Nil, Nan, Bool, Inf, Typedesc, Generic
Nil, Nan, Bool, Inf, Typedesc, Generic,
Any # Any is used internally in a few cases,
# for example when looking for operators
# when only the type of the arguments is of
# interest
Type* = ref object
## A wrapper around
## compile-time types
@ -404,7 +409,8 @@ proc compareTypesWithNullNode(self: Compiler, a, b: Type): bool =
if a.args.len() != b.args.len():
return false
elif not self.compareTypes(a.returnType, b.returnType):
return false
if a.returnType.kind != Any and b.returnType.kind != Any:
return false
for (argA, argB) in zip(a.args, b.args):
if not self.compareTypes(argA, argB):
return false
@ -419,7 +425,7 @@ proc compareTypes(self: Compiler, a, b: Type): bool =
if a == nil:
return b == nil
elif b == nil:
return a == nil
return a == nil
if a.kind != b.kind:
return false
case a.kind:
@ -433,12 +439,15 @@ proc compareTypes(self: Compiler, a, b: Type): bool =
let
a = FunDecl(a.node)
b = FunDecl(b.node)
typeOfA = self.inferType(a.returnType)
typeOfB = self.inferType(b.returnType)
if a.name.token.lexeme != b.name.token.lexeme:
return false
elif a.arguments.len() != b.arguments.len():
return false
elif not self.compareTypes(self.inferType(a.returnType), self.inferType(b.returnType)):
return false
elif not self.compareTypes(typeOfA, typeOfB):
if typeOfA.kind != Any and typeOfB.kind != Any:
return false
for (argA, argB) in zip(a.arguments, b.arguments):
if argA.mutable != argB.mutable:
return false
@ -722,41 +731,75 @@ proc literal(self: Compiler, node: ASTNode) =
self.error(&"invalid AST node of kind {node.kind} at literal(): {node} (This is an internal error and most likely a bug!)")
proc matchImpl(self: Compiler, name: string, kind: Type): Name =
## Tries to find a matching function implementation
## compatible with the given type and returns its
## name object
let impl = self.findByType(name, kind)
if impl.len() == 0:
var msg = &"cannot find a suitable implementation for '{name}'"
let names = self.findByName(name)
if names.len() > 0:
msg &= &", found {len(names)} candidates:"
for name in names:
echo name[]
msg &= &"- '{name.name.token.lexeme}' of type {self.typeToStr(name.valueType)}\n"
self.error(msg)
elif impl.len() > 1:
var msg = &"multiple matching implementations of '{name}' found:\n"
for fn in reversed(impl):
var node = FunDecl(fn.valueType.node)
msg &= &"- '{node.name.token.lexeme}' at line {node.token.line} of type {self.typeToStr(fn.valueType)}\n"
self.error(msg)
proc callUnaryOp(self: Compiler, fn: Name, op: UnaryExpr) =
## Emits the code to call a unary operator
# Pushes the return address
self.emitByte(LoadUInt32)
# We patch it later!
let idx = self.chunk.consts.len()
self.emitBytes(self.chunk.writeConstant((0xffffffff'u32).toQuad()))
self.expression(op.a) # Pushes the arguments onto the stack
self.emitByte(Call) # Creates a stack frame
self.emitBytes(fn.codePos.toTriple())
self.emitBytes(1.toTriple())
self.patchReturnAddress(idx)
proc callBinaryOp(self: Compiler, fn: Name, op: BinaryExpr) =
## Emits the code to call a binary operator
# Pushes the return address
self.emitByte(LoadUInt32)
# We patch it later!
let idx = self.chunk.consts.len()
self.emitBytes(self.chunk.writeConstant((0xffffffff'u32).toQuad()))
self.expression(op.a) # Pushes the arguments onto the stack
self.expression(op.b)
self.emitByte(Call) # Creates a stack frame
self.emitBytes(fn.codePos.toTriple())
self.emitBytes(1.toTriple())
self.patchReturnAddress(idx)
proc unary(self: Compiler, node: UnaryExpr) =
## Compiles unary expressions such as decimal
## and bitwise negation
let valueType = self.inferType(node.a)
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}'")
elif impl.len() > 2:
var msg = &"multiple matching implementations of '{node.token.lexeme}' found:\n"
for fn in reversed(impl):
var node = FunDecl(fn.valueType.node)
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)
else:
# Pushes the return address
self.emitByte(LoadUInt32)
# We patch it later!
let idx = self.chunk.consts.len()
self.emitBytes(self.chunk.writeConstant((0xffffffff'u32).toQuad()))
self.expression(node.a) # Pushes the operand onto the stack
self.emitByte(Call) # Creates a stack frame
self.emitBytes(impl[0].codePos.toTriple())
self.emitBytes(1.toTriple())
self.patchReturnAddress(idx)
let funct = self.matchImpl(node.token.lexeme, Type(kind: Function, returnType: Type(kind: Any), node: nil, args: @[valueType]))
self.callUnaryOp(funct, node)
proc binary(self: Compiler, node: BinaryExpr) =
## Compiles all binary expressions
# These two lines prepare the stack by pushing the
# opcode's operands onto it
self.expression(node.a)
self.expression(node.b)
# TODO: Find implementation of
# the given operator and call it
let typeOfA = self.inferType(node.a)
let typeOfB = self.inferType(node.b)
let funct = self.matchImpl(node.token.lexeme, Type(kind: Function, returnType: Type(kind: Any), node: nil, args: @[typeOfA, typeOfB]))
self.callBinaryOp(funct, node)
# TODO: Get rid of old code
#[
case node.operator.kind:
of NoMatch:
# a and b
@ -777,10 +820,16 @@ proc binary(self: Compiler, node: BinaryExpr) =
self.patchJump(jump)
else:
self.error(&"invalid AST node of kind {node.kind} at binary(): {node} (This is an internal error and most likely a bug!)")
]#
proc declareName(self: Compiler, node: Declaration) =
## Statically declares a name into the current scope
## Statically declares a name into the current scope.
## "Declaring" a name only means updating our internal
## list of identifiers so that further calls to resolve()
## correctly return them. There is no code to actually
## declare a variable at runtime: the value is already
## there on the stack
case node.kind:
of NodeKind.varDecl:
var node = VarDecl(node)