Updated config.nim and changed mechanism for finding operators (also added binary operator lookup)
This commit is contained in:
parent
b02e2c3d02
commit
15f412bcac
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue