Added template support and related test
This commit is contained in:
parent
405c47cd56
commit
af3c7234be
|
@ -1383,6 +1383,8 @@ proc endScope(self: Compiler) =
|
|||
# Automatic functions do not materialize
|
||||
# at runtime, so their arguments don't either
|
||||
continue
|
||||
if not name.isReal:
|
||||
continue
|
||||
inc(popCount)
|
||||
if not name.resolved:
|
||||
case name.kind:
|
||||
|
@ -1391,7 +1393,7 @@ proc endScope(self: Compiler) =
|
|||
self.warning(UnusedName, &"'{name.ident.token.lexeme}' is declared but not used (add '_' prefix to silence warning)", name)
|
||||
of NameKind.Argument:
|
||||
if not name.ident.token.lexeme.startsWith("_") and name.isPrivate:
|
||||
if not name.belongsTo.isNil() and not name.belongsTo.isBuiltin and name.belongsTo.isReal:
|
||||
if not name.belongsTo.isNil() and not name.belongsTo.isBuiltin and name.belongsTo.isReal and name.belongsTo.resolved:
|
||||
# Builtin functions never use their arguments. We also don't emit this
|
||||
# warning if the function was generated internally by the compiler (for
|
||||
# example as a result of generic specialization) because such objects do
|
||||
|
@ -1527,6 +1529,8 @@ proc declare(self: Compiler, node: ASTNode): Name {.discardable.} =
|
|||
kind: NameKind.Function,
|
||||
belongsTo: self.currentFunction,
|
||||
isReal: true)
|
||||
if node.isTemplate:
|
||||
fn.valueType.compiled = true
|
||||
if node.generics.len() > 0:
|
||||
fn.isGeneric = true
|
||||
var typ: Type
|
||||
|
@ -1967,6 +1971,9 @@ proc identifier(self: Compiler, node: IdentExpr, name: Name = nil, compile: bool
|
|||
self.emitByte(LoadInf, node.token.line)
|
||||
else:
|
||||
discard # Unreachable
|
||||
else:
|
||||
if not s.belongsTo.isNil() and s.belongsTo.valueType.fun.kind == funDecl and FunDecl(s.belongsTo.valueType.fun).isTemplate:
|
||||
discard
|
||||
else:
|
||||
# Loads a regular variable from the current frame
|
||||
self.emitByte(LoadVar, s.ident.token.line)
|
||||
|
@ -2119,7 +2126,8 @@ proc prepareFunction(self: Compiler, fn: Name) =
|
|||
belongsTo: fn,
|
||||
kind: NameKind.Argument,
|
||||
node: argument.name,
|
||||
position: self.stackIndex
|
||||
position: self.stackIndex,
|
||||
isReal: not node.isTemplate
|
||||
))
|
||||
if node.arguments.high() - node.defaults.high() <= node.arguments.high():
|
||||
# There's a default argument!
|
||||
|
@ -2133,6 +2141,8 @@ proc prepareFunction(self: Compiler, fn: Name) =
|
|||
fn.valueType.returnType = self.inferOrError(FunDecl(fn.node).returnType)
|
||||
fn.position = self.stackIndex
|
||||
self.stackIndex = idx
|
||||
if node.isTemplate:
|
||||
fn.valueType.compiled = true
|
||||
|
||||
|
||||
proc prepareAutoFunction(self: Compiler, fn: Name, args: seq[tuple[name: string, kind: Type, default: Expression]]): Name =
|
||||
|
@ -2170,14 +2180,18 @@ proc prepareAutoFunction(self: Compiler, fn: Name, args: seq[tuple[name: string,
|
|||
belongsTo: fn,
|
||||
kind: NameKind.Argument,
|
||||
node: argument.name,
|
||||
position: self.stackIndex
|
||||
position: self.stackIndex,
|
||||
isReal: not node.isTemplate
|
||||
))
|
||||
if node.isTemplate:
|
||||
fn.valueType.compiled = true
|
||||
fn.valueType.args = args
|
||||
fn.position = self.stackIndex
|
||||
self.stackIndex = idx
|
||||
return fn
|
||||
|
||||
|
||||
|
||||
proc generateCall(self: Compiler, fn: Name, args: seq[Expression], line: int) =
|
||||
## Small wrapper that abstracts emitting a call instruction
|
||||
## for a given function
|
||||
|
@ -2272,6 +2286,22 @@ proc call(self: Compiler, node: CallExpr, compile: bool = true): Type {.discarda
|
|||
self.funDecl(FunDecl(result.fun), impl)
|
||||
result = result.returnType
|
||||
if compile:
|
||||
if impl.valueType.fun.kind == funDecl and FunDecl(impl.valueType.fun).isTemplate:
|
||||
for arg in reversed(argExpr):
|
||||
self.expression(arg)
|
||||
let code = BlockStmt(FunDecl(impl.valueType.fun).body).code
|
||||
for i, decl in code:
|
||||
if i < code.high():
|
||||
self.declaration(decl)
|
||||
else:
|
||||
# The last expression in a template
|
||||
# is its return type, so we compute
|
||||
# it, but don't pop it off the stack
|
||||
if decl.kind == exprStmt:
|
||||
self.expression(ExprStmt(decl).expression)
|
||||
else:
|
||||
self.declaration(decl)
|
||||
else:
|
||||
self.generateCall(impl, argExpr, node.token.line)
|
||||
of NodeKind.callExpr:
|
||||
# Calling a call expression, like hello()()
|
||||
|
@ -2718,6 +2748,7 @@ proc funDecl(self: Compiler, node: FunDecl, name: Name) =
|
|||
return
|
||||
let stackIdx = self.stackIndex
|
||||
self.stackIndex = name.position
|
||||
if not node.isTemplate:
|
||||
# A function's code is just compiled linearly
|
||||
# and then jumped over
|
||||
name.valueType.compiled = true
|
||||
|
@ -2728,7 +2759,7 @@ proc funDecl(self: Compiler, node: FunDecl, name: Name) =
|
|||
self.chunk.functions.add(self.chunk.code.len().toTriple())
|
||||
self.functions.add((start: self.chunk.code.len(), stop: 0, pos: self.chunk.functions.len() - 3, fn: name))
|
||||
var offset = self.functions[^1]
|
||||
let idx = self.chunk.functions.len()
|
||||
var idx = self.chunk.functions.len()
|
||||
self.chunk.functions.add(0.toTriple()) # Patched it later
|
||||
self.chunk.functions.add(uint8(node.arguments.len()))
|
||||
if not node.name.isNil():
|
||||
|
|
|
@ -264,6 +264,7 @@ type
|
|||
defaults*: seq[Expression]
|
||||
isAsync*: bool
|
||||
isGenerator*: bool
|
||||
isTemplate*: bool
|
||||
isPure*: bool
|
||||
returnType*: Expression
|
||||
hasExplicitReturn*: bool
|
||||
|
@ -737,7 +738,7 @@ proc `$`*(self: ASTNode): string =
|
|||
result &= &"Var(name={self.name}, value={self.value}, const={self.isConst}, private={self.isPrivate}, type={self.valueType}, pragmas={self.pragmas})"
|
||||
of funDecl:
|
||||
var self = FunDecl(self)
|
||||
result &= &"""FunDecl(name={self.name}, body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generics=[{self.generics.join(", ")}], async={self.isAsync}, generator={self.isGenerator}, private={self.isPrivate}, pragmas={self.pragmas})"""
|
||||
result &= &"""FunDecl(name={self.name}, body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generics=[{self.generics.join(", ")}], async={self.isAsync}, generator={self.isGenerator}, template={self.isTemplate}, private={self.isPrivate}, pragmas={self.pragmas})"""
|
||||
of typeDecl:
|
||||
var self = TypeDecl(self)
|
||||
result &= &"""TypeDecl(name={self.name}, fields={self.fields}, defaults={self.defaults}, private={self.isPrivate}, pragmas={self.pragmas}, generics={self.generics}, parent={self.parent}, ref={self.isRef}, enum={self.isEnum})"""
|
||||
|
|
|
@ -38,7 +38,7 @@ type
|
|||
Yield, Defer, Try, Except,
|
||||
Finally, Type, Operator, Case,
|
||||
Enum, From, Ptr, Ref, Object,
|
||||
Export, Block
|
||||
Export, Block, Template
|
||||
|
||||
# Literal types
|
||||
Integer, Float, String, Identifier,
|
||||
|
|
|
@ -301,7 +301,7 @@ 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
|
||||
isLambda: bool = false, isOperator: bool = false, isTemplate: bool = false): Declaration
|
||||
proc declaration(self: Parser): Declaration
|
||||
proc parse*(self: Parser, tokens: seq[Token], file: string, lines: seq[tuple[start, stop: int]], source: string, persist: bool = false): seq[Declaration]
|
||||
# End of forward declarations
|
||||
|
@ -335,7 +335,7 @@ proc primary(self: Parser): Expression =
|
|||
self.expect(RightParen, "unterminated parenthesized expression")
|
||||
of Yield:
|
||||
let tok = self.step()
|
||||
if self.currentFunction.isNil():
|
||||
if self.currentFunction.isNil() or (self.currentFunction.kind == funDecl and FunDecl(self.currentFunction).isTemplate):
|
||||
self.error("'yield' cannot be used outside functions", tok)
|
||||
elif self.currentFunction.token.kind != Generator:
|
||||
# It's easier than doing conversions for lambda/funDecl
|
||||
|
@ -349,7 +349,7 @@ proc primary(self: Parser): Expression =
|
|||
result.file = self.file
|
||||
of Await:
|
||||
let tok = self.step()
|
||||
if self.currentFunction.isNil():
|
||||
if self.currentFunction.isNil() or (self.currentFunction.kind == funDecl and FunDecl(self.currentFunction).isTemplate):
|
||||
self.error("'await' cannot be used outside functions", tok)
|
||||
if self.currentFunction.token.kind != Coroutine:
|
||||
self.error("'await' can only be used inside coroutines", tok)
|
||||
|
@ -659,7 +659,7 @@ proc breakStmt(self: Parser): Statement =
|
|||
proc deferStmt(self: Parser): Statement =
|
||||
## Parses defer statements
|
||||
let tok = self.peek(-1)
|
||||
if self.currentFunction.isNil():
|
||||
if self.currentFunction.isNil() or (self.currentFunction.kind == funDecl and FunDecl(self.currentFunction).isTemplate):
|
||||
self.error("'defer' cannot be used outside functions")
|
||||
endOfLine("missing semicolon after 'defer'")
|
||||
result = newDeferStmt(self.expression(), tok)
|
||||
|
@ -683,7 +683,7 @@ proc continueStmt(self: Parser): Statement =
|
|||
proc returnStmt(self: Parser): Statement =
|
||||
## Parses return statements
|
||||
let tok = self.peek(-1)
|
||||
if self.currentFunction.isNil():
|
||||
if self.currentFunction.isNil() or (self.currentFunction.kind == funDecl and FunDecl(self.currentFunction).isTemplate):
|
||||
self.error("'return' cannot be used outside functions")
|
||||
var value: Expression
|
||||
if not self.check(Semicolon):
|
||||
|
@ -704,7 +704,7 @@ proc returnStmt(self: Parser): Statement =
|
|||
proc yieldStmt(self: Parser): Statement =
|
||||
## Parses yield statements
|
||||
let tok = self.peek(-1)
|
||||
if self.currentFunction.isNil():
|
||||
if self.currentFunction.isNil() or (self.currentFunction.kind == funDecl and FunDecl(self.currentFunction).isTemplate):
|
||||
self.error("'yield' cannot be outside functions")
|
||||
elif self.currentFunction.token.kind != Generator:
|
||||
self.error("'yield' can only be used inside generators")
|
||||
|
@ -719,7 +719,7 @@ proc yieldStmt(self: Parser): Statement =
|
|||
proc awaitStmt(self: Parser): Statement =
|
||||
## Parses await statements
|
||||
let tok = self.peek(-1)
|
||||
if self.currentFunction.isNil():
|
||||
if self.currentFunction.isNil() or (self.currentFunction.kind == funDecl and FunDecl(self.currentFunction).isTemplate):
|
||||
self.error("'await' cannot be used outside functions")
|
||||
if self.currentFunction.token.kind != Coroutine:
|
||||
self.error("'await' can only be used inside coroutines")
|
||||
|
@ -1039,7 +1039,7 @@ proc parseFunExpr(self: Parser): LambdaExpr =
|
|||
if self.match(LeftParen):
|
||||
self.parseDeclArguments(arguments, parameter, defaults)
|
||||
if self.match(":"):
|
||||
if self.match([Function, Coroutine, Generator]):
|
||||
if self.match([Function, Coroutine, Generator, Template]):
|
||||
result.returnType = self.parseFunExpr()
|
||||
else:
|
||||
result.returnType = self.expression()
|
||||
|
@ -1082,7 +1082,7 @@ proc parseGenerics(self: Parser, decl: Declaration) =
|
|||
|
||||
|
||||
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, isTemplate: bool = false): Declaration = # Can't use just FunDecl because it can also return LambdaExpr!
|
||||
## Parses all types of functions, coroutines, generators and operators
|
||||
## (with or without a name, where applicable)
|
||||
let tok = self.peek(-1)
|
||||
|
@ -1128,7 +1128,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
|
|||
returnType=nil, depth=self.scopeDepth)
|
||||
if self.match(":"):
|
||||
# Function has explicit return type
|
||||
if self.match([Function, Coroutine, Generator]):
|
||||
if self.match([Function, Coroutine, Generator, Template]):
|
||||
# The function's return type is another
|
||||
# function. We specialize this case because
|
||||
# the type declaration for a function lacks
|
||||
|
@ -1142,7 +1142,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
|
|||
self.parseDeclArguments(arguments, parameter, defaults)
|
||||
if self.match(":"):
|
||||
# Function's return type
|
||||
if self.match([Function, Coroutine, Generator]):
|
||||
if self.match([Function, Coroutine, Generator, Template]):
|
||||
returnType = self.parseFunExpr()
|
||||
else:
|
||||
returnType = self.expression()
|
||||
|
@ -1154,6 +1154,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
|
|||
if self.match(TokenType.Pragma):
|
||||
for pragma in self.parsePragmas():
|
||||
pragmas.add(pragma)
|
||||
FunDecl(self.currentFunction).isTemplate = isTemplate
|
||||
FunDecl(self.currentFunction).body = self.blockStmt()
|
||||
else:
|
||||
# This is a forward declaration, so we explicitly
|
||||
|
@ -1344,6 +1345,9 @@ proc declaration(self: Parser): Declaration =
|
|||
of Function:
|
||||
discard self.step()
|
||||
result = self.funDecl()
|
||||
of Template:
|
||||
discard self.step()
|
||||
result = self.funDecl(isTemplate=true)
|
||||
of Coroutine:
|
||||
discard self.step()
|
||||
result = self.funDecl(isAsync=true)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# Stub type declarations for peon's intrinsic types
|
||||
|
||||
|
||||
type int64* = object {
|
||||
#pragma[magic: "int64"]
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ proc fillSymbolTable*(tokenizer: Lexer) =
|
|||
tokenizer.symbols.addKeyword("ref", TokenType.Ref)
|
||||
tokenizer.symbols.addKeyword("ptr", TokenType.Ptr)
|
||||
tokenizer.symbols.addKeyword("block", TokenType.Block)
|
||||
tokenizer.symbols.addKeyword("template", TokenType.Template)
|
||||
for sym in [">", "<", "=", "~", "/", "+", "-", "_", "*", "?", "@", ":", "==", "!=",
|
||||
">=", "<=", "+=", "-=", "/=", "*=", "**=", "!", "%", "&", "|", "^",
|
||||
">>", "<<"]:
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# A test for templates
|
||||
import std;
|
||||
|
||||
|
||||
template sum[T: Integer](a, b: T): T {
|
||||
a + b;
|
||||
}
|
||||
|
||||
|
||||
print(sum(1, 2) == 3); # true
|
||||
print(sum(1'i32, 2'i32) == 3'i32); # true
|
Loading…
Reference in New Issue