Functions can now return functions which return functions... recursively. Initial work on forward declarations and function call resolution

This commit is contained in:
Mattia Giambirtone 2022-05-22 15:26:12 +02:00
parent 9f1769ab71
commit 21a9689f6c
5 changed files with 96 additions and 100 deletions

View File

@ -30,6 +30,7 @@ export ast
export token export token
export multibyte export multibyte
type type
TypeKind* = enum TypeKind* = enum
## An enumeration of compile-time ## An enumeration of compile-time
@ -44,7 +45,7 @@ type
node*: ASTNode node*: ASTNode
case kind*: TypeKind: case kind*: TypeKind:
of Function: of Function:
args*: seq[tuple[name: IdentExpr, kind: Type]] args*: seq[Type]
returnType*: Type returnType*: Type
else: else:
discard discard
@ -163,6 +164,8 @@ proc inferType(self: Compiler, node: LiteralExpr): Type
proc inferType(self: Compiler, node: Expression): Type proc inferType(self: Compiler, node: Expression): Type
proc findByName(self: Compiler, name: string): seq[Name] proc findByName(self: Compiler, name: string): seq[Name]
proc findByType(self: Compiler, name: string, kind: Type): seq[Name] proc findByType(self: Compiler, name: string, kind: Type): seq[Name]
proc compareTypes(self: Compiler, a, b: Type): bool
## End of forward declarations ## End of forward declarations
## Public getter for nicer error formatting ## 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] = proc makeConstant(self: Compiler, val: Expression, typ: Type): array[3, uint8] =
## Adds a constant to the current chunk's constant table ## Adds a constant to the current chunk's constant table
## and returns its index as a 3-byte array of uint8s ## and returns its index as a 3-byte array of uint8s
var v: int
discard parseInt(val.token.lexeme, v)
case typ.kind: case typ.kind:
of UInt8, Int8: of UInt8, Int8:
result = self.chunk.writeConstant([uint8(parseInt(val.token.lexeme))]) result = self.chunk.writeConstant([uint8(v)])
of Int16, UInt16: of Int16, UInt16:
result = self.chunk.writeConstant(parseInt(val.token.lexeme).toDouble()) result = self.chunk.writeConstant(v.toDouble())
of Int32, UInt32: of Int32, UInt32:
result = self.chunk.writeConstant(parseInt(val.token.lexeme).toQuad()) result = self.chunk.writeConstant(v.toQuad())
of Int64, UInt64: of Int64, UInt64:
result = self.chunk.writeConstant(parseInt(val.token.lexeme).toLong()) result = self.chunk.writeConstant(v.toLong())
else: else:
discard discard
@ -380,7 +385,6 @@ proc detectClosureVariable(self: Compiler, name: IdentExpr,
self.chunk.code[entry.codePos + 2] = idx[1] self.chunk.code[entry.codePos + 2] = idx[1]
self.chunk.code[entry.codePos + 3] = idx[2] self.chunk.code[entry.codePos + 3] = idx[2]
proc compareTypes(self: Compiler, a, b: Type): bool
proc compareTypesWithNullNode(self: Compiler, a, b: Type): bool = proc compareTypesWithNullNode(self: Compiler, a, b: Type): bool =
## Compares two types without using information from ## 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): elif not self.compareTypes(a.returnType, b.returnType):
return false return false
for (argA, argB) in zip(a.args, b.args): 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 false
return true return true
else: else:
@ -717,7 +721,7 @@ proc unary(self: Compiler, node: UnaryExpr) =
## and bitwise negation ## and bitwise negation
self.expression(node.a) # Pushes the operand onto the stack self.expression(node.a) # Pushes the operand onto the stack
let valueType = self.inferType(node.a) 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: if impl.len() == 0:
self.error(&"cannot find a suitable implementation for '{node.token.lexeme}'") self.error(&"cannot find a suitable implementation for '{node.token.lexeme}'")
@ -808,7 +812,7 @@ proc declareName(self: Compiler, node: Declaration) =
isLet: false)) isLet: false))
self.names[^1].valueType = self.inferType(argument.valueType) self.names[^1].valueType = self.inferType(argument.valueType)
self.names[^1].valueType.node = argument.name 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.emitByte(LoadVar)
self.emitBytes(self.names.high().toTriple()) self.emitBytes(self.names.high().toTriple())
else: else:
@ -1204,11 +1208,12 @@ proc varDecl(self: Compiler, node: VarDecl) =
proc funDecl(self: Compiler, node: FunDecl) = proc funDecl(self: Compiler, node: FunDecl) =
## Compiles function declarations ## Compiles function declarations
# A function's code is just compiled linearly # A function's code is just compiled linearly
# and then jumped over # and then jumped over
let jmp = self.emitJump(Jump) let jmp = self.emitJump(Jump)
var function = self.currentFunction
self.declareName(node) self.declareName(node)
# TODO: Forward declarations
if node.body != nil: if node.body != nil:
if BlockStmt(node.body).code.len() == 0: if BlockStmt(node.body).code.len() == 0:
self.error("Cannot declare function with empty body") self.error("Cannot declare function with empty body")
@ -1223,50 +1228,49 @@ proc funDecl(self: Compiler, node: FunDecl) =
discard self.typeToStr(fn.valueType) discard self.typeToStr(fn.valueType)
msg &= &"- '{node.name.token.lexeme}' at line {node.token.line} of type {self.typeToStr(fn.valueType)}\n" msg &= &"- '{node.name.token.lexeme}' at line {node.token.line} of type {self.typeToStr(fn.valueType)}\n"
self.error(msg) self.error(msg)
# We store the current function # We store the current function
var function = self.currentFunction self.currentFunction = node
self.currentFunction = node
# Since the deferred array is a linear # Since the deferred array is a linear
# sequence of instructions and we want # sequence of instructions and we want
# to keep track to whose function's each # to keep track to whose function's each
# set of deferred instruction belongs, # set of deferred instruction belongs,
# we record the length of the deferred # we record the length of the deferred
# array before compiling the function # array before compiling the function
# and use this info later to compile # and use this info later to compile
# the try/finally block with the deferred # the try/finally block with the deferred
# code # code
var deferStart = self.deferred.len() var deferStart = self.deferred.len()
self.blockStmt(BlockStmt(node.body)) self.blockStmt(BlockStmt(node.body))
# Yup, we're done. That was easy, huh? # Yup, we're done. That was easy, huh?
# But after all functions are just named # But after all functions are just named
# scopes, and we compile them just like that: # scopes, and we compile them just like that:
# we declare their name and arguments (before # we declare their name and arguments (before
# their body so recursion works) and then just # their body so recursion works) and then just
# handle them as a block statement (which takes # handle them as a block statement (which takes
# care of incrementing self.scopeDepth so locals # care of incrementing self.scopeDepth so locals
# are resolved properly). There's a need for a bit # are resolved properly). There's a need for a bit
# of boilerplate code to make closures work, but # of boilerplate code to make closures work, but
# that's about it # that's about it
case self.currentFunction.kind: case self.currentFunction.kind:
of NodeKind.funDecl: of NodeKind.funDecl:
if not self.currentFunction.hasExplicitReturn: if not self.currentFunction.hasExplicitReturn:
let typ = self.inferType(self.currentFunction) let typ = self.inferType(self.currentFunction)
if self.currentFunction.returnType == nil and typ != nil: if self.currentFunction.returnType == nil and typ != nil:
self.error("non-empty return statement is not allowed in functions without an explicit return type") self.error("non-empty return statement is not allowed in void functions")
if self.currentFunction.returnType != nil: if self.currentFunction.returnType != nil:
self.error("function has an explicit return type, but no explicit return statement was found") self.error("function has an explicit return type, but no explicit return statement was found")
self.emitByte(OpCode.Return) self.emitByte(OpCode.Return)
of NodeKind.lambdaExpr: of NodeKind.lambdaExpr:
if not LambdaExpr(Declaration(self.currentFunction)).hasExplicitReturn: if not LambdaExpr(Declaration(self.currentFunction)).hasExplicitReturn:
self.emitByte(OpCode.Return) self.emitByte(OpCode.Return)
else: else:
discard # Unreachable discard # Unreachable
# Currently defer is not functional so we # Currently defer is not functional so we
# just pop the instructions # just pop the instructions
for i in countup(deferStart, self.deferred.len() - 1, 1): for i in countup(deferStart, self.deferred.len() - 1, 1):
self.deferred.delete(i) self.deferred.delete(i)
self.patchJump(jmp) self.patchJump(jmp)
# This makes us compile nested functions correctly # This makes us compile nested functions correctly

