Fixed test suite and added test suite runner. Also yeeted templates
parent
20a2f07eba
commit
88073194f2
|
@ -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())
|
|
@ -489,9 +489,11 @@ proc compare*(self: Compiler, a, b: Type): bool =
|
|||
continue
|
||||
if argA.name != argB.name:
|
||||
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
|
||||
# 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():
|
||||
return false
|
||||
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:
|
||||
if not self.compare(constraint.kind, a) or not constraint.match:
|
||||
return false
|
||||
return true
|
||||
return true
|
||||
if a.kind == Any or b.kind == Any:
|
||||
# Here we already know that neither of
|
||||
# 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)}'")
|
||||
result = impl[0]
|
||||
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,
|
||||
# so we return the original function's name object
|
||||
result = result.valueType.nameObj
|
||||
|
@ -969,8 +971,6 @@ 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
|
||||
self.names.add(fn)
|
||||
|
|
|
@ -826,8 +826,12 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
|
|||
self.error("cannot declare more than 16777215 variables at a time")
|
||||
inc(self.stackIndex)
|
||||
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
|
||||
# Magic trick! We turn auto into any, just
|
||||
# to make our lives easier
|
||||
typ = "any".toIntrinsic()
|
||||
self.names.add(Name(depth: fn.depth + 1,
|
||||
isPrivate: true,
|
||||
|
@ -843,7 +847,7 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
|
|||
kind: NameKind.Argument,
|
||||
node: argument.name,
|
||||
position: self.stackIndex,
|
||||
isReal: not node.isTemplate
|
||||
isReal: true
|
||||
))
|
||||
if node.arguments.high() - node.defaults.high() <= node.arguments.high():
|
||||
# There's a default argument!
|
||||
|
@ -855,12 +859,14 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
|
|||
# The function needs a return type too!
|
||||
if not node.returnType.isNil():
|
||||
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
|
||||
# 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
|
||||
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 =
|
||||
|
@ -896,10 +902,8 @@ proc prepareAutoFunction(self: BytecodeCompiler, fn: Name, args: seq[tuple[name:
|
|||
kind: NameKind.Argument,
|
||||
node: argument.name,
|
||||
position: self.stackIndex,
|
||||
isReal: not node.isTemplate
|
||||
isReal: true
|
||||
))
|
||||
if node.isTemplate:
|
||||
fn.valueType.compiled = true
|
||||
fn.valueType.args = args
|
||||
fn.position = self.stackIndex
|
||||
self.stackIndex = idx
|
||||
|
@ -1331,24 +1335,7 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
|
|||
result = result.returnType
|
||||
self.dispatchDelayedPragmas(impl)
|
||||
if compile:
|
||||
# Lambdas can't be templates :P
|
||||
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)
|
||||
self.generateCall(impl, argExpr, node.token.line)
|
||||
of NodeKind.callExpr:
|
||||
# Calling a call expression, like hello()()
|
||||
var node: Expression = node
|
||||
|
@ -1707,6 +1694,8 @@ proc continueStmt(self: BytecodeCompiler, node: ContinueStmt, compile: bool = tr
|
|||
break
|
||||
if not found:
|
||||
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:
|
||||
self.emitByte(Jump, 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
|
||||
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
|
||||
jmp = self.emitJump(JumpForwards, node.token.line)
|
||||
name.codePos = self.chunk.code.len()
|
||||
name.valueType.location = name.codePos
|
||||
# We let our debugger know this function's boundaries
|
||||
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]
|
||||
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():
|
||||
self.chunk.functions.add(name.ident.token.lexeme.len().toDouble())
|
||||
var s = name.ident.token.lexeme
|
||||
if s.len() >= uint16.high().int:
|
||||
s = node.name.token.lexeme[0..uint16.high()]
|
||||
self.chunk.functions.add(s.toBytes())
|
||||
# A function's code is just compiled linearly
|
||||
# and then jumped over
|
||||
name.valueType.compiled = true
|
||||
jmp = self.emitJump(JumpForwards, node.token.line)
|
||||
name.codePos = self.chunk.code.len()
|
||||
name.valueType.location = name.codePos
|
||||
# We let our debugger know this function's boundaries
|
||||
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]
|
||||
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():
|
||||
self.chunk.functions.add(name.ident.token.lexeme.len().toDouble())
|
||||
var s = name.ident.token.lexeme
|
||||
if s.len() >= uint16.high().int:
|
||||
s = node.name.token.lexeme[0..uint16.high()]
|
||||
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:
|
||||
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():
|
||||
if 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:
|
||||
discard # Unreachable
|
||||
if not hasVal and not typ.isNil():
|
||||
# There is no explicit return statement anywhere in the function's
|
||||
# body: while this is not a tremendously useful piece of information
|
||||
# (since the presence of at least one doesn't mean all control flow
|
||||
# cases are covered), it definitely is an error worth reporting
|
||||
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)
|
||||
discard # Unreachable
|
||||
if not hasVal and not typ.isNil():
|
||||
# There is no explicit return statement anywhere in the function's
|
||||
# body: while this is not a tremendously useful piece of information
|
||||
# (since the presence of at least one doesn't mean all control flow
|
||||
# cases are covered), it definitely is an error worth reporting
|
||||
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).
|
||||
# Makes nested calls work (including recursion)
|
||||
self.currentFunction = function
|
||||
|
|
|
@ -272,7 +272,6 @@ type
|
|||
defaults*: seq[Expression]
|
||||
isAsync*: bool
|
||||
isGenerator*: bool
|
||||
isTemplate*: bool
|
||||
isPure*: bool
|
||||
returnType*: Expression
|
||||
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})"
|
||||
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}, 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:
|
||||
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})"""
|
||||
|
|
|
@ -306,7 +306,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, isTemplate: bool = false): Declaration
|
||||
isLambda: bool = false, isOperator: 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]
|
||||
proc findOperators(self: Parser, tokens: seq[Token])
|
||||
|
@ -341,7 +341,7 @@ proc primary(self: Parser): Expression =
|
|||
self.expect(RightParen, "unterminated parenthesized expression")
|
||||
of Yield:
|
||||
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)
|
||||
elif self.currentFunction.token.kind != Generator:
|
||||
# It's easier than doing conversions for lambda/funDecl
|
||||
|
@ -355,7 +355,7 @@ proc primary(self: Parser): Expression =
|
|||
result.file = self.file
|
||||
of Await:
|
||||
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)
|
||||
if self.currentFunction.token.kind != Coroutine:
|
||||
self.error("'await' can only be used inside coroutines", tok)
|
||||
|
@ -674,7 +674,7 @@ proc breakStmt(self: Parser): Statement =
|
|||
proc deferStmt(self: Parser): Statement =
|
||||
## Parses defer statements
|
||||
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")
|
||||
endOfLine("missing semicolon after 'defer'")
|
||||
result = newDeferStmt(self.expression(), tok)
|
||||
|
@ -698,7 +698,7 @@ proc continueStmt(self: Parser): Statement =
|
|||
proc returnStmt(self: Parser): Statement =
|
||||
## Parses return statements
|
||||
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")
|
||||
var value: Expression
|
||||
if not self.check(Semicolon):
|
||||
|
@ -719,7 +719,7 @@ proc returnStmt(self: Parser): Statement =
|
|||
proc yieldStmt(self: Parser): Statement =
|
||||
## Parses yield statements
|
||||
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")
|
||||
elif self.currentFunction.token.kind != Generator:
|
||||
self.error("'yield' can only be used inside generators")
|
||||
|
@ -734,7 +734,7 @@ proc yieldStmt(self: Parser): Statement =
|
|||
proc awaitStmt(self: Parser): Statement =
|
||||
## Parses await statements
|
||||
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")
|
||||
if self.currentFunction.token.kind != Coroutine:
|
||||
self.error("'await' can only be used inside coroutines")
|
||||
|
@ -1052,7 +1052,7 @@ proc parseFunExpr(self: Parser): LambdaExpr =
|
|||
if self.match(LeftParen):
|
||||
self.parseDeclArguments(arguments, parameter, defaults)
|
||||
if self.match(":"):
|
||||
if self.match([Function, Coroutine, Generator, Template]):
|
||||
if self.match([Function, Coroutine, Generator]):
|
||||
result.returnType = self.parseFunExpr()
|
||||
else:
|
||||
result.returnType = self.expression()
|
||||
|
@ -1095,7 +1095,7 @@ proc parseGenerics(self: Parser, decl: Declaration) =
|
|||
|
||||
|
||||
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
|
||||
## (with or without a name, where applicable)
|
||||
let tok = self.peek(-1)
|
||||
|
@ -1141,7 +1141,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, Template]):
|
||||
if self.match([Function, Coroutine, Generator]):
|
||||
# The function's return type is another
|
||||
# function. We specialize this case because
|
||||
# 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)
|
||||
if self.match(":"):
|
||||
# Function's return type
|
||||
if self.match([Function, Coroutine, Generator, Template]):
|
||||
if self.match([Function, Coroutine, Generator]):
|
||||
returnType = self.parseFunExpr()
|
||||
else:
|
||||
returnType = self.expression()
|
||||
|
@ -1167,7 +1167,6 @@ 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
|
||||
|
@ -1381,9 +1380,6 @@ 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)
|
||||
|
|
|
@ -38,7 +38,7 @@ type
|
|||
Yield, Defer, Try, Except,
|
||||
Finally, Type, Operator, Case,
|
||||
Enum, From, Ptr, Ref, Object,
|
||||
Export, Block, Template, Switch,
|
||||
Export, Block, Switch,
|
||||
|
||||
# Literal types
|
||||
Integer, Float, String, Identifier,
|
||||
|
|
|
@ -47,7 +47,6 @@ proc fillSymbolTable*(tokenizer: Lexer) =
|
|||
tokenizer.symbols.addKeyword("object", Object)
|
||||
tokenizer.symbols.addKeyword("export", Export)
|
||||
tokenizer.symbols.addKeyword("block", TokenType.Block)
|
||||
tokenizer.symbols.addKeyword("template", TokenType.Template)
|
||||
tokenizer.symbols.addKeyword("switch", TokenType.Switch)
|
||||
# These are more like expressions with a reserved
|
||||
# name that produce a value of a builtin type,
|
||||
|
|
|
@ -12,4 +12,3 @@ fn identity(x: int32): int32 {
|
|||
fn nope[T: int32 | int16](x: T): T {
|
||||
return identity(x);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +1,18 @@
|
|||
import std;
|
||||
|
||||
|
||||
print("Counting down...");
|
||||
var from = 10;
|
||||
let to = 0;
|
||||
while from > to {
|
||||
print(from);
|
||||
from = from - 1;
|
||||
}
|
||||
print("Done!");
|
||||
print(from == to);
|
||||
|
||||
print("Counting up...");
|
||||
var start = 0;
|
||||
let stop = 10;
|
||||
while start < stop {
|
||||
print(start);
|
||||
start = start + 1;
|
||||
}
|
||||
print("Done!");
|
||||
print(start == stop);
|
||||
|
||||
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue