Fixed test suite and added test suite runner. Also yeeted templates

This commit is contained in:
Mattia Giambirtone 2023-07-04 14:33:18 +02:00
parent 20a2f07eba
commit 88073194f2
Signed by: nocturn9x
GPG Key ID: 8270F9F467971E59
10 changed files with 138 additions and 133 deletions

40
run_tests.py Normal file
View File

@ -0,0 +1,40 @@
import sys
from tqdm import tqdm
import shlex
import subprocess
from pathlib import Path
NIM_FLAGS = "-d:debug"
PEON_FLAGS = "-n --showMismatches"
EXCLUDE = ["fib.pn", "gc.pn", "import_a.pn", "import_b.pn", "fizzbuzz.pn"]
def main() -> int:
tests: set[Path] = set()
skipped: set[Path] = set()
failed: set[Path] = set()
# We consume the generator now because I want tqdm to show the progress bar!
test_files = list((Path.cwd() / "tests").resolve(strict=True).glob("*.pn"))
for test_file in tqdm(test_files):
tests.add(test_file)
if test_file.name in EXCLUDE:
skipped.add(test_file)
continue
try:
cmd = f"nim {NIM_FLAGS} r src/main.nim {test_file} {PEON_FLAGS}"
out = subprocess.run(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as e:
print(f"An error occurred while executing test -> {type(e).__name__}: {e}")
failed.add(test_file)
continue
if not all(map(lambda s: s == b"true", out.stdout.splitlines())):
failed.add(test_file)
total = len(tests)
successful = len(tests - failed - skipped)
print(f"Collected {total} tests ({successful}/{total}) passed, {len(failed)}/{total} failed, {len(skipped)}/{total} skipped)")
return len(failed) == 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -489,9 +489,11 @@ proc compare*(self: Compiler, a, b: Type): bool =
continue continue
if argA.name != argB.name: if argA.name != argB.name:
return false return false
if a.forwarded or b.forwarded: if (a.forwarded or b.forwarded) and (not a.fun.isNil() and not b.fun.isNil()):
# We need to be more strict when checking forward # We need to be more strict when checking forward
# declarations # declarations. Also, if either of the nodes is null,
# then it means that object is coming from compiler internals
# and we don't really care about its pragmas
if b.fun.pragmas.len() != a.fun.pragmas.len(): if b.fun.pragmas.len() != a.fun.pragmas.len():
return false return false
for (pragA, pragB) in zip(a.fun.pragmas, b.fun.pragmas): for (pragA, pragB) in zip(a.fun.pragmas, b.fun.pragmas):
@ -535,7 +537,7 @@ proc compare*(self: Compiler, a, b: Type): bool =
for constraint in b.cond: for constraint in b.cond:
if not self.compare(constraint.kind, a) or not constraint.match: if not self.compare(constraint.kind, a) or not constraint.match:
return false return false
return true return true
if a.kind == Any or b.kind == Any: if a.kind == Any or b.kind == Any:
# Here we already know that neither of # Here we already know that neither of
# these types are nil, so we can always # these types are nil, so we can always
@ -870,7 +872,7 @@ proc match*(self: Compiler, name: string, kind: Type, node: ASTNode = nil, allow
self.error(&"expecting an implementation for function '{impl[0].ident.token.lexeme}' declared in module '{impl[0].owner.ident.token.lexeme}' at line {impl[0].ident.token.line} of type '{self.stringify(impl[0].valueType)}'") self.error(&"expecting an implementation for function '{impl[0].ident.token.lexeme}' declared in module '{impl[0].owner.ident.token.lexeme}' at line {impl[0].ident.token.line} of type '{self.stringify(impl[0].valueType)}'")
result = impl[0] result = impl[0]
result.resolved = true result.resolved = true
if result.kind == NameKind.Var: if result.kind == NameKind.Var and not result.valueType.nameObj.isNil():
# We found a function bound to a variable, # We found a function bound to a variable,
# so we return the original function's name object # so we return the original function's name object
result = result.valueType.nameObj result = result.valueType.nameObj
@ -969,8 +971,6 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
kind: NameKind.Function, kind: NameKind.Function,
belongsTo: self.currentFunction, belongsTo: self.currentFunction,
isReal: true) isReal: true)
if node.isTemplate:
fn.valueType.compiled = true
if node.generics.len() > 0: if node.generics.len() > 0:
fn.isGeneric = true fn.isGeneric = true
self.names.add(fn) self.names.add(fn)