View File

@ -371,7 +371,8 @@ proc newGroupingExpr*(expression: Expression, token: Token): GroupingExpr =
proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression, proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression,
mutable: bool, isRef: bool, isPtr: bool]], defaults: seq[Expression], mutable: bool, isRef: bool, isPtr: bool]], defaults: seq[Expression],
body: Statement, isGenerator: bool, isAsync: bool, token: Token, 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 = LambdaExpr(kind: lambdaExpr)
result.body = body result.body = body
result.arguments = arguments result.arguments = arguments
@ -382,6 +383,7 @@ proc newLambdaExpr*(arguments: seq[tuple[name: IdentExpr, valueType: Expression,
result.returnType = returnType result.returnType = returnType
result.isPure = false result.isPure = false
result.pragmas = pragmas result.pragmas = pragmas
result.generics = generics
proc newGetItemExpr*(obj: Expression, name: IdentExpr, 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], 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, body: Statement, isAsync, isGenerator: bool,
isPrivate: bool, token: Token, pragmas: seq[Pragma], 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 = FunDecl(kind: funDecl)
result.name = name result.name = name
result.arguments = arguments result.arguments = arguments
@ -584,7 +586,7 @@ proc newFunDecl*(name: IdentExpr, arguments: seq[tuple[name: IdentExpr, valueTyp
result.pragmas = pragmas result.pragmas = pragmas
result.returnType = returnType result.returnType = returnType
result.isPure = false result.isPure = false
result.generics = generics
proc `$`*(self: ASTNode): string = proc `$`*(self: ASTNode): string =

View File

@ -501,8 +501,7 @@ proc parseAssign(self: Parser): Expression =
of identExpr, sliceExpr: of identExpr, sliceExpr:
result = newAssignExpr(result, value, tok) result = newAssignExpr(result, value, tok)
of getItemExpr: of getItemExpr:
result = newSetItemExpr(GetItemExpr(result).obj, GetItemExpr( result = newSetItemExpr(GetItemExpr(result).obj, GetItemExpr(result).name, value, tok)
result).name, value, tok)
else: else:
self.error("invalid assignment target") 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") 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, 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! 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 ## 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, isGenerator = isGenerator,
isPrivate = true, isPrivate = true,
token = tok, pragmas = (@[]), token = tok, pragmas = (@[]),
returnType = nil) returnType = nil,
generics=(@[]))
if self.match("*"): if self.match("*"):
FunDecl(self.currentFunction).isPrivate = false FunDecl(self.currentFunction).isPrivate = false
self.checkDecl(FunDecl(self.currentFunction).isPrivate) self.checkDecl(FunDecl(self.currentFunction).isPrivate)
@ -916,7 +937,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
return result return result
elif isLambda: elif isLambda:
self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator = isGenerator, isAsync = isAsync, token = tok, self.currentFunction = newLambdaExpr(arguments, defaults, newBlockStmt(@[], Token()), isGenerator = isGenerator, isAsync = isAsync, token = tok,
returnType = nil, pragmas = (@[])) returnType = nil, pragmas = (@[]), generics=(@[]))
if self.match(":"): if self.match(":"):
# Function has explicit return type # Function has explicit return type
if self.match([Function, Coroutine, Generator]): 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 type declaration for a function lacks
# the braces that would qualify it as an # the braces that would qualify it as an
# expression # expression
var arguments: seq[tuple[name: IdentExpr, valueType: Expression, returnType = self.parseFunExpr()
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()
else: else:
returnType = self.expression() returnType = self.expression()
if not self.match(LeftBrace): if self.match(LeftParen):
self.expect(LeftParen)
var parameter: tuple[name: IdentExpr, valueType: Expression, var parameter: tuple[name: IdentExpr, valueType: Expression,
mutable: bool, isRef: bool, isPtr: bool] mutable: bool, isRef: bool, isPtr: bool]
self.parseDeclArguments(arguments, parameter, defaults) self.parseDeclArguments(arguments, parameter, defaults)
if self.match(":"): if self.match(":"):
# Function's return type # Function's return type
if self.match([Function, Coroutine, Generator]): if self.match([Function, Coroutine, Generator]):
var arguments: seq[tuple[name: IdentExpr, valueType: Expression, returnType = self.parseFunExpr()
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()
else: else:
returnType = self.expression() returnType = self.expression()
self.expect(LeftBrace)
if self.currentFunction.kind == funDecl: if self.currentFunction.kind == funDecl:
if not self.match(Semicolon): if not self.match(Semicolon):
# If we don't find a semicolon, # If we don't find a semicolon,
# it's not a forward declaration # it's not a forward declaration
self.expect(LeftBrace)
FunDecl(self.currentFunction).body = self.blockStmt() FunDecl(self.currentFunction).body = self.blockStmt()
else: 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 # nullify the function's body to tell the compiler
# to look for it elsewhere in the file later # to look for it elsewhere in the file later
FunDecl(self.currentFunction).body = nil FunDecl(self.currentFunction).body = nil
FunDecl(self.currentFunction).arguments = arguments FunDecl(self.currentFunction).arguments = arguments
FunDecl(self.currentFunction).returnType = returnType FunDecl(self.currentFunction).returnType = returnType
else: else:
self.expect(LeftBrace)
LambdaExpr(Expression(self.currentFunction)).body = self.blockStmt() LambdaExpr(Expression(self.currentFunction)).body = self.blockStmt()
LambdaExpr(Expression(self.currentFunction)).arguments = arguments LambdaExpr(Expression(self.currentFunction)).arguments = arguments
LambdaExpr(Expression(self.currentFunction)).returnType = returnType LambdaExpr(Expression(self.currentFunction)).returnType = returnType
@ -1089,6 +1076,7 @@ proc parsePragma(self: Parser): Pragma =
break break
else: else:
var decl = self.currentFunction var decl = self.currentFunction
# TODO
proc declaration(self: Parser): Declaration = proc declaration(self: Parser): Declaration =

View File

@ -131,6 +131,7 @@ proc repl =
styledEcho fgBlue, "Source line: " , fgDefault, line styledEcho fgBlue, "Source line: " , fgDefault, line
styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start) styledEcho fgCyan, " ".repeat(len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
except ParseError: except ParseError:
raise
let exc = ParseError(getCurrentException()) let exc = ParseError(getCurrentException())
let lexeme = exc.token.lexeme let lexeme = exc.token.lexeme
let lineNo = exc.token.line let lineNo = exc.token.line

View File

@ -2,4 +2,5 @@ operator `+`(a: int): int {
return a; return a;
} }
+1; +1; # Works: defined for int64
+1'u8; # No definition for int8: error!