Functions can now return functions which return functions... recursively. Initial work on forward declarations and function call resolution
This commit is contained in:
parent
9f1769ab71
commit
21a9689f6c
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -2,4 +2,5 @@ operator `+`(a: int): int {
|
|||
return a;
|
||||
}
|
||||
|
||||
+1;
|
||||
+1; # Works: defined for int64
|
||||
+1'u8; # No definition for int8: error!
|
||||
|
|
Loading…
Reference in New Issue