View File

@ -826,8 +826,12 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
self.error("cannot declare more than 16777215 variables at a time") self.error("cannot declare more than 16777215 variables at a time")
inc(self.stackIndex) inc(self.stackIndex)
typ = self.inferOrError(argument.valueType) typ = self.inferOrError(argument.valueType)
if self.compare(typ, "auto".toIntrinsic()): # We can't use self.compare(), because it would
# always just return true
if typ.kind == Auto:
fn.valueType.isAuto = true fn.valueType.isAuto = true
# Magic trick! We turn auto into any, just
# to make our lives easier
typ = "any".toIntrinsic() typ = "any".toIntrinsic()
self.names.add(Name(depth: fn.depth + 1, self.names.add(Name(depth: fn.depth + 1,
isPrivate: true, isPrivate: true,
@ -843,7 +847,7 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
kind: NameKind.Argument, kind: NameKind.Argument,
node: argument.name, node: argument.name,
position: self.stackIndex, position: self.stackIndex,
isReal: not node.isTemplate isReal: true
)) ))
if node.arguments.high() - node.defaults.high() <= node.arguments.high(): if node.arguments.high() - node.defaults.high() <= node.arguments.high():
# There's a default argument! # There's a default argument!
@ -855,12 +859,14 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
# The function needs a return type too! # The function needs a return type too!
if not node.returnType.isNil(): if not node.returnType.isNil():
fn.valueType.returnType = self.inferOrError(node.returnType) fn.valueType.returnType = self.inferOrError(node.returnType)
if self.compare(fn.valueType.returnType, "auto".toIntrinsic()): if fn.valueType.returnType.kind == Auto:
fn.valueType.isAuto = true fn.valueType.isAuto = true
# Here we don't bother changing the return type
# to any because returnStmt() will see the auto
# type and change it accordingly once we know what
# we're trying to return for the first time
fn.position = self.stackIndex fn.position = self.stackIndex
self.stackIndex = idx self.stackIndex = idx
if node.isTemplate:
fn.valueType.compiled = true
proc prepareAutoFunction(self: BytecodeCompiler, fn: Name, args: seq[tuple[name: string, kind: Type, default: Expression]]): Name = proc prepareAutoFunction(self: BytecodeCompiler, fn: Name, args: seq[tuple[name: string, kind: Type, default: Expression]]): Name =
@ -896,10 +902,8 @@ proc prepareAutoFunction(self: BytecodeCompiler, fn: Name, args: seq[tuple[name:
kind: NameKind.Argument, kind: NameKind.Argument,
node: argument.name, node: argument.name,
position: self.stackIndex, position: self.stackIndex,
isReal: not node.isTemplate isReal: true
)) ))
if node.isTemplate:
fn.valueType.compiled = true
fn.valueType.args = args fn.valueType.args = args
fn.position = self.stackIndex fn.position = self.stackIndex
self.stackIndex = idx self.stackIndex = idx
@ -1331,24 +1335,7 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
result = result.returnType result = result.returnType
self.dispatchDelayedPragmas(impl) self.dispatchDelayedPragmas(impl)
if compile: if compile:
# Lambdas can't be templates :P self.generateCall(impl, argExpr, node.token.line)
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 value", 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: of NodeKind.callExpr:
# Calling a call expression, like hello()() # Calling a call expression, like hello()()
var node: Expression = node var node: Expression = node
@ -1707,6 +1694,8 @@ proc continueStmt(self: BytecodeCompiler, node: ContinueStmt, compile: bool = tr
break break
if not found: if not found:
self.error(&"unknown block name '{node.label.token.lexeme}'", node.label) self.error(&"unknown block name '{node.label.token.lexeme}'", node.label)
if blocks[^1].start > 16777215:
self.error("too much code to jump over in continue statement")
if compile: if compile:
self.emitByte(Jump, node.token.line) self.emitByte(Jump, node.token.line)
self.emitBytes(blocks[^1].start.toTriple(), node.token.line) self.emitBytes(blocks[^1].start.toTriple(), node.token.line)
@ -1979,71 +1968,69 @@ proc funDecl(self: BytecodeCompiler, node: FunDecl, name: Name) =
self.currentFunction = name self.currentFunction = name
let stackIdx = self.stackIndex let stackIdx = self.stackIndex
self.stackIndex = name.position self.stackIndex = name.position
if not node.isTemplate: # A function's code is just compiled linearly
# A function's code is just compiled linearly # and then jumped over
# and then jumped over name.valueType.compiled = true
name.valueType.compiled = true jmp = self.emitJump(JumpForwards, node.token.line)
jmp = self.emitJump(JumpForwards, node.token.line) name.codePos = self.chunk.code.len()
name.codePos = self.chunk.code.len() name.valueType.location = name.codePos
name.valueType.location = name.codePos # We let our debugger know this function's boundaries
# We let our debugger know this function's boundaries self.chunk.functions.add(self.chunk.code.len().toTriple())
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))
self.functions.add((start: self.chunk.code.len(), stop: 0, pos: self.chunk.functions.len() - 3, fn: name)) var offset = self.functions[^1]
var offset = self.functions[^1] var idx = self.chunk.functions.len()
var idx = self.chunk.functions.len() self.chunk.functions.add(0.toTriple()) # Patched it later
self.chunk.functions.add(0.toTriple()) # Patched it later self.chunk.functions.add(uint8(node.arguments.len()))
self.chunk.functions.add(uint8(node.arguments.len())) if not node.name.isNil():
if not node.name.isNil(): self.chunk.functions.add(name.ident.token.lexeme.len().toDouble())
self.chunk.functions.add(name.ident.token.lexeme.len().toDouble()) var s = name.ident.token.lexeme
var s = name.ident.token.lexeme if s.len() >= uint16.high().int:
if s.len() >= uint16.high().int: s = node.name.token.lexeme[0..uint16.high()]
s = node.name.token.lexeme[0..uint16.high()] self.chunk.functions.add(s.toBytes())
self.chunk.functions.add(s.toBytes()) else:
self.chunk.functions.add(0.toDouble())
if BlockStmt(node.body).code.len() == 0:
self.error("cannot declare function with empty body")
var last: Declaration
self.beginScope()
for decl in BlockStmt(node.body).code:
if not last.isNil() and last.kind == returnStmt:
self.warning(UnreachableCode, "code after 'return' statement is unreachable", nil, decl)
self.declaration(decl)
last = decl
let typ = self.currentFunction.valueType.returnType
var hasVal: bool = false
case self.currentFunction.valueType.fun.kind:
of NodeKind.funDecl:
hasVal = FunDecl(self.currentFunction.valueType.fun).hasExplicitReturn
of NodeKind.lambdaExpr:
hasVal = LambdaExpr(self.currentFunction.valueType.fun).hasExplicitReturn
else: else:
self.chunk.functions.add(0.toDouble()) discard # Unreachable
if BlockStmt(node.body).code.len() == 0: if not hasVal and not typ.isNil():
self.error("cannot declare function with empty body") # There is no explicit return statement anywhere in the function's
var last: Declaration # body: while this is not a tremendously useful piece of information
self.beginScope() # (since the presence of at least one doesn't mean all control flow
for decl in BlockStmt(node.body).code: # cases are covered), it definitely is an error worth reporting
if not last.isNil(): self.error("function has an explicit return type, but no return statement was found", node)
if last.kind == returnStmt: hasVal = hasVal and not typ.isNil()
self.warning(UnreachableCode, "code after 'return' statement is unreachable", nil, decl) for jump in self.currentFunction.valueType.retJumps:
self.declaration(decl) self.patchJump(jump)
last = decl self.endScope()
let typ = self.currentFunction.valueType.returnType # Terminates the function's context
var hasVal: bool = false let stop = self.chunk.code.len().toTriple()
case self.currentFunction.valueType.fun.kind: self.emitByte(OpCode.Return, self.peek().token.line)
of NodeKind.funDecl: if hasVal:
hasVal = FunDecl(self.currentFunction.valueType.fun).hasExplicitReturn self.emitByte(1, self.peek().token.line)
of NodeKind.lambdaExpr: else:
hasVal = LambdaExpr(self.currentFunction.valueType.fun).hasExplicitReturn self.emitByte(0, self.peek().token.line)
else: self.chunk.functions[idx] = stop[0]
discard # Unreachable self.chunk.functions[idx + 1] = stop[1]
if not hasVal and not typ.isNil(): self.chunk.functions[idx + 2] = stop[2]
# There is no explicit return statement anywhere in the function's offset.stop = self.chunk.code.len()
# body: while this is not a tremendously useful piece of information # Well, we've compiled everything: time to patch
# (since the presence of at least one doesn't mean all control flow # the jump offset
# cases are covered), it definitely is an error worth reporting self.patchJump(jmp)
self.error("function has an explicit return type, but no return statement was found", node)
hasVal = hasVal and not typ.isNil()
for jump in self.currentFunction.valueType.retJumps:
self.patchJump(jump)
self.endScope()
# Terminates the function's context
let stop = self.chunk.code.len().toTriple()
self.emitByte(OpCode.Return, self.peek().token.line)
if hasVal:
self.emitByte(1, self.peek().token.line)
else:
self.emitByte(0, self.peek().token.line)
self.chunk.functions[idx] = stop[0]
self.chunk.functions[idx + 1] = stop[1]
self.chunk.functions[idx + 2] = stop[2]
offset.stop = self.chunk.code.len()
# Well, we've compiled everything: time to patch
# the jump offset
self.patchJump(jmp)
# Restores the enclosing function (if any). # Restores the enclosing function (if any).
# Makes nested calls work (including recursion) # Makes nested calls work (including recursion)
self.currentFunction = function self.currentFunction = function

