summary refs log tree commit diff stats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/collections/tcounttable.nim19
-rw-r--r--tests/collections/tsets.nim48
-rw-r--r--tests/collections/ttables.nim16
-rw-r--r--tests/collections/ttablesref.nim18
-rw-r--r--tests/distinct/tdistinct_consts.nim20
-rw-r--r--tests/manyloc/standalone/panicoverride.nim6
-rw-r--r--tests/sets/tsets.nim170
-rw-r--r--tests/template/tdefault_nil.nim14
-rw-r--r--tests/tuples/tuple_with_nil.nim766
-rw-r--r--tests/tuples/tuple_with_seq.nim46
-rw-r--r--tests/types/temptyseqs.nim66
11 files changed, 1141 insertions, 48 deletions
diff --git a/tests/collections/tcounttable.nim b/tests/collections/tcounttable.nim
new file mode 100644
index 000000000..ebbb1c8e5
--- /dev/null
+++ b/tests/collections/tcounttable.nim
@@ -0,0 +1,19 @@
+discard """
+  output: "And we get here"
+"""
+
+# bug #2625
+
+const s_len = 32
+
+import tables
+var substr_counts: CountTable[string] = initCountTable[string]()
+var my_string = "Hello, this is sadly broken for strings over 64 characters. Note that it *does* appear to work for short strings."
+for i in 0..(my_string.len - s_len):
+  let s = my_string[i..i+s_len-1]
+  substr_counts[s] = 1
+  # substr_counts[s] = substr_counts[s] + 1  # Also breaks, + 2 as well, etc.
+  # substr_counts.inc(s)  # This works
+  #echo "Iteration ", i
+
+echo "And we get here"
diff --git a/tests/collections/tsets.nim b/tests/collections/tsets.nim
index 656c5b3f2..a5bbe8dbd 100644
--- a/tests/collections/tsets.nim
+++ b/tests/collections/tsets.nim
@@ -1,17 +1,37 @@
-discard """
-  output: '''true
-true'''
-"""
-
 import sets
-var
-  a = initSet[int]()
-  b = initSet[int]()
-  c = initSet[string]()
 
-for i in 0..5: a.incl(i)
-for i in 1..6: b.incl(i)
-for i in 0..5: c.incl($i)
+block setEquality:
+  var
+    a = initSet[int]()
+    b = initSet[int]()
+    c = initSet[string]()
+
+  for i in 0..5: a.incl(i)
+  for i in 1..6: b.incl(i)
+  for i in 0..5: c.incl($i)
+
+  doAssert map(a, proc(x: int): int = x + 1) == b
+  doAssert map(a, proc(x: int): string = $x) == c
+
+
+block setsContainingTuples:
+  var set = initSet[tuple[i: int, i64: int64, f: float]]()
+  set.incl( (i: 123, i64: 123'i64, f: 3.14) )
+  doAssert set.contains( (i: 123, i64: 123'i64, f: 3.14) )
+  doAssert( not set.contains( (i: 456, i64: 789'i64, f: 2.78) ) )
+
+
+block setWithTuplesWithSeqs:
+  var s = initSet[tuple[s: seq[int]]]()
+  s.incl( (s: @[1, 2, 3]) )
+  doAssert s.contains( (s: @[1, 2, 3]) )
+  doAssert( not s.contains((s: @[4, 5, 6])) )
+
+
+block setWithSequences:
+  var s = initSet[seq[int]]()
+  s.incl( @[1, 2, 3] )
+  doAssert s.contains(@[1, 2, 3])
+  doAssert( not s.contains(@[4, 5, 6]) )
+
 
-echo map(a, proc(x: int): int = x + 1) == b
-echo map(a, proc(x: int): string = $x) == c
diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim
index 3a923610e..a10606843 100644
--- a/tests/collections/ttables.nim
+++ b/tests/collections/ttables.nim
@@ -22,15 +22,15 @@ const
     "---00": 346677844,
     "0": 34404,
     "1": 344004,
-    "10": 34484, 
+    "10": 34484,
     "11": 34474,
     "12": 789,
     "19": 34464,
-    "2": 344774, "20": 34454, 
+    "2": 344774, "20": 34454,
     "3": 342244, "30": 34141244,
     "34": 123456,
     "4": 3412344, "40": 344114,
-    "5": 341232144, "50": 344490, 
+    "5": 341232144, "50": 344490,
     "6": 34214544, "60": 344491,
     "7": 3434544, "70": 344492,
     "8": 344544, "80": 344497,
@@ -46,7 +46,7 @@ block tableTest1:
   for x in 0..1:
     for y in 0..1:
       assert t[(x,y)] == $x & $y
-  assert($t == 
+  assert($t ==
     "{(x: 0, y: 1): 01, (x: 0, y: 0): 00, (x: 1, y: 0): 10, (x: 1, y: 1): 11}")
 
 block tableTest2:
@@ -55,14 +55,16 @@ block tableTest2:
   t["111"] = 1.000043
   t["123"] = 1.23
   t.del("111")
-  
+
   t["012"] = 67.9
   t["123"] = 1.5 # test overwriting
-  
+
   assert t["123"] == 1.5
   assert t["111"] == 0.0 # deleted
   assert(not hasKey(t, "111"))
-  
+  assert "123" in t
+  assert("111" notin t)
+
   for key, val in items(data): t[key] = val.toFloat
   for key, val in items(data): assert t[key] == val.toFloat
 
diff --git a/tests/collections/ttablesref.nim b/tests/collections/ttablesref.nim
index 16b0d831e..0b641ebc7 100644
--- a/tests/collections/ttablesref.nim
+++ b/tests/collections/ttablesref.nim
@@ -22,15 +22,15 @@ const
     "---00": 346677844,
     "0": 34404,
     "1": 344004,
-    "10": 34484, 
+    "10": 34484,
     "11": 34474,
     "12": 789,
     "19": 34464,
-    "2": 344774, "20": 34454, 
+    "2": 344774, "20": 34454,
     "3": 342244, "30": 34141244,
     "34": 123456,
     "4": 3412344, "40": 344114,
-    "5": 341232144, "50": 344490, 
+    "5": 341232144, "50": 344490,
     "6": 34214544, "60": 344491,
     "7": 3434544, "70": 344492,
     "8": 344544, "80": 344497,
@@ -46,7 +46,7 @@ block tableTest1:
   for x in 0..1:
     for y in 0..1:
       assert t[(x,y)] == $x & $y
-  assert($t == 
+  assert($t ==
     "{(x: 0, y: 1): 01, (x: 0, y: 0): 00, (x: 1, y: 0): 10, (x: 1, y: 1): 11}")
 
 block tableTest2:
@@ -55,17 +55,19 @@ block tableTest2:
   t["111"] = 1.000043
   t["123"] = 1.23
   t.del("111")
-  
+
   t["012"] = 67.9
   t["123"] = 1.5 # test overwriting
-  
+
   assert t["123"] == 1.5
   assert t["111"] == 0.0 # deleted
+  assert "123" in t
   assert(not hasKey(t, "111"))
-  
+  assert "111" notin t
+
   for key, val in items(data): t[key] = val.toFloat
   for key, val in items(data): assert t[key] == val.toFloat
-  
+
 
 block orderedTableTest1:
   var t = newOrderedTable[string, int](2)
diff --git a/tests/distinct/tdistinct_consts.nim b/tests/distinct/tdistinct_consts.nim
new file mode 100644
index 000000000..4f6ced2d2
--- /dev/null
+++ b/tests/distinct/tdistinct_consts.nim
@@ -0,0 +1,20 @@
+
+# bug #2641
+
+type MyChar = distinct char
+const c:MyChar = MyChar('a')
+
+type MyBool = distinct bool
+const b:MyBool = MyBool(true)
+
+type MyBoolSet = distinct set[bool]
+const bs:MyBoolSet = MyBoolSet({true})
+
+type MyCharSet= distinct set[char]
+const cs:MyCharSet = MyCharSet({'a'})
+
+type MyBoolSeq = distinct seq[bool]
+const bseq:MyBoolSeq = MyBoolSeq(@[true, false])
+
+type MyBoolArr = distinct array[3, bool]
+const barr:MyBoolArr = MyBoolArr([true, false, true])
diff --git a/tests/manyloc/standalone/panicoverride.nim b/tests/manyloc/standalone/panicoverride.nim
index efd2b21f9..d9b3f4388 100644
--- a/tests/manyloc/standalone/panicoverride.nim
+++ b/tests/manyloc/standalone/panicoverride.nim
@@ -7,13 +7,13 @@ proc exit(code: int) {.importc, header: "<stdlib.h>", cdecl.}
 proc rawoutput(s: string) =
   printf("%s\n", s)
 
-proc panic(s: string) =
+proc panic(s: string) {.noreturn.} =
   rawoutput(s)
   exit(1)
 
 # Alternatively we also could implement these 2 here:
 #
-# template sysFatal(exceptn: typeDesc, message: string)
-# template sysFatal(exceptn: typeDesc, message, arg: string)
+# proc sysFatal(exceptn: typeDesc, message: string) {.noReturn.}
+# proc sysFatal(exceptn: typeDesc, message, arg: string) {.noReturn.}
 
 {.pop.}
diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim
index e370209ed..646175329 100644
--- a/tests/sets/tsets.nim
+++ b/tests/sets/tsets.nim
@@ -1,6 +1,7 @@
 discard """

   file: "tsets.nim"

-  output: "Ha ein F ist in s!"

+  output: '''Ha ein F ist in s!

+false'''

 """

 # Test the handling of sets

 

@@ -15,30 +16,30 @@ type
   TAZ = range['a'..'z']

   TAZset = set[TAZ]

 

-  TTokType* = enum 

+  TTokType* = enum

     tkInvalid, tkEof,

     tkSymbol,

-    tkAddr, tkAnd, tkAs, tkAsm, tkBlock, tkBreak, tkCase, tkCast, tkConst, 

-    tkContinue, tkConverter, tkDiscard, tkDiv, tkElif, tkElse, tkEnd, tkEnum, 

-    tkExcept, tkException, tkFinally, tkFor, tkFrom, tkGeneric, tkIf, tkImplies, 

-    tkImport, tkIn, tkInclude, tkIs, tkIsnot, tkIterator, tkLambda, tkMacro, 

-    tkMethod, tkMod, tkNil, tkNot, tkNotin, tkObject, tkOf, tkOr, tkOut, tkProc, 

-    tkPtr, tkRaise, tkRecord, tkRef, tkReturn, tkShl, tkShr, tkTemplate, tkTry, 

+    tkAddr, tkAnd, tkAs, tkAsm, tkBlock, tkBreak, tkCase, tkCast, tkConst,

+    tkContinue, tkConverter, tkDiscard, tkDiv, tkElif, tkElse, tkEnd, tkEnum,

+    tkExcept, tkException, tkFinally, tkFor, tkFrom, tkGeneric, tkIf, tkImplies,

+    tkImport, tkIn, tkInclude, tkIs, tkIsnot, tkIterator, tkLambda, tkMacro,

+    tkMethod, tkMod, tkNil, tkNot, tkNotin, tkObject, tkOf, tkOr, tkOut, tkProc,

+    tkPtr, tkRaise, tkRecord, tkRef, tkReturn, tkShl, tkShr, tkTemplate, tkTry,

     tkType, tkVar, tkWhen, tkWhere, tkWhile, tkWith, tkWithout, tkXor, tkYield,

-    tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, tkFloatLit, 

-    tkFloat32Lit, tkFloat64Lit, tkStrLit, tkRStrLit, tkTripleStrLit, tkCharLit, 

-    tkRCharLit, tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, 

-    tkCurlyRi, tkBracketDotLe, tkBracketDotRi, 

-    tkCurlyDotLe, tkCurlyDotRi, 

+    tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, tkFloatLit,

+    tkFloat32Lit, tkFloat64Lit, tkStrLit, tkRStrLit, tkTripleStrLit, tkCharLit,

+    tkRCharLit, tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe,

+    tkCurlyRi, tkBracketDotLe, tkBracketDotRi,

+    tkCurlyDotLe, tkCurlyDotRi,

     tkParDotLe, tkParDotRi,

-    tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkHat, tkOpr, 

+    tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkHat, tkOpr,

     tkComment, tkAccent, tkInd, tkSad, tkDed,

     tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr

   TTokTypeRange = range[tkSymbol..tkDed]

   TTokTypes* = set[TTokTypeRange]

 

 const

-  toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit), 

+  toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit),

                          tkStrLit..tkTripleStrLit}

 

 var

@@ -62,3 +63,142 @@ for x in low(TTokTypeRange) .. high(TTokTypeRange):
 #OUT Ha ein F ist in s!

 

 

+type

+  TMsgKind* = enum

+    errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated,

+    errXCompilerDoesNotSupportCpp, errStringLiteralExpected,

+    errIntLiteralExpected, errInvalidCharacterConstant,

+    errClosingTripleQuoteExpected, errClosingQuoteExpected,

+    errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong,

+    errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter,

+    errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected,

+    errNewlineExpected,

+    errInvalidModuleName,

+    errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected,

+    errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected,

+    errInvalidPragma, errUnknownPragma, errInvalidDirectiveX,

+    errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,

+    errExceptionExpected, errExceptionAlreadyHandled,

+    errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,

+    errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine,

+    errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,

+    errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,

+    errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,

+    errOnOrOffExpectedButXFound, errNoneBoehmRefcExpectedButXFound,

+    errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound,

+    errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound,

+    errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected,

+    errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected,

+    errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable,

+    errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue,

+    errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous,

+    errConstantDivisionByZero, errOrdinalTypeExpected,

+    errOrdinalOrFloatTypeExpected, errOverOrUnderflow,

+    errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255,

+    errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess,

+    errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType,

+    errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit,

+    errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType,

+    errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected,

+    errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected,

+    errMagicOnlyInSystem, errPowerOfTwoExpected,

+    errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv,

+    errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected,

+    errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes,

+    errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid,

+    errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop,

+    errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue,

+    errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig,

+    errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects,

+    errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX,

+    errCannotInstantiateX, errExprHasNoAddress, errXStackEscape,

+    errVarForOutParamNeeded,

+    errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,

+    errAmbiguousCallXYZ, errWrongNumberOfArguments,

+    errXCannotBePassedToProcVar,

+    errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed,

+    errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,

+    errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,

+    errInvalidOrderInArrayConstructor,

+    errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry,

+    errOptionExpected, errXisNoLabel, errNotAllCasesCovered,

+    errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable,

+    errNoPragmasAllowedForX, errNoGenericParamsAllowedForX,

+    errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent,

+    errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX,

+    errXNotAllowedHere, errInvalidControlFlowX,

+    errXisNoType, errCircumNeedsPointer, errInvalidExpression,

+    errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected,

+    errNamedExprNotAllowed, errXExpectsOneTypeParam,

+    errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed,

+    errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType,

+    errNoReturnTypeDeclared,

+    errInvalidCommandX, errXOnlyAtModuleScope,

+    errXNeedsParamObjectType,

+    errTemplateInstantiationTooNested, errInstantiationFrom,

+    errInvalidIndexValueForTuple, errCommandExpectsFilename,

+    errMainModuleMustBeSpecified,

+    errXExpected,

+    errTIsNotAConcreteType,

+    errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,

+    errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,

+    errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,

+    errOnlyACallOpCanBeDelegator, errUsingNoSymbol,

+    errMacroBodyDependsOnGenericTypes,

+    errDestructorNotGenericEnough,

+    errInlineIteratorsAsProcParams,

+    errXExpectsTwoArguments,

+    errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,

+    errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,

+    errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,

+    errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,

+    errXhasSideEffects, errIteratorExpected, errLetNeedsInit,

+    errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,

+    errXCannotBeClosure, errXMustBeCompileTime,

+    errCannotInferTypeOfTheLiteral,

+    errCannotInferReturnType,

+    errGenericLambdaNotAllowed,

+    errCompilerDoesntSupportTarget,

+    errUser,

+    warnCannotOpenFile,

+    warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,

+    warnDeprecated, warnConfigDeprecated,

+    warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,

+    warnUnknownSubstitutionX, warnLanguageXNotSupported,

+    warnFieldXNotSupported, warnCommentXIgnored,

+    warnNilStatement, warnTypelessParam,

+    warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,

+    warnEachIdentIsTuple, warnShadowIdent,

+    warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,

+    warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,

+    warnUser,

+    hintSuccess, hintSuccessX,

+    hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,

+    hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,

+    hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,

+    hintConditionAlwaysTrue, hintName, hintPattern,

+    hintUser

+

+const

+  fatalMin* = errUnknown

+  fatalMax* = errInternal

+  errMin* = errUnknown

+  errMax* = errUser

+  warnMin* = warnCannotOpenFile

+  warnMax* = pred(hintSuccess)

+  hintMin* = hintSuccess

+  hintMax* = high(TMsgKind)

+

+type

+  TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints

+  TNoteKinds* = set[TNoteKind]

+

+var

+  gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} -

+                        {warnShadowIdent, warnUninit,

+                         warnProveField, warnProveIndex, warnGcUnsafe}

+

+

+#import compiler.msgs

+

+echo warnUninit in gNotes

diff --git a/tests/template/tdefault_nil.nim b/tests/template/tdefault_nil.nim
new file mode 100644
index 000000000..891166306
--- /dev/null
+++ b/tests/template/tdefault_nil.nim
@@ -0,0 +1,14 @@
+
+# bug #2629
+import sequtils, os
+
+template glob_rst(basedir: string = nil): expr =
+  if baseDir.isNil:
+    to_seq(walk_files("*.rst"))
+  else:
+    to_seq(walk_files(basedir/"*.rst"))
+
+let
+  rst_files = concat(glob_rst(), glob_rst("docs"))
+
+when isMainModule: echo rst_files
diff --git a/tests/tuples/tuple_with_nil.nim b/tests/tuples/tuple_with_nil.nim
new file mode 100644
index 000000000..26e4ae85e
--- /dev/null
+++ b/tests/tuples/tuple_with_nil.nim
@@ -0,0 +1,766 @@
+import macros
+from strutils import IdentStartChars
+import parseutils
+import unicode
+import math
+import fenv
+import unsigned
+import pegs
+import streams
+
+type
+  FormatError = object of Exception ## Error in the format string.
+
+  Writer = concept W
+    ## Writer to output a character `c`.
+    when (NimMajor, NimMinor, NimPatch) > (0, 10, 2):
+      write(W, 'c')
+    else:
+      block:
+        var x: W
+        write(x, char)
+
+  FmtAlign = enum ## Format alignment
+    faDefault  ## default for given format type
+    faLeft     ## left aligned
+    faRight    ## right aligned
+    faCenter   ## centered
+    faPadding  ## right aligned, fill characters after sign (numbers only)
+
+  FmtSign = enum ## Format sign
+    fsMinus    ## only unary minus, no reservered sign space for positive numbers
+    fsPlus     ## unary minus and unary plus
+    fsSpace    ## unary minus and reserved space for positive numbers
+
+  FmtType = enum ## Format type
+    ftDefault  ## default format for given parameter type
+    ftStr      ## string
+    ftChar     ## character
+    ftDec      ## decimal integer
+    ftBin      ## binary integer
+    ftOct      ## octal integer
+    ftHex      ## hexadecimal integer
+    ftFix      ## real number in fixed point notation
+    ftSci      ## real number in scientific notation
+    ftGen      ## real number in generic form (either fixed point or scientific)
+    ftPercent  ## real number multiplied by 100 and % added
+
+  Format = tuple ## Formatting information.
+    typ: FmtType     ## format type
+    precision: int    ## floating point precision
+    width: int        ## minimal width
+    fill: string      ## the fill character, UTF8
+    align: FmtAlign  ## aligment
+    sign: FmtSign    ## sign notation
+    baseprefix: bool  ## whether binary, octal, hex should be prefixed by 0b, 0x, 0o
+    upcase: bool      ## upper case letters in hex or exponential formats
+    comma: bool       ##
+    arysep: string    ## separator for array elements
+
+  PartKind = enum pkStr, pkFmt
+
+  Part = object
+    ## Information of a part of the target string.
+    case kind: PartKind ## type of the part
+    of pkStr:
+      str: string ## literal string
+    of pkFmt:
+      arg: int ## position argument
+      fmt: string ## format string
+      field: string ## field of argument to be accessed
+      index: int ## array index of argument to be accessed
+      nested: bool ## true if the argument contains nested formats
+
+const
+  DefaultPrec = 6 ## Default precision for floating point numbers.
+  DefaultFmt: Format = (ftDefault, -1, -1, nil, faDefault, fsMinus, false, false, false, nil)
+    ## Default format corresponding to the empty format string, i.e.
+    ##   `x.format("") == x.format(DefaultFmt)`.
+  round_nums = [0.5, 0.05, 0.005, 0.0005, 0.00005, 0.000005, 0.0000005, 0.00000005]
+    ## Rounding offset for floating point numbers up to precision 8.
+
+proc write(s: var string; c: char) =
+  s.add(c)
+
+proc has(c: Captures; i: range[0..pegs.MaxSubpatterns-1]): bool {.nosideeffect, inline.} =
+  ## Tests whether `c` contains a non-empty capture `i`.
+  let b = c.bounds(i)
+  result = b.first <= b.last
+
+proc get(str: string; c: Captures; i: range[0..MaxSubpatterns-1]; def: char): char {.nosideeffect, inline.} =
+  ## If capture `i` is non-empty return that portion of `str` casted
+  ## to `char`, otherwise return `def`.
+  result = if c.has(i): str[c.bounds(i).first] else: def
+
+proc get(str: string; c: Captures; i: range[0..MaxSubpatterns-1]; def: string; begoff: int = 0): string {.nosideeffect, inline.} =
+  ## If capture `i` is non-empty return that portion of `str` as
+  ## string, otherwise return `def`.
+  let b = c.bounds(i)
+  result = if c.has(i): str.substr(b.first + begoff, b.last) else: def
+
+proc get(str: string; c: Captures; i: range[0..MaxSubpatterns-1]; def: int; begoff: int = 0): int {.nosideeffect, inline.} =
+  ## If capture `i` is non-empty return that portion of `str`
+  ## converted to int, otherwise return `def`.
+  if c.has(i):
+    discard str.parseInt(result, c.bounds(i).first + begoff)
+  else:
+    result = def
+
+proc parse(fmt: string): Format {.nosideeffect.} =
+  # Converts the format string `fmt` into a `Format` structure.
+  let p =
+    sequence(capture(?sequence(anyRune(), &charSet({'<', '>', '=', '^'}))),
+             capture(?charSet({'<', '>', '=', '^'})),
+             capture(?charSet({'-', '+', ' '})),
+             capture(?charSet({'#'})),
+             capture(?(+digits())),
+             capture(?charSet({','})),
+             capture(?sequence(charSet({'.'}), +digits())),
+             capture(?charSet({'b', 'c', 'd', 'e', 'E', 'f', 'F', 'g', 'G', 'n', 'o', 's', 'x', 'X', '%'})),
+             capture(?sequence(charSet({'a'}), *pegs.any())))
+  # let p=peg"{(_&[<>=^])?}{[<>=^]?}{[-+ ]?}{[#]?}{[0-9]+?}{[,]?}{([.][0-9]+)?}{[bcdeEfFgGnosxX%]?}{(a.*)?}"
+
+  var caps: Captures
+  if fmt.rawmatch(p, 0, caps) < 0:
+    raise newException(FormatError, "Invalid format string")
+
+  result.fill = fmt.get(caps, 0, nil)
+
+  case fmt.get(caps, 1, 0.char)
+  of '<': result.align = faLeft
+  of '>': result.align = faRight
+  of '^': result.align = faCenter
+  of '=': result.align = faPadding
+  else: result.align = faDefault
+
+  case fmt.get(caps, 2, '-')
+  of '-': result.sign = fsMinus
+  of '+': result.sign = fsPlus
+  of ' ': result.sign = fsSpace
+  else: result.sign = fsMinus
+
+  result.baseprefix = caps.has(3)
+
+  result.width = fmt.get(caps, 4, -1)
+
+  if caps.has(4) and fmt[caps.bounds(4).first] == '0':
+    if result.fill != nil:
+      raise newException(FormatError, "Leading 0 in with not allowed with explicit fill character")
+    if result.align != faDefault:
+      raise newException(FormatError, "Leading 0 in with not allowed with explicit alignment")
+    result.fill = "0"
+    result.align = faPadding
+
+  result.comma = caps.has(5)
+
+  result.precision = fmt.get(caps, 6, -1, 1)
+
+  case fmt.get(caps, 7, 0.char)
+  of 's': result.typ = ftStr
+  of 'c': result.typ = ftChar
+  of 'd', 'n': result.typ = ftDec
+  of 'b': result.typ = ftBin
+  of 'o': result.typ = ftOct
+  of 'x': result.typ = ftHex
+  of 'X': result.typ = ftHex; result.upcase = true
+  of 'f', 'F': result.typ = ftFix
+  of 'e': result.typ = ftSci
+  of 'E': result.typ = ftSci; result.upcase = true
+  of 'g': result.typ = ftGen
+  of 'G': result.typ = ftGen; result.upcase = true
+  of '%': result.typ = ftPercent
+  else: result.typ = ftDefault
+
+  result.arysep = fmt.get(caps, 8, nil, 1)
+
+proc getalign(fmt: Format; defalign: FmtAlign; slen: int) : tuple[left, right:int] {.nosideeffect.} =
+  ## Returns the number of left and right padding characters for a
+  ## given format alignment and width of the object to be printed.
+  ##
+  ## `fmt`
+  ##    the format data
+  ## `default`
+  ##    if `fmt.align == faDefault`, then this alignment is used
+  ## `slen`
+  ##    the width of the object to be printed.
+  ##
+  ## The returned values `(left, right)` will be as minimal as possible
+  ## so that `left + slen + right >= fmt.width`.
+  result.left = 0
+  result.right = 0
+  if (fmt.width >= 0) and (slen < fmt.width):
+    let alg = if fmt.align == faDefault: defalign else: fmt.align
+    case alg:
+    of faLeft: result.right = fmt.width - slen
+    of faRight, faPadding: result.left = fmt.width - slen
+    of faCenter:
+      result.left = (fmt.width - slen) div 2
+      result.right = fmt.width - slen - result.left
+    else: discard
+
+proc writefill(o: var Writer; fmt: Format; n: int; signum: int = 0) =
+  ## Write characters for filling. This function also writes the sign
+  ## of a numeric format and handles the padding alignment
+  ## accordingly.
+  ##
+  ## `o`
+  ##   output object
+  ## `add`
+  ##   output function
+  ## `fmt`
+  ##   format to be used (important for padding aligment)
+  ## `n`
+  ##   the number of filling characters to be written
+  ## `signum`
+  ##   the sign of the number to be written, < 0 negative, > 0 positive, = 0 zero
+  if fmt.align == faPadding and signum != 0:
+    if signum < 0: write(o, '-')
+    elif fmt.sign == fsPlus: write(o, '+')
+    elif fmt.sign == fsSpace: write(o, ' ')
+
+  if fmt.fill == nil:
+    for i in 1..n: write(o, ' ')
+  else:
+    for i in 1..n:
+      for c in fmt.fill:
+        write(o, c)
+
+  if fmt.align != faPadding and signum != 0:
+    if signum < 0: write(o, '-')
+    elif fmt.sign == fsPlus: write(o, '+')
+    elif fmt.sign == fsSpace: write(o, ' ')
+
+proc writeformat(o: var Writer; s: string; fmt: Format) =
+  ## Write string `s` according to format `fmt` using output object
+  ## `o` and output function `add`.
+  if fmt.typ notin {ftDefault, ftStr}:
+    raise newException(FormatError, "String variable must have 's' format type")
+
+  # compute alignment
+  let len = if fmt.precision < 0: runelen(s) else: min(runelen(s), fmt.precision)
+  var alg = getalign(fmt, faLeft, len)
+  writefill(o, fmt, alg.left)
+  var pos = 0
+  for i in 0..len-1:
+    let rlen = runeLenAt(s, pos)
+    for j in pos..pos+rlen-1: write(o, s[j])
+    pos += rlen
+  writefill(o, fmt, alg.right)
+
+proc writeformat(o: var Writer; c: char; fmt: Format) =
+  ## Write character `c` according to format `fmt` using output object
+  ## `o` and output function `add`.
+  if not (fmt.typ in {ftChar, ftDefault}):
+    raise newException(FormatError, "Character variable must have 'c' format type")
+
+  # compute alignment
+  var alg = getalign(fmt, faLeft, 1)
+  writefill(o, fmt, alg.left)
+  write(o, c)
+  writefill(o, fmt, alg.right)
+
+proc writeformat(o: var Writer; c: Rune; fmt: Format) =
+  ## Write rune `c` according to format `fmt` using output object
+  ## `o` and output function `add`.
+  if not (fmt.typ in {ftChar, ftDefault}):
+    raise newException(FormatError, "Character variable must have 'c' format type")
+
+  # compute alignment
+  var alg = getalign(fmt, faLeft, 1)
+  writefill(o, fmt, alg.left)
+  let s = c.toUTF8
+  for c in s: write(o, c)
+  writefill(o, fmt, alg.right)
+
+proc abs(x: SomeUnsignedInt): SomeUnsignedInt {.inline.} = x
+  ## Return the absolute value of the unsigned int `x`.
+
+proc writeformat(o: var Writer; i: SomeInteger; fmt: Format) =
+  ## Write integer `i` according to format `fmt` using output object
+  ## `o` and output function `add`.
+  var fmt = fmt
+  if fmt.typ == ftDefault:
+    fmt.typ = ftDec
+  if not (fmt.typ in {ftBin, ftOct, ftHex, ftDec}):
+    raise newException(FormatError, "Integer variable must of one of the following types: b,o,x,X,d,n")
+
+  var base: type(i)
+  var len = 0
+  case fmt.typ:
+  of ftDec:
+    base = 10
+  of ftBin:
+    base = 2
+    if fmt.baseprefix: len += 2
+  of ftOct:
+    base = 8
+    if fmt.baseprefix: len += 2
+  of ftHex:
+    base = 16
+    if fmt.baseprefix: len += 2
+  else: assert(false)
+
+  if fmt.sign != fsMinus or i < 0: len.inc
+
+  var x: type(i) = abs(i)
+  var irev: type(i) = 0
+  var ilen = 0
+  while x > 0.SomeInteger:
+    len.inc
+    ilen.inc
+    irev = irev * base + x mod base
+    x = x div base
+  if ilen == 0:
+    ilen.inc
+    len.inc
+
+  var alg = getalign(fmt, faRight, len)
+  writefill(o, fmt, alg.left, if i >= 0.SomeInteger: 1 else: -1)
+  if fmt.baseprefix:
+    case fmt.typ
+    of ftBin:
+      write(o, '0')
+      write(o, 'b')
+    of ftOct:
+      write(o, '0')
+      write(o, 'o')
+    of ftHex:
+      write(o, '0')
+      write(o, 'x')
+    else:
+      raise newException(FormatError, "# only allowed with b, o, x or X")
+  while ilen > 0:
+    ilen.dec
+    let c = irev mod base
+    irev = irev div base
+    if c < 10:
+      write(o, ('0'.int + c.int).char)
+    elif fmt.upcase:
+      write(o, ('A'.int + c.int - 10).char)
+    else:
+      write(o, ('a'.int + c.int - 10).char)
+  writefill(o, fmt, alg.right)
+
+proc writeformat(o: var Writer; p: pointer; fmt: Format) =
+  ## Write pointer `i` according to format `fmt` using output object
+  ## `o` and output function `add`.
+  ##
+  ## Pointers are casted to unsigned int and formated as hexadecimal
+  ## with prefix unless specified otherwise.
+  var f = fmt
+  if f.typ == 0.char:
+    f.typ = 'x'
+    f.baseprefix = true
+  writeformat(o, add, cast[uint](p), f)
+
+proc writeformat(o: var Writer; x: SomeReal; fmt: Format) =
+  ## Write real number `x` according to format `fmt` using output
+  ## object `o` and output function `add`.
+  var fmt = fmt
+  # handle default format
+  if fmt.typ == ftDefault:
+    fmt.typ = ftGen
+    if fmt.precision < 0: fmt.precision = DefaultPrec
+  if not (fmt.typ in {ftFix, ftSci, ftGen, ftPercent}):
+    raise newException(FormatError, "Integer variable must of one of the following types: f,F,e,E,g,G,%")
+
+  let positive = x >= 0 and classify(x) != fcNegZero
+  var len = 0
+
+  if fmt.sign != fsMinus or not positive: len.inc
+
+  var prec = if fmt.precision < 0: DefaultPrec else: fmt.precision
+  var y = abs(x)
+  var exp = 0
+  var numstr, frstr: array[0..31, char]
+  var numlen, frbeg, frlen = 0
+
+  if fmt.typ == ftPercent: y *= 100
+
+  case classify(x):
+  of fcNan:
+    numstr[0..2] = ['n', 'a', 'n']
+    numlen = 3
+  of fcInf, fcNegInf:
+    numstr[0..2] = ['f', 'n', 'i']
+    numlen = 3
+  of fcZero, fcNegZero:
+    numstr[0] = '0'
+    numlen = 1
+  else: # a usual fractional number
+    if not (fmt.typ in {ftFix, ftPercent}): # not fixed point
+      exp = int(floor(log10(y)))
+      if fmt.typ == ftGen:
+        if prec == 0: prec = 1
+        if -4 <= exp and exp < prec:
+          prec = prec-1-exp
+          exp = 0
+        else:
+          prec = prec - 1
+          len += 4 # exponent
+      else:
+        len += 4 # exponent
+      # shift y so that 1 <= abs(y) < 2
+      if exp > 0: y /= pow(10.SomeReal, abs(exp).SomeReal)
+      elif exp < 0: y *= pow(10.SomeReal, abs(exp).SomeReal)
+    elif fmt.typ == ftPercent:
+      len += 1 # percent sign
+
+    # handle rounding by adding +0.5 * LSB
+    if prec < len(round_nums): y += round_nums[prec]
+
+    # split into integer and fractional part
+    var mult = 1'i64
+    for i in 1..prec: mult *= 10
+    var num = y.int64
+    var fr = ((y - num.SomeReal) * mult.SomeReal).int64
+    # build integer part string
+    while num != 0:
+      numstr[numlen] = ('0'.int + (num mod 10)).char
+      numlen.inc
+      num = num div 10
+    if numlen == 0:
+      numstr[0] = '0'
+      numlen.inc
+    # build fractional part string
+    while fr != 0:
+      frstr[frlen] = ('0'.int + (fr mod 10)).char
+      frlen.inc
+      fr = fr div 10
+    while frlen < prec:
+      frstr[frlen] = '0'
+      frlen.inc
+    # possible remove trailing 0
+    if fmt.typ == ftGen:
+      while frbeg < frlen and frstr[frbeg] == '0': frbeg.inc
+  # update length of string
+  len += numlen;
+  if frbeg < frlen:
+    len += 1 + frlen - frbeg # decimal point and fractional string
+
+  let alg = getalign(fmt, faRight, len)
+  writefill(o, fmt, alg.left, if positive: 1 else: -1)
+  for i in (numlen-1).countdown(0): write(o, numstr[i])
+  if frbeg < frlen:
+    write(o, '.')
+    for i in (frlen-1).countdown(frbeg): write(o, frstr[i])
+  if fmt.typ == ftSci or (fmt.typ == ftGen and exp != 0):
+    write(o, if fmt.upcase: 'E' else: 'e')
+    if exp >= 0:
+      write(o, '+')
+    else:
+      write(o, '-')
+      exp = -exp
+    if exp < 10:
+      write(o, '0')
+      write(o, ('0'.int + exp).char)
+    else:
+      var i=0
+      while exp > 0:
+        numstr[i] = ('0'.int + exp mod 10).char
+        i+=1
+        exp = exp div 10
+      while i>0:
+        i-=1
+        write(o, numstr[i])
+  if fmt.typ == ftPercent: write(o, '%')
+  writefill(o, fmt, alg.right)
+
+proc writeformat(o: var Writer; b: bool; fmt: Format) =
+  ## Write boolean value `b` according to format `fmt` using output
+  ## object `o`. A boolean may be formatted numerically or as string.
+  ## In the former case true is written as 1 and false as 0, in the
+  ## latter the strings "true" and "false" are shown, respectively.
+  ## The default is string format.
+  if fmt.typ in {ftStr, ftDefault}:
+    writeformat(o,
+                if b: "true"
+                else: "false",
+                fmt)
+  elif fmt.typ in {ftBin, ftOct, ftHex, ftDec}:
+    writeformat(o,
+                if b: 1
+                else: 0,
+                fmt)
+  else:
+    raise newException(FormatError, "Boolean values must of one of the following types: s,b,o,x,X,d,n")
+
+proc writeformat(o: var Writer; ary: openarray[any]; fmt: Format) =
+  ## Write array `ary` according to format `fmt` using output object
+  ## `o` and output function `add`.
+  if ary.len == 0: return
+
+  var sep: string
+  var nxtfmt = fmt
+  if fmt.arysep == nil:
+    sep = "\t"
+  elif fmt.arysep.len == 0:
+    sep = ""
+  else:
+    let sepch = fmt.arysep[0]
+    let nxt = 1 + skipUntil(fmt.arysep, sepch, 1)
+    if nxt >= 1:
+      nxtfmt.arysep = fmt.arysep.substr(nxt)
+      sep = fmt.arysep.substr(1, nxt-1)
+    else:
+      nxtfmt.arysep = ""
+      sep = fmt.arysep.substr(1)
+  writeformat(o, ary[0], nxtfmt)
+  for i in 1..ary.len-1:
+    for c in sep: write(o, c)
+    writeformat(o, ary[i], nxtfmt)
+
+proc addformat[T](o: var Writer; x: T; fmt: Format = DefaultFmt) {.inline.} =
+  ## Write `x` formatted with `fmt` to `o`.
+  writeformat(o, x, fmt)
+
+proc addformat[T](o: var Writer; x: T; fmt: string) {.inline.} =
+  ## The same as `addformat(o, x, parse(fmt))`.
+  addformat(o, x, fmt.parse)
+
+proc addformat(s: var string; x: string) {.inline.} =
+  ## Write `x` to `s`. This is a fast specialized version for
+  ## appending unformatted strings.
+  add(s, x)
+
+proc addformat(f: File; x: string) {.inline.} =
+  ## Write `x` to `f`. This is a fast specialized version for
+  ## writing unformatted strings to a file.
+  write(f, x)
+
+proc addformat[T](f: File; x: T; fmt: Format = DefaultFmt) {.inline.} =
+  ## Write `x` to file `f` using format `fmt`.
+  var g = f
+  writeformat(g, x, fmt)
+
+proc addformat[T](f: File; x: T; fmt: string) {.inline.} =
+  ## Write `x` to file `f` using format string `fmt`. This is the same
+  ## as `addformat(f, x, parse(fmt))`
+  addformat(f, x, parse(fmt))
+
+proc addformat(s: Stream; x: string) {.inline.} =
+  ## Write `x` to `s`. This is a fast specialized version for
+  ## writing unformatted strings to a stream.
+  write(s, x)
+
+proc addformat[T](s: Stream; x: T; fmt: Format = DefaultFmt) {.inline.} =
+  ## Write `x` to stream `s` using format `fmt`.
+  var g = s
+  writeformat(g, x, fmt)
+
+proc addformat[T](s: Stream; x: T; fmt: string) {.inline.} =
+  ## Write `x` to stream `s` using format string `fmt`. This is the same
+  ## as `addformat(s, x, parse(fmt))`
+  addformat(s, x, parse(fmt))
+
+proc format[T](x: T; fmt: Format): string =
+  ## Return `x` formatted as a string according to format `fmt`.
+  result = ""
+  addformat(result, x, fmt)
+
+proc format[T](x: T; fmt: string): string =
+  ## Return `x` formatted as a string according to format string `fmt`.
+  result = format(x, fmt.parse)
+
+proc format[T](x: T): string {.inline.} =
+  ## Return `x` formatted as a string according to the default format.
+  ## The default format corresponds to an empty format string.
+  var fmt {.global.} : Format = DefaultFmt
+  result = format(x, fmt)
+
+proc unquoted(s: string): string {.compileTime.} =
+  ## Return `s` {{ and }} by single { and }, respectively.
+  result = ""
+  var pos = 0
+  while pos < s.len:
+    let nxt = pos + skipUntil(s, {'{', '}'})
+    result.add(s.substr(pos, nxt))
+    pos = nxt + 2
+
+proc splitfmt(s: string): seq[Part] {.compiletime, nosideeffect.} =
+  ## Split format string `s` into a sequence of "parts".
+  ##
+
+  ## Each part is either a literal string or a format specification. A
+  ## format specification is a substring of the form
+  ## "{[arg][:format]}" where `arg` is either empty or a number
+  ## refering to the arg-th argument and an additional field or array
+  ## index. The format string is a string accepted by `parse`.
+  let subpeg = sequence(capture(digits()),
+                          capture(?sequence(charSet({'.'}), *pegs.identStartChars(), *identChars())),
+                          capture(?sequence(charSet({'['}), +digits(), charSet({']'}))),
+                          capture(?sequence(charSet({':'}), *pegs.any())))
+  result = @[]
+  var pos = 0
+  while true:
+    let oppos = pos + skipUntil(s, {'{', '}'}, pos)
+    # reached the end
+    if oppos >= s.len:
+      if pos < s.len:
+        result.add(Part(kind: pkStr, str: s.substr(pos).unquoted))
+      return
+    # skip double
+    if oppos + 1 < s.len and s[oppos] == s[oppos+1]:
+      result.add(Part(kind: pkStr, str: s.substr(pos, oppos)))
+      pos = oppos + 2
+      continue
+    if s[oppos] == '}':
+      error("Single '}' encountered in format string")
+    if oppos > pos:
+      result.add(Part(kind: pkStr, str: s.substr(pos, oppos-1).unquoted))
+    # find matching closing }
+    var lvl = 1
+    var nested = false
+    pos = oppos
+    while lvl > 0:
+      pos.inc
+      pos = pos + skipUntil(s, {'{', '}'}, pos)
+      if pos >= s.len:
+        error("Single '{' encountered in format string")
+      if s[pos] == '{':
+        lvl.inc
+        if lvl == 2:
+          nested = true
+        if lvl > 2:
+          error("Too many nested format levels")
+      else:
+        lvl.dec
+    let clpos = pos
+    var fmtpart = Part(kind: pkFmt, arg: -1, fmt: s.substr(oppos+1, clpos-1), field: nil, index: int.high, nested: nested)
+    if fmtpart.fmt.len > 0:
+      var m: array[0..3, string]
+      if not fmtpart.fmt.match(subpeg, m):
+        error("invalid format string")
+
+      if m[1] != nil and m[1].len > 0:
+        fmtpart.field = m[1].substr(1)
+      if m[2] != nil and m[2].len > 0:
+        discard parseInt(m[2].substr(1, m[2].len-2), fmtpart.index)
+
+      if m[0].len > 0: discard parseInt(m[0], fmtpart.arg)
+      if m[3] == nil or m[3].len == 0:
+        fmtpart.fmt = ""
+      elif m[3][0] == ':':
+        fmtpart.fmt = m[3].substr(1)
+      else:
+        fmtpart.fmt = m[3]
+    result.add(fmtpart)
+    pos = clpos + 1
+
+proc literal(s: string): NimNode {.compiletime, nosideeffect.} =
+  ## Return the nim literal of string `s`. This handles the case if
+  ## `s` is nil.
+  result = if s == nil: newNilLit() else: newLit(s)
+
+proc literal(b: bool): NimNode {.compiletime, nosideeffect.} =
+  ## Return the nim literal of boolean `b`. This is either `true`
+  ## or `false` symbol.
+  result = if b: "true".ident else: "false".ident
+
+proc literal[T](x: T): NimNode {.compiletime, nosideeffect.} =
+  ## Return the nim literal of value `x`.
+  when type(x) is enum:
+    result = ($x).ident
+  else:
+    result = newLit(x)
+
+proc generatefmt(fmtstr: string;
+                 args: var openarray[tuple[arg:NimNode, cnt:int]];
+                 arg: var int;): seq[tuple[val, fmt:NimNode]] {.compiletime.} =
+  ## fmtstr
+  ##   the format string
+  ## args
+  ##   array of expressions for the arguments
+  ## arg
+  ##   the number of the next argument for automatic parsing
+  ##
+  ## If arg is < 0 then the functions assumes that explicit numbering
+  ## must be used, otherwise automatic numbering is used starting at
+  ## `arg`. The value of arg is updated according to the number of
+  ## arguments being used. If arg == 0 then automatic and manual
+  ## numbering is not decided (because no explicit manual numbering is
+  ## fixed und no automatically numbered argument has been used so
+  ## far).
+  ##
+  ## The function returns a list of pairs `(val, fmt)` where `val` is
+  ## an expression to be formatted and `fmt` is the format string (or
+  ## Format). Therefore, the resulting string can be generated by
+  ## concatenating expressions `val.format(fmt)`. If `fmt` is `nil`
+  ## then `val` is a (literal) string expression.
+  try:
+    result = @[]
+    for part in splitfmt(fmtstr):
+      case part.kind
+      of pkStr: result.add((newLit(part.str), nil))
+      of pkFmt:
+        # first compute the argument expression
+        # start with the correct index
+        var argexpr : NimNode
+        if part.arg >= 0:
+          if arg > 0:
+            error("Cannot switch from automatic field numbering to manual field specification")
+          if part.arg >= args.len:
+            error("Invalid explicit argument index: " & $part.arg)
+          argexpr = args[part.arg].arg
+          args[part.arg].cnt = args[part.arg].cnt + 1
+          arg = -1
+        else:
+          if arg < 0:
+            error("Cannot switch from manual field specification to automatic field numbering")
+          if arg >= args.len:
+            error("Too few arguments for format string")
+          argexpr = args[arg].arg
+          args[arg].cnt = args[arg].cnt + 1
+          arg.inc
+        # possible field access
+        if part.field != nil and part.field.len > 0:
+          argexpr = newDotExpr(argexpr, part.field.ident)
+        # possible array access
+        if part.index < int.high:
+          argexpr = newNimNode(nnkBracketExpr).add(argexpr, newLit(part.index))
+        # now the expression for the format data
+        var fmtexpr: NimNode
+        if part.nested:
+          # nested format string. Compute the format string by
+          # concatenating the parts of the substring.
+          for e in generatefmt(part.fmt, args, arg):
+            var newexpr = if part.fmt == nil: e.val else: newCall(bindsym"format", e.val, e.fmt)
+            if fmtexpr != nil and fmtexpr.kind != nnkNilLit:
+              fmtexpr = infix(fmtexpr, "&", newexpr)
+            else:
+              fmtexpr = newexpr
+        else:
+          # literal format string, precompute the format data
+          fmtexpr = newNimNode(nnkPar)
+          for field, val in part.fmt.parse.fieldPairs:
+            fmtexpr.add(newNimNode(nnkExprColonExpr).add(field.ident, literal(val)))
+        # add argument
+        result.add((argexpr, fmtexpr))
+  finally:
+    discard
+
+proc addfmtfmt(fmtstr: string; args: NimNode; retvar: NimNode): NimNode {.compileTime.} =
+  var argexprs = newseq[tuple[arg:NimNode; cnt:int]](args.len)
+  result = newNimNode(nnkStmtListExpr)
+  # generate let bindings for arguments
+  for i in 0..args.len-1:
+    let argsym = gensym(nskLet, "arg" & $i)
+    result.add(newLetStmt(argsym, args[i]))
+    argexprs[i].arg = argsym
+  # add result values
+  var arg = 0
+  for e in generatefmt(fmtstr, argexprs, arg):
+    if e.fmt == nil or e.fmt.kind == nnkNilLit:
+      result.add(newCall(bindsym"addformat", retvar, e.val))
+    else:
+      result.add(newCall(bindsym"addformat", retvar, e.val, e.fmt))
+  for i, arg in argexprs:
+    if arg.cnt == 0:
+      warning("Argument " & $(i+1) & " `" & args[i].repr & "` is not used in format string")
+
+macro addfmt(s: var string, fmtstr: string{lit}, args: varargs[expr]): expr =
+  ## The same as `s.add(fmtstr.fmt(args...))` but faster.
+  result = addfmtfmt($fmtstr, args, s)
+
+var s: string = ""
+s.addfmt("a:{}", 42)
diff --git a/tests/tuples/tuple_with_seq.nim b/tests/tuples/tuple_with_seq.nim
new file mode 100644
index 000000000..39edb500f
--- /dev/null
+++ b/tests/tuples/tuple_with_seq.nim
@@ -0,0 +1,46 @@
+discard """
+  output: '''it's nil
+@[1, 2, 3]'''
+"""
+
+template foo(s: string = nil) =
+  if isNil(s):
+    echo "it's nil"
+  else:
+    echo s
+
+foo
+
+
+# bug #2632
+
+proc takeTup(x: tuple[s: string;x: seq[int]]) =
+  discard
+
+takeTup(("foo", @[]))
+
+
+#proc foobar(): () =
+
+proc f(xs: seq[int]) =
+  discard
+
+proc g(t: tuple[n:int, xs:seq[int]]) =
+  discard
+
+when isMainModule:
+  f(@[]) # OK
+  g((1,@[1])) # OK
+  g((0,@[])) # NG
+
+
+# bug #2630
+type T = tuple[a: seq[int], b: int]
+
+var t: T = (@[1,2,3], 7)
+
+proc test(s: seq[int]): T =
+  echo s
+  (s, 7)
+
+t = test(t.a)
diff --git a/tests/types/temptyseqs.nim b/tests/types/temptyseqs.nim
index 2b07ba679..834f63729 100644
--- a/tests/types/temptyseqs.nim
+++ b/tests/types/temptyseqs.nim
@@ -1,5 +1,13 @@
 discard """
-  output: "1"
+  output: '''1
+foo
+bar
+baz
+foo
+bar
+baz
+yes
+no'''
 """
 
 # bug #1708
@@ -24,3 +32,59 @@ when true:
 const foo2: seq[string] = @[]
 
 echo foo[0][0][0]
+
+proc takeEmpty(x: openArray[string] = []) = discard
+takeEmpty()
+takeEmpty([])
+
+proc takeEmpty2(x: openArray[string] = @[]) = discard
+takeEmpty2()
+takeEmpty2([])
+takeEmpty2(@[])
+
+#takeEmpty2([nil])
+
+#rawMessage(errExecutionOfProgramFailed, [])
+
+# bug #2470
+const
+  stuff: seq[string] = @[]
+
+for str in stuff:
+  echo "str=", str
+
+# bug #1354
+proc foo4[T](more: seq[T] = @[]) =
+  var more2 = more
+
+foo4[int]()
+
+proc maino: int =
+  var wd: cstring = nil
+  inc result
+
+discard maino()
+
+proc varargso(a: varargs[string]) =
+  for x in a:
+    echo x
+
+varargso(["foo", "bar", "baz"])
+varargso("foo", "bar", "baz")
+
+
+type
+  Flago = enum
+    tfNeedsInit, tfNotNil
+
+var s: set[Flago] = {tfNeedsInit}
+
+if {tfNeedsInit, tfNotNil} * s != {}:
+  echo "yes"
+else:
+  echo "no"
+
+if {tfNeedsInit, tfNotNil} * s <= {tfNotNil}:
+  echo "yes"
+else:
+  echo "no"