View File

@ -272,7 +272,6 @@ type
defaults*: seq[Expression] defaults*: seq[Expression]
isAsync*: bool isAsync*: bool
isGenerator*: bool isGenerator*: bool
isTemplate*: bool
isPure*: bool isPure*: bool
returnType*: Expression returnType*: Expression
hasExplicitReturn*: bool hasExplicitReturn*: bool
@ -767,7 +766,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})" result &= &"Var(name={self.name}, value={self.value}, const={self.isConst}, private={self.isPrivate}, type={self.valueType}, pragmas={self.pragmas})"
of funDecl: of funDecl:
var self = FunDecl(self) 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}, template={self.isTemplate}, 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}, private={self.isPrivate}, pragmas={self.pragmas})"""
of typeDecl: of typeDecl:
var self = TypeDecl(self) 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}, value={self.value})""" 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}, value={self.value})"""

View File

@ -306,7 +306,7 @@ proc varDecl(self: Parser, isLet: bool = false,
isConst: bool = false): Declaration isConst: bool = false): Declaration
proc parseFunExpr(self: Parser): LambdaExpr proc parseFunExpr(self: Parser): LambdaExpr
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, isTemplate: bool = false): Declaration isLambda: bool = false, isOperator: bool = false): Declaration
proc declaration(self: Parser): 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] proc parse*(self: Parser, tokens: seq[Token], file: string, lines: seq[tuple[start, stop: int]], source: string, persist: bool = false): seq[Declaration]
proc findOperators(self: Parser, tokens: seq[Token]) proc findOperators(self: Parser, tokens: seq[Token])
@ -341,7 +341,7 @@ proc primary(self: Parser): Expression =
self.expect(RightParen, "unterminated parenthesized expression") self.expect(RightParen, "unterminated parenthesized expression")
of Yield: of Yield:
let tok = self.step() let tok = self.step()
if self.currentFunction.isNil() or (self.currentFunction.kind == funDecl and FunDecl(self.currentFunction).isTemplate): if self.currentFunction.isNil():
self.error("'yield' cannot be used outside functions", tok) self.error("'yield' cannot be used outside functions", tok)
elif self.currentFunction.token.kind != Generator: elif self.currentFunction.token.kind != Generator:
# It's easier than doing conversions for lambda/funDecl # It's easier than doing conversions for lambda/funDecl
@ -355,7 +355,7 @@ proc primary(self: Parser): Expression =
result.file = self.file result.file = self.file
of Await: of Await:
let tok = self.step() let tok = self.step()
if self.currentFunction.isNil() or (self.currentFunction.kind == funDecl and FunDecl(self.currentFunction).isTemplate): if self.currentFunction.isNil():
self.error("'await' cannot be used outside functions", tok) self.error("'await' cannot be used outside functions", tok)
if self.currentFunction.token.kind != Coroutine: if self.currentFunction.token.kind != Coroutine:
self.error("'await' can only be used inside coroutines", tok) self.error("'await' can only be used inside coroutines", tok)
@ -674,7 +674,7 @@ proc breakStmt(self: Parser): Statement =
proc deferStmt(self: Parser): Statement = proc deferStmt(self: Parser): Statement =
## Parses defer statements ## Parses defer statements
let tok = self.peek(-1) let tok = self.peek(-1)
if self.currentFunction.isNil() or (self.currentFunction.kind == funDecl and FunDecl(self.currentFunction).isTemplate): if self.currentFunction.isNil():
self.error("'defer' cannot be used outside functions") self.error("'defer' cannot be used outside functions")
endOfLine("missing semicolon after 'defer'") endOfLine("missing semicolon after 'defer'")
result = newDeferStmt(self.expression(), tok) result = newDeferStmt(self.expression(), tok)
@ -698,7 +698,7 @@ proc continueStmt(self: Parser): Statement =
proc returnStmt(self: Parser): Statement = proc returnStmt(self: Parser): Statement =
## Parses return statements ## Parses return statements
let tok = self.peek(-1) let tok = self.peek(-1)
if self.currentFunction.isNil() or (self.currentFunction.kind == funDecl and FunDecl(self.currentFunction).isTemplate): if self.currentFunction.isNil():
self.error("'return' cannot be used outside functions") self.error("'return' cannot be used outside functions")
var value: Expression var value: Expression
if not self.check(Semicolon): if not self.check(Semicolon):
@ -719,7 +719,7 @@ proc returnStmt(self: Parser): Statement =
proc yieldStmt(self: Parser): Statement = proc yieldStmt(self: Parser): Statement =
## Parses yield statements ## Parses yield statements
let tok = self.peek(-1) let tok = self.peek(-1)
if self.currentFunction.isNil() or (self.currentFunction.kind == funDecl and FunDecl(self.currentFunction).isTemplate): if self.currentFunction.isNil():
self.error("'yield' cannot be outside functions") self.error("'yield' cannot be outside functions")
elif self.currentFunction.token.kind != Generator: elif self.currentFunction.token.kind != Generator:
self.error("'yield' can only be used inside generators") self.error("'yield' can only be used inside generators")
@ -734,7 +734,7 @@ proc yieldStmt(self: Parser): Statement =
proc awaitStmt(self: Parser): Statement = proc awaitStmt(self: Parser): Statement =
## Parses await statements ## Parses await statements
let tok = self.peek(-1) let tok = self.peek(-1)
if self.currentFunction.isNil() or (self.currentFunction.kind == funDecl and FunDecl(self.currentFunction).isTemplate): if self.currentFunction.isNil():
self.error("'await' cannot be used outside functions") self.error("'await' cannot be used outside functions")
if self.currentFunction.token.kind != Coroutine: if self.currentFunction.token.kind != Coroutine:
self.error("'await' can only be used inside coroutines") self.error("'await' can only be used inside coroutines")
@ -1052,7 +1052,7 @@ proc parseFunExpr(self: Parser): LambdaExpr =
if self.match(LeftParen): if self.match(LeftParen):
self.parseDeclArguments(arguments, parameter, defaults) self.parseDeclArguments(arguments, parameter, defaults)
if self.match(":"): if self.match(":"):
if self.match([Function, Coroutine, Generator, Template]): if self.match([Function, Coroutine, Generator]):
result.returnType = self.parseFunExpr() result.returnType = self.parseFunExpr()
else: else:
result.returnType = self.expression() result.returnType = self.expression()
@ -1095,7 +1095,7 @@ proc parseGenerics(self: Parser, decl: Declaration) =
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, isTemplate: 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
## (with or without a name, where applicable) ## (with or without a name, where applicable)
let tok = self.peek(-1) let tok = self.peek(-1)
@ -1141,7 +1141,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
returnType=nil, depth=self.scopeDepth) returnType=nil, depth=self.scopeDepth)
if self.match(":"): if self.match(":"):
# Function has explicit return type # Function has explicit return type
if self.match([Function, Coroutine, Generator, Template]): if self.match([Function, Coroutine, Generator]):
# The function's return type is another # The function's return type is another
# function. We specialize this case because # function. We specialize this case because
# the type declaration for a function lacks # the type declaration for a function lacks
@ -1155,7 +1155,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
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, Template]): if self.match([Function, Coroutine, Generator]):
returnType = self.parseFunExpr() returnType = self.parseFunExpr()
else: else:
returnType = self.expression() returnType = self.expression()
@ -1167,7 +1167,6 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
if self.match(TokenType.Pragma): if self.match(TokenType.Pragma):
for pragma in self.parsePragmas(): for pragma in self.parsePragmas():
pragmas.add(pragma) pragmas.add(pragma)
FunDecl(self.currentFunction).isTemplate = isTemplate
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
@ -1381,9 +1380,6 @@ proc declaration(self: Parser): Declaration =
of Function: of Function:
discard self.step() discard self.step()
result = self.funDecl() result = self.funDecl()
of Template:
discard self.step()
result = self.funDecl(isTemplate=true)
of Coroutine: of Coroutine:
discard self.step() discard self.step()
result = self.funDecl(isAsync=true) result = self.funDecl(isAsync=true)

View File

@ -38,7 +38,7 @@ type
Yield, Defer, Try, Except, Yield, Defer, Try, Except,
Finally, Type, Operator, Case, Finally, Type, Operator, Case,
Enum, From, Ptr, Ref, Object, Enum, From, Ptr, Ref, Object,
Export, Block, Template, Switch, Export, Block, Switch,
# Literal types # Literal types
Integer, Float, String, Identifier, Integer, Float, String, Identifier,

View File

@ -47,7 +47,6 @@ proc fillSymbolTable*(tokenizer: Lexer) =
tokenizer.symbols.addKeyword("object", Object) tokenizer.symbols.addKeyword("object", Object)
tokenizer.symbols.addKeyword("export", Export) tokenizer.symbols.addKeyword("export", Export)
tokenizer.symbols.addKeyword("block", TokenType.Block) tokenizer.symbols.addKeyword("block", TokenType.Block)
tokenizer.symbols.addKeyword("template", TokenType.Template)
tokenizer.symbols.addKeyword("switch", TokenType.Switch) tokenizer.symbols.addKeyword("switch", TokenType.Switch)
# These are more like expressions with a reserved # These are more like expressions with a reserved
# name that produce a value of a builtin type, # name that produce a value of a builtin type,

View File

@ -12,4 +12,3 @@ fn identity(x: int32): int32 {
fn nope[T: int32 | int16](x: T): T { fn nope[T: int32 | int16](x: T): T {
return identity(x); return identity(x);
} }

View File

@ -1,22 +1,18 @@
import std; import std;
print("Counting down...");
var from = 10; var from = 10;
let to = 0; let to = 0;
while from > to { while from > to {
print(from);
from = from - 1; from = from - 1;
} }
print("Done!"); print(from == to);
print("Counting up...");
var start = 0; var start = 0;
let stop = 10; let stop = 10;
while start < stop { while start < stop {
print(start);
start = start + 1; start = start + 1;
} }
print("Done!"); print(start == stop);

View File

@ -1,11 +0,0 @@
# 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