Merge remote-tracking branch 'upstream/devel' into devel

This commit is contained in:
Jonathan Edwards 2015-03-11 16:26:10 -04:00
commit 50ed39fd66
129 changed files with 3195 additions and 2204 deletions

View file

@ -196,6 +196,7 @@ type
nkTypeOfExpr, # type(1+2)
nkObjectTy, # object body
nkTupleTy, # tuple body
nkTupleClassTy, # tuple type class
nkTypeClassTy, # user-defined type class
nkStaticTy, # ``static[T]``
nkRecList, # list of object parts
@ -458,7 +459,7 @@ type
tfNotNil, # type cannot be 'nil'
tfNeedsInit, # type constains a "not nil" constraint somewhere or some
# other type so that it requires inititalization
# other type so that it requires initalization
tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode
tfHasMeta, # type contains "wildcard" sub-types such as generic params
# or other type classes
@ -946,10 +947,7 @@ template `{}=`*(n: PNode, i: int, s: PNode): stmt =
n.sons[i -| n] = s
when defined(useNodeIds):
const nodeIdToDebug* = -1 # 884953 # 612794
#612840 # 612905 # 614635 # 614637 # 614641
# 423408
#429107 # 430443 # 441048 # 441090 # 441153
const nodeIdToDebug* = -1 # 299750 # 300761 #300863 # 300879
var gNodeId: int
proc newNode*(kind: TNodeKind): PNode =
@ -1320,7 +1318,7 @@ proc isGCedMem*(t: PType): bool {.inline.} =
t.kind == tyProc and t.callConv == ccClosure
proc propagateToOwner*(owner, elem: PType) =
const HaveTheirOwnEmpty = {tySequence, tySet}
const HaveTheirOwnEmpty = {tySequence, tySet, tyPtr, tyRef, tyProc}
owner.flags = owner.flags + (elem.flags * {tfHasMeta})
if tfNotNil in elem.flags:
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}:

View file

@ -203,9 +203,9 @@ proc mustRehash(length, counter: int): bool =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
proc spaces(x: int): PRope =
proc rspaces(x: int): PRope =
# returns x spaces
result = toRope(repeatChar(x))
result = toRope(spaces(x))
proc toYamlChar(c: char): string =
case c
@ -253,7 +253,7 @@ proc typeToYamlAux(n: PType, marker: var IntSet,
indent, maxRecDepth: int): PRope
proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int,
maxRecDepth: int): PRope =
var istr = spaces(indent + 2)
var istr = rspaces(indent + 2)
result = toRope("[")
var mycount = 0
for i in countup(0, high(n.data)):
@ -262,20 +262,20 @@ proc strTableToYaml(n: TStrTable, marker: var IntSet, indent: int,
appf(result, "$N$1$2",
[istr, symToYamlAux(n.data[i], marker, indent + 2, maxRecDepth - 1)])
inc(mycount)
if mycount > 0: appf(result, "$N$1", [spaces(indent)])
if mycount > 0: appf(result, "$N$1", [rspaces(indent)])
app(result, "]")
assert(mycount == n.counter)
proc ropeConstr(indent: int, c: openArray[PRope]): PRope =
# array of (name, value) pairs
var istr = spaces(indent + 2)
var istr = rspaces(indent + 2)
result = toRope("{")
var i = 0
while i <= high(c):
if i > 0: app(result, ",")
appf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]])
inc(i, 2)
appf(result, "$N$1}", [spaces(indent)])
appf(result, "$N$1}", [rspaces(indent)])
proc symToYamlAux(n: PSym, marker: var IntSet, indent: int,
maxRecDepth: int): PRope =
@ -310,9 +310,9 @@ proc typeToYamlAux(n: PType, marker: var IntSet, indent: int,
result = toRope("[")
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), typeToYamlAux(n.sons[i],
appf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)])
appf(result, "$N$1]", [spaces(indent + 2)])
appf(result, "$N$1]", [rspaces(indent + 2)])
else:
result = toRope("null")
result = ropeConstr(indent, [toRope("kind"),
@ -331,7 +331,7 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
if n == nil:
result = toRope("null")
else:
var istr = spaces(indent + 2)
var istr = rspaces(indent + 2)
result = ropef("{$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
if maxRecDepth != 0:
appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
@ -359,12 +359,12 @@ proc treeToYamlAux(n: PNode, marker: var IntSet, indent: int,
appf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), treeToYamlAux(n.sons[i],
appf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(n.sons[i],
marker, indent + 4, maxRecDepth - 1)])
appf(result, "$N$1]", [istr])
appf(result, ",$N$1\"typ\": $2",
[istr, typeToYamlAux(n.typ, marker, indent + 2, maxRecDepth)])
appf(result, "$N$1}", [spaces(indent)])
appf(result, "$N$1}", [rspaces(indent)])
proc treeToYaml(n: PNode, indent: int = 0, maxRecDepth: int = - 1): PRope =
var marker = initIntSet()
@ -408,7 +408,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
if n == nil:
result = toRope("null")
else:
var istr = spaces(indent + 2)
var istr = rspaces(indent + 2)
result = ropef("{$N$1\"kind\": $2",
[istr, makeYamlString($n.kind)])
if maxRecDepth != 0:
@ -440,11 +440,11 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
appf(result, ",$N$1\"sons\": [", [istr])
for i in countup(0, sonsLen(n) - 1):
if i > 0: app(result, ",")
appf(result, "$N$1$2", [spaces(indent + 4), debugTree(n.sons[i],
appf(result, "$N$1$2", [rspaces(indent + 4), debugTree(n.sons[i],
indent + 4, maxRecDepth - 1, renderType)])
appf(result, "$N$1]", [istr])
appf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
appf(result, "$N$1}", [spaces(indent)])
appf(result, "$N$1}", [rspaces(indent)])
proc debug(n: PSym) =
if n == nil:
@ -681,9 +681,8 @@ proc initIdentIter(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
else: result = nextIdentIter(ti, tab)
proc nextIdentIter(ti: var TIdentIter, tab: TStrTable): PSym =
var h, start: THash
h = ti.h and high(tab.data)
start = h
var h = ti.h and high(tab.data)
var start = h
result = tab.data[h]
while result != nil:
if result.name.id == ti.name.id: break

View file

@ -339,7 +339,8 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): PRope =
let typ = skipTypes(ri.sons[0].typ, abstractInst)
if pat[i+1] == '+': result.app genArgNoParam(p, ri.sons[0])
result.app(~"(")
result.app genOtherArg(p, ri, 1, typ)
if 1 < ri.len:
result.app genOtherArg(p, ri, 1, typ)
for k in j+1 .. < ri.len:
result.app(~", ")
result.app genOtherArg(p, ri, k, typ)

View file

@ -56,11 +56,6 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): PRope =
case skipTypes(ty, abstractVarRange).kind
of tyChar, tyNil:
result = intLiteral(n.intVal)
of tyInt:
if n.intVal >= low(int32) and n.intVal <= high(int32):
result = int32Literal(int32(n.intVal))
else:
result = intLiteral(n.intVal)
of tyBool:
if n.intVal != 0: result = ~"NIM_TRUE"
else: result = ~"NIM_FALSE"
@ -954,7 +949,7 @@ proc genEcho(p: BProc, n: PNode) =
initLocExpr(p, n.sons[i], a)
appf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
linefmt(p, cpsStmts, "printf($1$2);$n",
makeCString(repeatStr(n.len, "%s") & tnl), args)
makeCString(repeat("%s", n.len) & tnl), args)
proc gcUsage(n: PNode) =
if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree)

View file

@ -99,7 +99,7 @@ proc getUniqueType*(key: PType): PType =
gCanonicalTypes[k] = key
result = key
of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor:
internalError("GetUniqueType")
internalError("getUniqueType")
of tyDistinct:
if key.deepCopy != nil: result = key
else: result = getUniqueType(lastSon(key))
@ -135,7 +135,7 @@ proc getUniqueType*(key: PType): PType =
if idTableHasObjectAsKey(gTypeTable[k], key): return key
for h in countup(0, high(gTypeTable[k].data)):
var t = PType(gTypeTable[k].data[h].key)
if t != nil and sameType(t, key):
if t != nil and sameBackendType(t, key):
return t
idTablePut(gTypeTable[k], key, key)
result = key

View file

@ -164,6 +164,7 @@ proc packObject(x: PNode, typ: PType, res: pointer) =
let field = getField(typ.n, i)
pack(it, field.typ, res +! field.offset)
else:
# XXX: todo
globalError(x.info, "cannot pack unnamed tuple")
const maxPackDepth = 20

View file

@ -572,6 +572,9 @@ proc footprint(filename: string): TCrc32 =
getCompileCFileCmd(filename, true)
proc externalFileChanged(filename: string): bool =
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
return false
var crcFile = toGeneratedFile(filename.withPackageName, "crc")
var currentCrc = int(footprint(filename))
var f: File

View file

@ -37,11 +37,11 @@ const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
proc newLine(p: var TTmplParser) =
llStreamWrite(p.outp, repeatChar(p.emitPar, ')'))
llStreamWrite(p.outp, repeat(')', p.emitPar))
p.emitPar = 0
if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
if p.pendingExprLine:
llStreamWrite(p.outp, repeatChar(2))
llStreamWrite(p.outp, spaces(2))
p.pendingExprLine = false
proc scanPar(p: var TTmplParser, d: int) =
@ -88,24 +88,24 @@ proc parseLine(p: var TTmplParser) =
else:
p.info.col = int16(j)
localError(p.info, errXNotAllowedHere, "end")
llStreamWrite(p.outp, repeatChar(p.indent))
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, "#end")
of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
wConverter, wMacro, wTemplate, wMethod:
llStreamWrite(p.outp, repeatChar(p.indent))
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d))
inc(p.indent, 2)
of wElif, wOf, wElse, wExcept, wFinally:
llStreamWrite(p.outp, repeatChar(p.indent - 2))
llStreamWrite(p.outp, spaces(p.indent - 2))
llStreamWrite(p.outp, substr(p.x, d))
of wLet, wVar, wConst, wType:
llStreamWrite(p.outp, repeatChar(p.indent))
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d))
if not p.x.contains({':', '='}):
# no inline element --> treat as block:
inc(p.indent, 2)
else:
llStreamWrite(p.outp, repeatChar(p.indent))
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, substr(p.x, d))
p.state = psDirective
else:
@ -120,11 +120,11 @@ proc parseLine(p: var TTmplParser) =
# next line of string literal:
llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, "\n")
llStreamWrite(p.outp, repeatChar(p.indent + 2))
llStreamWrite(p.outp, spaces(p.indent + 2))
llStreamWrite(p.outp, "\"")
of psDirective:
newLine(p)
llStreamWrite(p.outp, repeatChar(p.indent))
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, p.emit)
llStreamWrite(p.outp, "(\"")
inc(p.emitPar)

View file

@ -27,7 +27,7 @@ proc getModuleName*(n: PNode): string =
result = n.ident.s
of nkSym:
result = n.sym.name.s
of nkInfix:
of nkInfix, nkPrefix:
if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id:
# XXX hack ahead:
n.kind = nkImportAs

View file

@ -780,7 +780,7 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) =
moveInto(p, stmt, r)
appf(p.body, "}$n" | "end$n")
if p.target == targetJS:
app(p.body, repeatChar(toClose, '}') & tnl)
app(p.body, repeat('}', toClose) & tnl)
else:
for i in 1..toClose: appf(p.body, "end$n")

View file

@ -35,7 +35,7 @@ proc llStreamOpen*(data: string): PLLStream =
result.s = data
result.kind = llsString
proc llStreamOpen*(f: var File): PLLStream =
proc llStreamOpen*(f: File): PLLStream =
new(result)
result.f = f
result.kind = llsFile

View file

@ -176,7 +176,7 @@ proc addInterfaceDeclAux(c: PContext, sym: PSym) =
if sfExported in sym.flags:
# add to interface:
if c.module != nil: strTableAdd(c.module.tab, sym)
else: internalError(sym.info, "AddInterfaceDeclAux")
else: internalError(sym.info, "addInterfaceDeclAux")
proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
addDeclAt(scope, sym)

View file

@ -117,7 +117,8 @@ type
warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
warnEachIdentIsTuple, warnShadowIdent,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnUser,
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
warnUser,
hintSuccess, hintSuccessX,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
@ -391,6 +392,7 @@ const
warnGcMem: "'$1' uses GC'ed memory [GcMem]",
warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future. [Destructor]",
warnLockLevel: "$1 [LockLevel]",
warnResultShadowed: "Special variable 'result' is shadowed. [ResultShadowed]",
warnUser: "$1 [User]",
hintSuccess: "operation successful [Success]",
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]",
@ -411,7 +413,7 @@ const
hintUser: "$1 [User]"]
const
WarningsToStr*: array[0..29, string] = ["CannotOpenFile", "OctalEscape",
WarningsToStr*: array[0..30, string] = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit",
"Deprecated", "ConfigDeprecated",
"SmallLshouldNotBeUsed", "UnknownMagic",
@ -421,7 +423,7 @@ const
"TypelessParam", "DifferentHeaps", "WriteToForeignHeap",
"UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "Destructor", "LockLevel", "User"]
"GcMem", "Destructor", "LockLevel", "ResultShadowed", "User"]
HintsToStr*: array[0..16, string] = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
@ -784,7 +786,7 @@ proc rawMessage*(msg: TMsgKind, arg: string) =
proc writeSurroundingSrc(info: TLineInfo) =
const indent = " "
msgWriteln(indent & info.sourceLine.ropeToStr)
msgWriteln(indent & repeatChar(info.col, ' ') & '^')
msgWriteln(indent & spaces(info.col) & '^')
proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string =
let frmt = case msg

View file

@ -18,3 +18,4 @@ define:useStdoutAsStdmsg
cs:partial
#define:useNodeIds
symbol:nimfix
#gc:markAndSweep

View file

@ -166,4 +166,4 @@ proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
inc(i)
result.add("\n")
if marker:
result.add(repeatChar(getColNumber(L, L.bufpos)) & '^' & "\n")
result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n")

View file

@ -865,6 +865,7 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
#| [' optInd (identColonEquals (comma/semicolon)?)* optPar ']'
#| extTupleDecl = 'tuple'
#| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?
#| tupleClass = 'tuple'
result = newNodeP(nkTupleTy, p)
getTok(p)
if p.tok.tokType == tkBracketLe:
@ -894,6 +895,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
parMessage(p, errIdentifierExpected, p.tok)
break
if not sameInd(p): break
else:
result = newNodeP(nkTupleClassTy, p)
proc parseParamList(p: var TParser, retColon = true): PNode =
#| paramList = '(' declColonEquals ^* (comma/semicolon) ')'

View file

@ -172,7 +172,7 @@ proc processModule(module: PSym, stream: PLLStream, rd: PRodReader) =
let filename = fileIdx.toFullPathConsiderDirty
if module.name.s == "-":
module.name.s = "stdinfile"
s = llStreamOpenStdIn()
s = llStreamOpen(stdin)
else:
s = llStreamOpen(filename, fmRead)
if s == nil:

View file

@ -92,7 +92,7 @@ proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
proc addPendingNL(g: var TSrcGen) =
if g.pendingNL >= 0:
addTok(g, tkSpaces, "\n" & repeatChar(g.pendingNL))
addTok(g, tkSpaces, "\n" & spaces(g.pendingNL))
g.lineLen = g.pendingNL
g.pendingNL = - 1
@ -190,7 +190,7 @@ proc putComment(g: var TSrcGen, s: string) =
if not isCode and (g.lineLen + (j - i) > MaxLineLen):
put(g, tkComment, com)
optNL(g, ind)
com = '#' & repeatChar(comIndent)
com = '#' & spaces(comIndent)
while s[i] > ' ':
add(com, s[i])
inc(i)
@ -280,7 +280,7 @@ proc gcom(g: var TSrcGen, n: PNode) =
(g.lineLen < LineCommentColumn):
var ml = maxLineLength(n.comment)
if ml + LineCommentColumn <= MaxLineLen:
put(g, tkSpaces, repeatChar(LineCommentColumn - g.lineLen))
put(g, tkSpaces, spaces(LineCommentColumn - g.lineLen))
putComment(g, n.comment) #assert(g.comStack[high(g.comStack)] = n);
proc gcoms(g: var TSrcGen) =
@ -395,6 +395,7 @@ proc lsub(n: PNode): int =
of nkClosedSymChoice, nkOpenSymChoice:
result = lsons(n) + len("()") + sonsLen(n) - 1
of nkTupleTy: result = lcomma(n) + len("tuple[]")
of nkTupleClassTy: result = len("tuple")
of nkDotExpr: result = lsons(n) + 1
of nkBind: result = lsons(n) + len("bind_")
of nkBindStmt: result = lcomma(n) + len("bind_")
@ -1292,10 +1293,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gsub(g, n.sons[0])
of nkTupleTy:
put(g, tkTuple, "tuple")
if sonsLen(n) > 0:
put(g, tkBracketLe, "[")
gcomma(g, n)
put(g, tkBracketRi, "]")
put(g, tkBracketLe, "[")
gcomma(g, n)
put(g, tkBracketRi, "]")
of nkTupleClassTy:
put(g, tkTuple, "tuple")
of nkMetaNode_Obsolete:
put(g, tkParLe, "(META|")
gsub(g, n.sons[0])

View file

@ -41,48 +41,55 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
best, alt: var TCandidate,
errors: var CandidateErrors) =
var o: TOverloadIter
var sym = initOverloadIter(o, c, headSymbol)
var symScope = o.lastOverloadScope
# thanks to the lazy semchecking for operands, we need to iterate over the
# symbol table *before* any call to 'initCandidate' which might invoke
# semExpr which might modify the symbol table in cases like
# 'init(a, 1, (var b = new(Type2); b))'.
var symx = initOverloadIter(o, c, headSymbol)
let symScope = o.lastOverloadScope
var syms: seq[tuple[a: PSym, b: int]] = @[]
while symx != nil:
if symx.kind in filter: syms.add((symx, o.lastOverloadScope))
symx = nextOverloadIter(o, c, headSymbol)
if syms.len == 0: return
var z: TCandidate
if sym == nil: return
initCandidate(c, best, sym, initialBinding, symScope)
initCandidate(c, alt, sym, initialBinding, symScope)
initCandidate(c, best, syms[0][0], initialBinding, symScope)
initCandidate(c, alt, syms[0][0], initialBinding, symScope)
best.state = csNoMatch
while sym != nil:
if sym.kind in filter:
determineType(c, sym)
initCandidate(c, z, sym, initialBinding, o.lastOverloadScope)
z.calleeSym = sym
for i in 0 .. <syms.len:
let sym = syms[i][0]
determineType(c, sym)
initCandidate(c, z, sym, initialBinding, syms[i][1])
z.calleeSym = sym
#if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140:
# gDebug = true
matches(c, n, orig, z)
if errors != nil:
errors.safeAdd(sym)
if z.errors != nil:
for err in z.errors:
errors.add(err)
if z.state == csMatch:
# little hack so that iterators are preferred over everything else:
if sym.kind in skIterators: inc(z.exactMatches, 200)
case best.state
of csEmpty, csNoMatch: best = z
of csMatch:
var cmp = cmpCandidates(best, z)
if cmp < 0: best = z # x is better than the best so far
elif cmp == 0: alt = z # x is as good as the best so far
else: discard
#if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140:
# echo "Matches ", n.info, " ", typeToString(sym.typ)
# debug sym
# writeMatches(z)
# for i in 1 .. <len(z.call):
# z.call[i].typ.debug
# quit 1
sym = nextOverloadIter(o, c, headSymbol)
#if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140:
# gDebug = true
matches(c, n, orig, z)
if errors != nil:
errors.safeAdd(sym)
if z.errors != nil:
for err in z.errors:
errors.add(err)
if z.state == csMatch:
# little hack so that iterators are preferred over everything else:
if sym.kind in skIterators: inc(z.exactMatches, 200)
case best.state
of csEmpty, csNoMatch: best = z
of csMatch:
var cmp = cmpCandidates(best, z)
if cmp < 0: best = z # x is better than the best so far
elif cmp == 0: alt = z # x is as good as the best so far
else: discard
#if sym.name.s == "cmp" and (n.info ?? "rstgen.nim") and n.info.line == 516:
# echo "Matches ", n.info, " ", typeToString(sym.typ)
# debug sym
# writeMatches(z)
# for i in 1 .. <len(z.call):
# z.call[i].typ.debug
# quit 1
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# Gives a detailed error message; this is separated from semOverloadedCall,

View file

@ -1866,6 +1866,14 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
addSonSkipIntLit(typ, n.sons[i].typ)
result.typ = typ
proc isTupleType(n: PNode): bool =
if n.len == 0:
return false # don't interpret () as type
for i in countup(0, n.len - 1):
if n[i].typ == nil or n[i].typ.kind != tyTypeDesc:
return false
return true
proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) =
case n.kind
of nkRecList:
@ -2055,7 +2063,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkBind:
message(n.info, warnDeprecated, "bind")
result = semExpr(c, n.sons[0], flags)
of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy, nkStaticTy:
of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
result.typ = makeTypeDesc(c, typ)
#result = symNodeFromType(c, typ, n.info)
@ -2129,7 +2137,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkPar:
case checkPar(n)
of paNone: result = errorNode(c, n)
of paTuplePositions: result = semTuplePositionsConstr(c, n, flags)
of paTuplePositions:
var tupexp = semTuplePositionsConstr(c, n, flags)
if isTupleType(tupexp):
# reinterpret as type
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
result.typ = makeTypeDesc(c, typ)
else:
result = tupexp
of paTupleFields: result = semTupleFieldsConstr(c, n, flags)
of paSingle: result = semExpr(c, n.sons[0], flags)
of nkCurly: result = semSetConstr(c, n)

View file

@ -356,7 +356,7 @@ proc semGenericStmt(c: PContext, n: PNode,
of nkIdent: a = n.sons[i]
else: illFormedAst(n)
addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c))
of nkObjectTy, nkTupleTy:
of nkObjectTy, nkTupleTy, nkTupleClassTy:
discard
of nkFormalParams:
checkMinSonsLen(n, 1)

View file

@ -138,7 +138,7 @@ proc guardDotAccess(a: PEffects; n: PNode) =
if g.kind == skUnknown:
var field: PSym = nil
var ty = n.sons[0].typ.skipTypes(abstractPtrs)
if ty.kind == tyTuple:
if ty.kind == tyTuple and not ty.n.isNil:
field = lookupInRecord(ty.n, g.name)
else:
while ty != nil and ty.kind == tyObject:

View file

@ -400,6 +400,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
elif tup.kind == tyTuple and def.kind == nkPar and
a.kind == nkIdentDefs and a.len > 3:
message(a.info, warnEachIdentIsTuple)
for j in countup(0, length-3):
var v = semIdentDef(c, a.sons[j], symkind)
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
@ -409,6 +410,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
let shadowed = findShadowedVar(c, v)
if shadowed != nil:
shadowed.flags.incl(sfShadowed)
if shadowed.kind == skResult:
message(a.info, warnResultShadowed)
# a shadowed variable is an error unless it appears on the right
# side of the '=':
if warnShadowIdent in gNotes and not identWithin(def, v.name):
@ -692,7 +695,7 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
assignType(s.typ, t)
s.typ.id = t.id # same id
checkConstructedType(s.info, s.typ)
if s.typ.kind in {tyObject, tyTuple}:
if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
checkForMetaFields(s.typ.n)
let aa = a.sons[2]
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
@ -786,7 +789,8 @@ proc semProcAnnotation(c: PContext, prc: PNode;
result = semStmt(c, x)
# since a proc annotation can set pragmas, we process these here again.
# This is required for SqueakNim-like export pragmas.
if result[namePos].kind == nkSym and result[pragmasPos].kind != nkEmpty:
if result.kind in procDefs and result[namePos].kind == nkSym and
result[pragmasPos].kind != nkEmpty:
pragma(c, result[namePos].sym, result[pragmasPos], validPragmas)
return

View file

@ -345,8 +345,14 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
localError(n.info, errIdentifierExpected)
result = errorSym(c, n)
proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType =
if sonsLen(n) == 0:
localError(n.info, errTypeExpected)
result = newOrPrevType(tyTuple, prev, c)
for i in countup(0, sonsLen(n) - 1):
addSonSkipIntLit(result, semTypeNode(c, n.sons[i], nil))
proc semTuple(c: PContext, n: PNode, prev: PType): PType =
if n.sonsLen == 0: return newConstraint(c, tyTuple)
var typ: PType
result = newOrPrevType(tyTuple, prev, c)
result.n = newNodeI(nkRecList, n.info)
@ -1117,9 +1123,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkPar:
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
else:
# XXX support anon tuple here
localError(n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c)
result = semAnonTuple(c, n, prev)
of nkCallKinds:
if isRange(n):
result = semRangeAux(c, n, prev)
@ -1227,6 +1231,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyError, prev, c)
of nkObjectTy: result = semObjectNode(c, n, prev)
of nkTupleTy: result = semTuple(c, n, prev)
of nkTupleClassTy: result = newConstraint(c, tyTuple)
of nkTypeClassTy: result = semTypeClass(c, n, prev)
of nkRefTy: result = semAnyRef(c, n, tyRef, prev)
of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)

View file

@ -341,6 +341,8 @@ proc skipIntLiteralParams*(t: PType) =
proc propagateFieldFlags(t: PType, n: PNode) =
# This is meant for objects and tuples
# The type must be fully instantiated!
if n.isNil:
return
internalAssert n.kind != nkRecWhen
case n.kind
of nkSym:

View file

@ -157,17 +157,40 @@ proc sumGeneric(t: PType): int =
result = ord(t.kind == tyGenericInvocation)
for i in 0 .. <t.len: result += t.sons[i].sumGeneric
break
of tyProc:
# proc matches proc better than 'stmt' to disambiguate 'spawn'
return 1
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer,
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64:
return 1
else: return 0
#var ggDebug: bool
proc complexDisambiguation(a, b: PType): int =
var x, y: int
for i in 1 .. <a.len: x += a.sons[i].sumGeneric
for i in 1 .. <b.len: y += b.sons[i].sumGeneric
result = x - y
# 'a' matches better if *every* argument matches better or equal than 'b'.
var winner = 0
for i in 1 .. <min(a.len, b.len):
let x = a.sons[i].sumGeneric
let y = b.sons[i].sumGeneric
#if ggDebug:
# echo "came her ", typeToString(a.sons[i]), " ", typeToString(b.sons[i])
if x != y:
if winner == 0:
if x > y: winner = 1
else: winner = -1
elif x > y:
if winner != 1:
# contradiction
return 0
else:
if winner != -1:
return 0
result = winner
when false:
var x, y: int
for i in 1 .. <a.len: x += a.sons[i].sumGeneric
for i in 1 .. <b.len: y += b.sons[i].sumGeneric
result = x - y
when false:
proc betterThan(a, b: PType): bool {.inline.} = a.sumGeneric > b.sumGeneric
@ -338,7 +361,8 @@ proc minRel(a, b: TTypeRelation): TTypeRelation =
proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
result = isNone
if sameType(f, a): result = isEqual
if sameType(f, a):
result = isEqual
elif sonsLen(a) == sonsLen(f):
result = isEqual
let firstField = if f.kind == tyTuple: 0
@ -380,17 +404,17 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
# no luck resolving the type, so the inference fails
return isNone
let reverseRel = typeRel(c, a, f)
if reverseRel == isGeneric:
if reverseRel >= isGeneric:
result = isInferred
inc c.genericMatches
#inc c.genericMatches
else:
result = typeRel(c, f, a)
if result <= isSubtype or inconsistentVarTypes(f, a):
result = isNone
if result == isEqual:
inc c.exactMatches
#if result == isEqual:
# inc c.exactMatches
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
case a.kind
@ -433,6 +457,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
return isNone
when useEffectSystem:
if not compatibleEffects(f, a): return isNone
of tyNil:
result = f.allowsNil
of tyIter:
@ -590,10 +615,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
return typeRel(c, f, lastSon(a))
template bindingRet(res) =
when res == isGeneric:
if doBind:
let bound = aOrig.skipTypes({tyRange}).skipIntLit
if doBind: put(c.bindings, f, bound)
if doBind:
let bound = aOrig.skipTypes({tyRange}).skipIntLit
if doBind: put(c.bindings, f, bound)
return res
template considerPreviousT(body: stmt) {.immediate.} =
@ -605,20 +629,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyOr:
# seq[int|string] vs seq[number]
# both int and string must match against number
# but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219):
result = isGeneric
for branch in a.sons:
if typeRel(c, f, branch, false) == isNone:
return isNone
return isGeneric
let x = typeRel(c, f, branch, false)
if x == isNone: return isNone
if x < result: result = x
of tyAnd:
# seq[Sortable and Iterable] vs seq[Sortable]
# only one match is enough
for branch in a.sons:
if typeRel(c, f, branch, false) != isNone:
return isGeneric
return isNone
let x = typeRel(c, f, branch, false)
if x != isNone:
return if x >= isGeneric: isGeneric else: x
result = isNone
of tyNot:
case f.kind
@ -781,11 +806,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
inc(c.inheritancePenalty, depth)
result = isSubtype
of tyDistinct:
if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual
if a.kind == tyDistinct and sameDistinctTypes(f, a): result = isEqual
elif c.coerceDistincts: result = typeRel(c, f.base, a)
of tySet:
if a.kind == tySet:
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
if f.sons[0].kind != tyGenericParam and a.sons[0].kind == tyEmpty:
result = isSubtype
else:
result = typeRel(c, f.sons[0], a.sons[0])
@ -865,7 +890,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = typeRel(c, ff, aa)
if result == isNone: return
if ff.kind == tyRange and result != isEqual: return isNone
result = isGeneric
#result = isGeneric
# XXX See bug #2220. A[int] should match A[int] better than some generic X
else:
result = typeRel(c, lastSon(f), a)
@ -904,18 +929,23 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyAnd:
considerPreviousT:
for branch in f.sons:
if typeRel(c, branch, aOrig) < isSubtype:
return isNone
bindingRet isGeneric
let x = typeRel(c, branch, aOrig)
if x < isSubtype: return isNone
# 'and' implies minimum matching result:
if x < result: result = x
bindingRet result
of tyOr:
considerPreviousT:
result = isNone
for branch in f.sons:
if typeRel(c, branch, aOrig) >= isSubtype:
bindingRet isGeneric
return isNone
let x = typeRel(c, branch, aOrig)
# 'or' implies maximum matching result:
if x > result: result = x
if result >= isSubtype:
bindingRet result
else:
result = isNone
of tyNot:
considerPreviousT:
@ -975,6 +1005,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
internalAssert a.sons != nil and a.sons.len > 0
c.typedescMatched = true
result = typeRel(c, f.base, a.skipTypes({tyGenericParam, tyTypeDesc}))
if result > isGeneric: result = isGeneric
else:
result = isNone
else:
@ -998,12 +1029,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
return isNone
if doBind:
put(c.bindings, f, concrete)
elif result > isGeneric:
result = isGeneric
elif a.kind == tyEmpty:
result = isGeneric
elif x.kind == tyGenericParam:
result = isGeneric
else:
result = typeRel(c, x, a) # check if it fits
if result > isGeneric: result = isGeneric
of tyStatic:
let prev = PType(idTableGet(c.bindings, f))
@ -1220,8 +1254,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
of isConvertible, isIntConv: inc(m.convMatches)
of isSubtype, isSubrange: inc(m.subtypeMatches)
of isGeneric, isInferred: inc(m.genericMatches)
of isInferredConvertible: inc(m.genericMatches); inc(m.convMatches)
of isFromIntLit: inc(m.intConvMatches, 256)
of isInferredConvertible:
inc(m.convMatches)
of isEqual: inc(m.exactMatches)
of isNone: discard
@ -1232,7 +1267,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
elif f.kind == tyStatic:
return arg.typ.n
else:
return argOrig
return argSemantized # argOrig
if r != isNone and f.isInlineIterator:
var inlined = newTypeS(tyStatic, c)
@ -1244,21 +1279,22 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
case r
of isConvertible:
inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isIntConv:
# I'm too lazy to introduce another ``*matches`` field, so we conflate
# ``isIntConv`` and ``isIntLit`` here:
inc(m.intConvMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isSubtype:
inc(m.subtypeMatches)
result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c)
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
of isSubrange:
inc(m.subtypeMatches)
#result = copyTree(arg)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
if f.kind == tyVar:
result = arg
else:
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isInferred, isInferredConvertible:
inc(m.genericMatches)
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
result = c.semInferredLambda(c, m.bindings, arg)
else:
@ -1267,41 +1303,35 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
if r == isInferredConvertible:
inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, result, m, c)
else:
inc(m.genericMatches)
of isGeneric:
inc(m.genericMatches)
when true:
if skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple:
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
elif arg.typ != nil and arg.typ.isEmptyContainer:
result = arg.copyTree
result.typ = getInstantiatedType(c, arg, m, f)
else:
result = arg
else:
# XXX Why is this ever necessary? arg's type should not be retrofitted
# to match formal's type in this way!
result = copyTree(arg)
if arg.typ == nil:
result = arg
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple:
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
elif arg.typ.isEmptyContainer:
result = arg.copyTree
result.typ = getInstantiatedType(c, arg, m, f)
# BUG: f may not be the right key!
if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
# BUGFIX: use ``result.typ`` and not `f` here
else:
result = arg
of isFromIntLit:
# too lazy to introduce another ``*matches`` field, so we conflate
# ``isIntConv`` and ``isIntLit`` here:
inc(m.intConvMatches, 256)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isEqual:
inc(m.exactMatches)
result = copyTree(arg)
result = arg
if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
result = implicitConv(nkHiddenStdConv, f, arg, m, c)
of isNone:
# do not do this in ``typeRel`` as it then can't infere T in ``ref T``:
if a.kind in {tyProxy, tyUnknown}:
inc(m.genericMatches)
m.fauxMatch = a.kind
return copyTree(arg)
return arg
result = userConvMatch(c, m, f, a, arg)
# check for a base type match, which supports varargs[T] without []
# constructor in a call:
@ -1340,7 +1370,16 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
for i in countup(0, sonsLen(arg) - 1):
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators:
copyCandidate(z, m)
z.callee = arg.sons[i].typ
z.calleeSym = arg.sons[i].sym
#if arg.sons[i].sym.name.s == "cmp":
# ggDebug = true
# echo "CALLLEEEEEEEE ", typeToString(z.callee)
var r = typeRel(z, f, arg.sons[i].typ)
#if arg.sons[i].sym.name.s == "cmp": # and arg.info.line == 606:
# echo "M ", r, " ", arg.info, " ", typeToString(arg.sons[i].sym.typ)
# debug arg.sons[i].sym
# writeMatches(z)
if r != isNone:
case x.state
of csEmpty, csNoMatch:
@ -1653,7 +1692,7 @@ tests:
setup:
var c: TCandidate
InitCandidate(nil, c, nil)
initCandidate(nil, c, nil)
template yes(x, y) =
test astToStr(x) & " is " & astToStr(y):

View file

@ -507,18 +507,22 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
if prefer == preferModuleInfo: preferModuleInfo else: preferName)
of tyTuple:
# we iterate over t.sons here, because t.n may be nil
result = "tuple["
if t.n != nil:
result = "tuple["
assert(sonsLen(t.n) == sonsLen(t))
for i in countup(0, sonsLen(t.n) - 1):
assert(t.n.sons[i].kind == nkSym)
add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i]))
if i < sonsLen(t.n) - 1: add(result, ", ")
add(result, ']')
elif sonsLen(t) == 0:
result = "tuple[]"
else:
result = "("
for i in countup(0, sonsLen(t) - 1):
add(result, typeToString(t.sons[i]))
if i < sonsLen(t) - 1: add(result, ", ")
add(result, ']')
add(result, ')')
of tyPtr, tyRef, tyVar, tyMutable, tyConst:
result = typeToStr[t.kind]
if t.len >= 2:
@ -654,7 +658,7 @@ type
dcEqOrDistinctOf ## a equals b or a is distinct of b
TTypeCmpFlag* = enum
IgnoreTupleFields
IgnoreTupleFields ## NOTE: Only set this flag for backends!
IgnoreCC
ExactTypeDescValues
ExactGenericParams
@ -774,8 +778,8 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool =
var x = a.sons[i]
var y = b.sons[i]
if IgnoreTupleFields in c.flags:
x = skipTypes(x, {tyRange})
y = skipTypes(y, {tyRange})
x = skipTypes(x, {tyRange, tyGenericInst})
y = skipTypes(y, {tyRange, tyGenericInst})
result = sameTypeAux(x, y, c)
if not result: return
@ -823,8 +827,13 @@ proc sameEnumTypes*(a, b: PType): bool {.inline.} =
proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool =
if a == b:
result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind):
if sameTypeOrNilAux(a.typ, b.typ, c):
elif a != nil and b != nil and a.kind == b.kind:
var x = a.typ
var y = b.typ
if IgnoreTupleFields in c.flags:
if x != nil: x = skipTypes(x, {tyRange, tyGenericInst})
if y != nil: y = skipTypes(y, {tyRange, tyGenericInst})
if sameTypeOrNilAux(x, y, c):
case a.kind
of nkSym:
# same symbol as string is enough:

View file

@ -68,7 +68,6 @@ proc renderType(n: PNode): string =
assert n[i].kind == nkIdent
result.add(',' & typeStr)
of nkTupleTy:
assert len(n) > 0
result = "tuple["
for i in 0 .. <len(n): result.add(renderType(n[i]) & ',')
result[<len(result)] = ']'

View file

@ -1435,6 +1435,7 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode =
newSeq(tos.slots, c.prc.maxSlots)
#for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty)
result = rawExecute(c, start, tos).regToNode
if result.info.line < 0: result.info = n.info
proc evalConstExpr*(module: PSym, e: PNode): PNode =
result = evalConstExprAux(module, nil, e, emConst)
@ -1496,6 +1497,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
# temporary storage:
#for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
result = rawExecute(c, start, tos).regToNode
if result.info.line < 0: result.info = n.info
if cyclicTree(result): globalError(n.info, errCyclicTree)
dec(evalMacroCounter)
c.callsite = nil

View file

@ -151,7 +151,7 @@ clang.options.size = "-Os"
vcc.options.linker = "/DEBUG /Zi /Fd\"$projectName.pdb\" /F33554432" # set the stack size to 8 MB
vcc.options.debug = "/Zi /Fd\"$projectName.pdb\""
vcc.options.always = "/nologo"
vcc.options.speed = "/Ox /arch:SSE2"
vcc.options.speed = "/O2 /arch:SSE2"
vcc.options.size = "/O1"
# Configuration for the Digital Mars C/C++ compiler:

View file

@ -175,6 +175,9 @@ Generic Operating System Services
This module implements the ability to monitor a directory/file for changes
using Posix's inotify API.
* `asyncfile <asyncfile.html>`_
This module implements asynchronous file reading and writing using
``asyncdispatch``.
Math libraries
--------------

View file

@ -351,7 +351,7 @@ dispatch.
.. code-block:: nim
type
Expression = object ## abstract base class for an expression
Expression = object of RootObj ## abstract base class for an expression
Literal = object of Expression
x: int
PlusExpr = object of Expression
@ -387,7 +387,7 @@ dispatching:
.. code-block:: nim
type
Thing = object
Thing = object of RootObj
Unit = object of Thing
x: int

View file

@ -347,7 +347,7 @@ proc temp(args: string) =
if args.len > 0: exec(finalDest & " " & args)
proc showHelp() =
quit(HelpText % [VersionAsString & repeatChar(44-len(VersionAsString)),
quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)),
CompileDate, CompileTime], QuitSuccess)
var op = initOptParser()

View file

@ -60,7 +60,7 @@ type
nnkStmtListType, nnkBlockType,
nnkWith, nnkWithout,
nnkTypeOfExpr, nnkObjectTy,
nnkTupleTy, nnkTypeClassTy, nnkStaticTy,
nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy,
nnkRecList, nnkRecCase, nnkRecWhen,
nnkRefTy, nnkPtrTy, nnkVarTy,
nnkConstTy, nnkMutableTy,
@ -343,7 +343,7 @@ proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
## checks that `n` is of kind `k`. If this is not the case,
## compilation aborts with an error message. This is useful for writing
## macros that check the AST that is passed to them.
if n.kind != k: error("macro expects a node of kind: " & $k)
if n.kind != k: error("Expected a node of kind " & $k & ", got " & $n.kind)
proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} =
## checks that `n` has at least `min` children. If this is not the case,
@ -581,10 +581,8 @@ const
CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
nnkCallStrLit, nnkHiddenCallConv}
from strutils import cmpIgnoreStyle, format
proc expectKind*(n: PNimrodNode; k: set[TNimrodNodeKind]) {.compileTime.} =
assert n.kind in k, "Expected one of $1, got $2".format(k, n.kind)
assert n.kind in k, "Expected one of " & $k & ", got " & $n.kind
proc newProc*(name = newEmptyNode(); params: openArray[PNimrodNode] = [newEmptyNode()];
body: PNimrodNode = newStmtList(), procType = nnkProcDef): PNimrodNode {.compileTime.} =
@ -654,7 +652,7 @@ proc `pragma=`*(someProc: PNimrodNode; val: PNimrodNode){.compileTime.}=
template badNodeKind(k; f): stmt{.immediate.} =
assert false, "Invalid node kind $# for macros.`$2`".format(k, f)
assert false, "Invalid node kind " & $k & " for macros.`" & $f & "`"
proc body*(someProc: PNimrodNode): PNimrodNode {.compileTime.} =
case someProc.kind:
@ -776,6 +774,22 @@ proc copy*(node: PNimrodNode): PNimrodNode {.compileTime.} =
## An alias for copyNimTree().
return node.copyNimTree()
proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} =
proc toLower(c: char): char {.inline.} =
if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A')))
else: result = c
var i = 0
var j = 0
while true:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
var aa = toLower(a[i])
var bb = toLower(b[j])
result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break
inc(i)
inc(j)
proc eqIdent* (a, b: string): bool = cmpIgnoreStyle(a, b) == 0
## Check if two idents are identical.

View file

@ -66,9 +66,9 @@ type
ppointer = ptr pointer
pbyteArray = ptr array[0.. 0xffff, int8]
TGenSeq = object
TGenericSeq {.importc.} = object
len, space: int
PGenSeq = ptr TGenSeq
PGenSeq = ptr TGenericSeq
const
GenericSeqSize = (2 * sizeof(int))

View file

@ -33,27 +33,71 @@ when defined(Windows):
stdout.write(prompt)
result = readLine(stdin, line)
import winlean
const
VK_SHIFT* = 16
VK_CONTROL* = 17
VK_MENU* = 18
KEY_EVENT* = 1
type
KEY_EVENT_RECORD = object
bKeyDown: WinBool
wRepeatCount: uint16
wVirtualKeyCode: uint16
wVirtualScanCode: uint16
unicodeChar: uint16
dwControlKeyState: uint32
INPUT_RECORD = object
eventType*: int16
reserved*: int16
event*: KEY_EVENT_RECORD
safetyBuffer: array[0..5, DWORD]
proc readConsoleInputW*(hConsoleInput: THANDLE, lpBuffer: var INPUTRECORD,
nLength: uint32,
lpNumberOfEventsRead: var uint32): WINBOOL{.
stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".}
proc getch(): uint16 =
let hStdin = getStdHandle(STD_INPUT_HANDLE)
var
irInputRecord: INPUT_RECORD
dwEventsRead: uint32
while readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead) != 0:
if irInputRecord.eventType == KEY_EVENT and
irInputRecord.event.wVirtualKeyCode notin {VK_SHIFT, VK_MENU, VK_CONTROL}:
result = irInputRecord.event.unicodeChar
discard readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead)
return result
from unicode import toUTF8, Rune, runeLenAt
proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
bool {.tags: [ReadIOEffect, WriteIOEffect].} =
## Reads a `password` from stdin without printing it. `password` must not
## be ``nil``! Returns ``false`` if the end of the file has been reached,
## ``true`` otherwise.
proc getch(): cint {.header: "<conio.h>", importc: "_getch".}
password.setLen(0)
var c: char
stdout.write(prompt)
while true:
c = getch().char
case c
let c = getch()
case c.char
of '\r', chr(0xA):
break
of '\b':
password.setLen(password.len - 1)
# ensure we delete the whole UTF-8 character:
var i = 0
var x = 1
while i < password.len:
x = runeLenAt(password, i)
inc i, x
password.setLen(password.len - x)
else:
password.add(c)
password.add(toUTF8(c.Rune))
stdout.write "\n"
# TODO: How to detect EOF on Windows?
else:
import readline, history, termios, unsigned

View file

@ -66,7 +66,7 @@ proc rawCompile(pattern: string, flags: cint): PPcre =
offset: cint
result = pcre.compile(pattern, flags, addr(msg), addr(offset), nil)
if result == nil:
raiseInvalidRegex($msg & "\n" & pattern & "\n" & repeatChar(offset) & "^\n")
raiseInvalidRegex($msg & "\n" & pattern & "\n" & spaces(offset) & "^\n")
proc finalizeRegEx(x: Regex) =
# XXX This is a hack, but PCRE does not export its "free" function properly.
@ -291,7 +291,7 @@ proc replace*(s: string, sub: Regex, by = ""): string =
## accessed in `by`. Examples:
##
## .. code-block:: nim
## "var1=key; var2=key2".replace(re"(\w+)'='(\w+)")
## "var1=key; var2=key2".replace(re"(\w+)=(\w+)")
##
## Results in:
##
@ -313,7 +313,7 @@ proc replacef*(s: string, sub: Regex, by: string): string =
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
##
## .. code-block:: nim
## "var1=key; var2=key2".replacef(re"(\w+)'='(\w+)", "$1<-$2$2")
## "var1=key; var2=key2".replacef(re"(\w+)=(\w+)", "$1<-$2$2")
##
## Results in:
##

View file

@ -103,7 +103,11 @@ __clang__
# define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name)
# define N_SAFECALL_PTR(rettype, name) rettype (__safecall *name)
# define N_LIB_EXPORT extern __declspec(dllexport)
# ifdef __cplusplus
# define N_LIB_EXPORT extern "C" __declspec(dllexport)
# else
# define N_LIB_EXPORT extern __declspec(dllexport)
# endif
# define N_LIB_IMPORT extern __declspec(dllimport)
#else
# define N_CDECL(rettype, name) rettype name
@ -118,7 +122,11 @@ __clang__
# define N_FASTCALL_PTR(rettype, name) rettype (*name)
# define N_SAFECALL_PTR(rettype, name) rettype (*name)
# define N_LIB_EXPORT extern
# ifdef __cplusplus
# define N_LIB_EXPORT extern "C"
# else
# define N_LIB_EXPORT extern
# endif
# define N_LIB_IMPORT extern
#endif
@ -398,4 +406,6 @@ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(
# include <sys/types.h>
# include <types/vxWind.h>
# include <tool/gnu/toolMacros.h>
#elif defined(__FreeBSD__)
# include <sys/types.h>
#endif

View file

@ -189,7 +189,7 @@ proc getIndent(L: var TLexer, tok: var TToken) =
tok.line = L.line
L.col = tok.ival
tok.ival = max(tok.ival - L.baseIndent, 0)
tok.symbol = "\n" & repeatChar(tok.ival)
tok.symbol = "\n" & spaces(tok.ival)
proc rawGetTok(L: var TLexer, tok: var TToken) =
tok.symbol = ""
@ -963,7 +963,7 @@ proc parseLiteralBlock(p: var TRstParser): PRstNode =
break
else:
add(n.text, "\n")
add(n.text, repeatChar(p.tok[p.idx].ival - indent))
add(n.text, spaces(p.tok[p.idx].ival - indent))
inc(p.idx)
else:
add(n.text, p.tok[p.idx].symbol)

View file

@ -110,7 +110,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
const
lvlToChar: array[0..8, char] = ['!', '=', '-', '~', '`', '<', '*', '|', '+']
if n == nil: return
var ind = repeatChar(d.indent)
var ind = spaces(d.indent)
case n.kind
of rnInner:
renderRstSons(d, n, result)
@ -124,7 +124,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
result.add("\n")
result.add(ind)
result.add repeatChar(headlineLen, lvlToChar[n.level])
result.add repeat(lvlToChar[n.level], headlineLen)
of rnOverline:
result.add("\n")
result.add(ind)
@ -132,7 +132,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
var headline = ""
renderRstSons(d, n, headline)
let lvl = repeatChar(headline.len - d.indent, lvlToChar[n.level])
let lvl = repeat(lvlToChar[n.level], headline.len - d.indent)
result.add(lvl)
result.add("\n")
result.add(headline)
@ -143,7 +143,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
of rnTransition:
result.add("\n\n")
result.add(ind)
result.add repeatChar(78-d.indent, '-')
result.add repeat('-', 78-d.indent)
result.add("\n\n")
of rnParagraph:
result.add("\n\n")
@ -196,7 +196,7 @@ proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
result.add ':'
result.add tmp
result.add ':'
result.add repeatChar(L - tmp.len - 2)
result.add spaces(L - tmp.len - 2)
renderRstToRst(d, n.sons[1], result)
dec(d.indent, L)

View file

@ -461,9 +461,9 @@ proc indentToLevel(level: var int, newLevel: int): string =
if level == newLevel:
return
if newLevel > level:
result = repeatStr(newLevel - level, "<ul>")
result = repeat("<ul>", newLevel - level)
else:
result = repeatStr(level - newLevel, "</ul>")
result = repeat("</ul>", level - newLevel)
level = newLevel
proc generateDocumentationTOC(entries: seq[TIndexEntry]): string =
@ -701,7 +701,7 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
# Generate index entry using spaces to indicate TOC level for the output HTML.
assert n.level >= 0
setIndexTerm(d, refname, tmp.stripTOCHTML,
repeatChar(max(0, n.level), ' ') & tmp)
spaces(max(0, n.level)) & tmp)
proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
if d.meta[metaTitle].len == 0:

View file

@ -75,11 +75,12 @@ const
proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int =
## same as binarySearch except that if key is not in `a` then this
## returns the location where `key` would be if it were. In other
## words if you have a sorted sequence and you call insert(thing, elm, lowerBound(thing, elm))
## the sequence will still be sorted
## words if you have a sorted sequence and you call
## insert(thing, elm, lowerBound(thing, elm))
## the sequence will still be sorted.
##
## `cmp` is the comparator function to use, the expected return values are the same as
## that of system.cmp
## `cmp` is the comparator function to use, the expected return values are
## the same as that of system.cmp.
##
## example::
##
@ -187,6 +188,35 @@ proc sort*[T](a: var openArray[T],
dec(m, s*2)
s = s*2
proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.},
order = SortOrder.Ascending): seq[T] =
## returns `a` sorted by `cmp` in the specified `order`.
result = newSeq[T](a.len)
for i in 0 .. a.high:
result[i] = a[i]
sort(result, cmp, order)
template sortByIt*(seq1, op: expr): expr =
## Convenience template around the ``sorted`` proc to reduce typing.
##
## The template injects the ``it`` variable which you can use directly in an
## expression. Example:
##
## .. code-block:: nim
##
## var users: seq[tuple[id: int, name: string]] =
## @[(0, "Smith"), (1, "Pratt"), (2, "Sparrow")]
##
## echo users.sortByIt(it.name)
##
var result {.gensym.} = sorted(seq1, proc(x, y: type(seq1[0])): int =
var it {.inject.} = x
let a = op
it = y
let b = op
result = cmp(a, b))
result
proc product*[T](x: openArray[seq[T]]): seq[seq[T]] =
## produces the Cartesian product of the array. Warning: complexity
## may explode.

View file

@ -1222,7 +1222,7 @@ proc processBody(node, retFutureSym: PNimrodNode,
of nnkTryStmt:
# try: await x; except: ...
result = newNimNode(nnkStmtList, node)
template wrapInTry(n, tryBody: PNimrodNode) =
template wrapInTry(n, tryBody: expr) =
var temp = n
n[0] = tryBody
tryBody = temp
@ -1323,14 +1323,25 @@ macro async*(prc: stmt): stmt {.immediate.} =
newLit(prc[0].getName)))) # Get type from return type of this proc
# -> iterator nameIter(): FutureBase {.closure.} =
# -> {.push warning[resultshadowed]: off.}
# -> var result: T
# -> {.pop.}
# -> <proc_body>
# -> complete(retFuture, result)
var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter")
var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, nil)
if not subtypeIsVoid:
procBody.insert(0, newNimNode(nnkVarSection, prc[6]).add(
procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"),
newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
newIdentNode("warning"), newIdentNode("resultshadowed")),
newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.}
procBody.insert(1, newNimNode(nnkVarSection, prc[6]).add(
newIdentDefs(newIdentNode("result"), returnType[1]))) # -> var result: T
procBody.insert(2, newNimNode(nnkPragma).add(
newIdentNode("pop"))) # -> {.pop.})
procBody.add(
newCall(newIdentNode("complete"),
retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
## This module implements asynchronous file handling.
## This module implements asynchronous file reading and writing.
##
## .. code-block:: Nim
## import asyncfile, asyncdispatch, os
@ -24,17 +24,17 @@
import asyncdispatch, os
when defined(windows):
when defined(windows) or defined(nimdoc):
import winlean
else:
import posix
type
AsyncFile = ref object
AsyncFile* = ref object
fd: TAsyncFd
offset: int64
when defined(windows):
when defined(windows) or defined(nimdoc):
proc getDesiredAccess(mode: FileMode): int32 =
case mode
of fmRead:
@ -70,7 +70,7 @@ else:
proc getFileSize(f: AsyncFile): int64 =
## Retrieves the specified file's size.
when defined(windows):
when defined(windows) or defined(nimdoc):
var high: DWord
let low = getFileSize(f.fd.THandle, addr high)
if low == INVALID_FILE_SIZE:
@ -81,7 +81,7 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
## Opens a file specified by the path in ``filename`` using
## the specified ``mode`` asynchronously.
new result
when defined(windows):
when defined(windows) or defined(nimdoc):
let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL
let desiredAccess = getDesiredAccess(mode)
let creationDisposition = getCreationDisposition(mode, filename)
@ -120,7 +120,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
## returned.
var retFuture = newFuture[string]("asyncfile.read")
when defined(windows):
when defined(windows) or defined(nimdoc):
var buffer = alloc0(size)
var ol = PCustomOverlapped()
@ -224,7 +224,7 @@ proc setFilePos*(f: AsyncFile, pos: int64) =
## Sets the position of the file pointer that is used for read/write
## operations. The file's first byte has the index zero.
f.offset = pos
when not defined(windows):
when not defined(windows) and not defined(nimdoc):
let ret = lseek(f.fd.cint, pos, SEEK_SET)
if ret == -1:
raiseOSError(osLastError())
@ -245,7 +245,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
## specified file.
var retFuture = newFuture[void]("asyncfile.write")
var copy = data
when defined(windows):
when defined(windows) or defined(nimdoc):
var buffer = alloc0(data.len)
copyMem(buffer, addr copy[0], data.len)
@ -316,7 +316,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
proc close*(f: AsyncFile) =
## Closes the file specified.
when defined(windows):
when defined(windows) or defined(nimdoc):
if not closeHandle(f.fd.THandle).bool:
raiseOSError(osLastError())
else:

View file

@ -47,6 +47,24 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] =
result[i] = itm
inc(i)
proc repeat*[T](s: seq[T], n: Natural): seq[T] =
## Returns a new sequence with the items of `s` repeated `n` times.
##
## Example:
##
## .. code-block:
##
## let
## s = @[1, 2, 3]
## total = s.repeat(3)
## assert total == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
result = newSeq[T](n * s.len)
var o = 0
for x in 1..n:
for e in s:
result[o] = e
inc o
proc deduplicate*[T](seq1: seq[T]): seq[T] =
## Returns a new sequence without duplicates.
##
@ -86,7 +104,7 @@ proc zip*[S, T](seq1: seq[S], seq2: seq[T]): seq[tuple[a: S, b: T]] =
newSeq(result, m)
for i in 0 .. m-1: result[i] = (seq1[i], seq2[i])
proc distribute*[T](s: seq[T], num: int, spread = true): seq[seq[T]] =
proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] =
## Splits and distributes a sequence `s` into `num` sub sequences.
##
## Returns a sequence of `num` sequences. For some input values this is the
@ -113,11 +131,12 @@ proc distribute*[T](s: seq[T], num: int, spread = true): seq[seq[T]] =
## assert numbers.distribute(6)[0] == @[1, 2]
## assert numbers.distribute(6)[5] == @[7]
assert(not s.isNil, "`s` can't be nil")
assert(num > 0, "`num` has to be greater than zero")
if num < 2:
result = @[s]
return
let num = int(num) # XXX probably only needed because of .. bug
# Create the result and calculate the stride size and the remainder if any.
result = newSeq[seq[T]](num)
var
@ -587,4 +606,14 @@ when isMainModule:
seq2D[0][1] = true
doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
block: # repeat tests
let
a = @[1, 2, 3]
b: seq[int] = @[]
doAssert a.repeat(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3]
doAssert a.repeat(0) == @[]
#doAssert a.repeat(-1) == @[] # will not compile!
doAssert b.repeat(3) == @[]
echo "Finished doc tests"

View file

@ -196,7 +196,11 @@ proc mget*[A, B](t: var Table[A, B], key: A): var B =
var hc: THash
var index = rawGet(t, key, hc)
if index >= 0: result = t.data[index].val
else: raise newException(KeyError, "key not found: " & $key)
else:
when compiles($key):
raise newException(KeyError, "key not found: " & $key)
else:
raise newException(KeyError, "key not found")
iterator allValues*[A, B](t: Table[A, B]; key: A): B =
## iterates over any value in the table `t` that belongs to the given `key`.

View file

@ -365,9 +365,9 @@ proc polar*(z: Complex): tuple[r, phi: float] =
result.phi = phase(z)
proc rect*(r: float, phi: float): Complex =
## Returns the complex number with poolar coordinates `r` and `phi`.
## Returns the complex number with polar coordinates `r` and `phi`.
result.re = r * cos(phi)
result.im = sin(phi)
result.im = r * sin(phi)
proc `$`*(z: Complex): string =
@ -438,5 +438,6 @@ when isMainModule:
assert( arccoth(a) =~ arctanh(1/a) )
assert( phase(a) == 1.1071487177940904 )
assert( polar(a) =~ (2.23606797749979, 1.1071487177940904) )
var t = polar(a)
assert( rect(t.r, t.phi) =~ a )
assert( rect(1.0, 2.0) =~ (-0.4161468365471424, 0.9092974268256817) )

View file

@ -33,8 +33,8 @@
## proc hash(x: Something): THash =
## ## Computes a THash from `x`.
## var h: THash = 0
## h = h &! hash(x.foo)
## h = h &! hash(x.bar)
## h = h !& hash(x.foo)
## h = h !& hash(x.bar)
## result = !$h
import

View file

@ -390,8 +390,11 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
## server takes longer than specified an ETimeout exception will be raised.
var r = if proxy == nil: parseUri(url) else: proxy.url
var headers = substr(httpMethod, len("http"))
# TODO: Use generateHeaders further down once it supports proxies.
if proxy == nil:
headers.add(" " & r.path)
headers.add ' '
if r.path[0] != '/': headers.add '/'
headers.add(r.path)
if r.query.len > 0:
headers.add("?" & r.query)
else:
@ -567,9 +570,12 @@ proc downloadFile*(url: string, outputFilename: string,
proc generateHeaders(r: Uri, httpMethod: string,
headers: StringTableRef): string =
# TODO: Use this in the blocking HttpClient once it supports proxies.
result = substr(httpMethod, len("http"))
# TODO: Proxies
result.add(" " & r.path)
result.add ' '
if r.path[0] != '/': result.add '/'
result.add(r.path)
if r.query.len > 0:
result.add("?" & r.query)
result.add(" HTTP/1.1\c\L")

View file

@ -816,7 +816,7 @@ proc copy*(p: JsonNode): JsonNode =
# ------------- pretty printing ----------------------------------------------
proc indent(s: var string, i: int) =
s.add(repeatChar(i))
s.add(spaces(i))
proc newIndent(curr, indent: int, ml: bool): int =
if ml: return curr + indent

View file

@ -165,5 +165,5 @@ proc getCurrentLine(L: BaseLexer, marker: bool = true): string =
inc(i)
add(result, "\n")
if marker:
add(result, repeatChar(getColNumber(L, L.bufpos)) & "^\n")
add(result, spaces(getColNumber(L, L.bufpos)) & "^\n")

View file

@ -81,6 +81,23 @@ type
TReadLineResult: ReadLineResult, TSOBool: SOBool, PSocket: Socket,
TSocketImpl: SocketImpl].}
type
IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
IPv6, ## IPv6 address
IPv4 ## IPv4 address
TIpAddress* = object ## stores an arbitrary IP address
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
of IpAddressFamily.IPv6:
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
## case of IPv6
of IpAddressFamily.IPv4:
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
## case of IPv4
proc isIpAddress*(address_str: string): bool {.tags: [].}
proc parseIpAddress*(address_str: string): TIpAddress
proc isDisconnectionError*(flags: set[SocketFlag],
lastError: OSErrorCode): bool =
## Determines whether ``lastError`` is a disconnection error. Only does this
@ -511,6 +528,12 @@ proc connect*(socket: Socket, address: string, port = Port(0),
when defined(ssl):
if socket.isSSL:
# RFC3546 for SNI specifies that IP addresses are not allowed.
if not isIpAddress(address):
# Discard result in case OpenSSL version doesn't support SNI, or we're
# not using TLSv1+
discard SSL_set_tlsext_host_name(socket.sslHandle, address)
let ret = SSLConnect(socket.sslHandle)
socketError(socket, ret)
@ -969,20 +992,6 @@ proc isSsl*(socket: Socket): bool =
proc getFd*(socket: Socket): SocketHandle = return socket.fd
## Returns the socket's file descriptor
type
IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address
IPv6, ## IPv6 address
IPv4 ## IPv4 address
TIpAddress* = object ## stores an arbitrary IP address
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
of IpAddressFamily.IPv6:
address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in
## case of IPv6
of IpAddressFamily.IPv4:
address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in
## case of IPv4
proc IPv4_any*(): TIpAddress =
## Returns the IPv4 any address, which can be used to listen on all available
## network adapters
@ -1241,7 +1250,7 @@ proc parseIPv6Address(address_str: string): TIpAddress =
raise newException(ValueError,
"Invalid IP Address. The address consists of too many groups")
proc parseIpAddress*(address_str: string): TIpAddress =
proc parseIpAddress(address_str: string): TIpAddress =
## Parses an IP address
## Raises EInvalidValue on error
if address_str == nil:
@ -1250,3 +1259,13 @@ proc parseIpAddress*(address_str: string): TIpAddress =
return parseIPv6Address(address_str)
else:
return parseIPv4Address(address_str)
proc isIpAddress(address_str: string): bool =
## Checks if a string is an IP address
## Returns true if it is, false otherwise
try:
discard parseIpAddress(address_str)
except ValueError:
return false
return true

View file

@ -11,9 +11,12 @@
## It supports one convenience iterator over all command line options and some
## lower-level features.
##
## **Deprecated since version 0.9.3:** Use the `parseopt2 <parseopt2.html>`_
## module instead as this version has issues with spaces in arguments.
{.deprecated.}
## Supported syntax:
##
## 1. short options - ``-abcd``, where a, b, c, d are names
## 2. long option - ``--foo:bar``, ``--foo=bar`` or ``--foo``
## 3. argument - everything else
{.push debugger: off.}
include "system/inclrtl"

View file

@ -128,6 +128,7 @@ proc open*(my: var XmlParser, input: Stream, filename: string,
my.kind = xmlError
my.a = ""
my.b = ""
my.c = nil
my.options = options
proc close*(my: var XmlParser) {.inline.} =
@ -138,43 +139,43 @@ proc kind*(my: XmlParser): XmlEventKind {.inline.} =
## returns the current event type for the XML parser
return my.kind
proc charData*(my: XmlParser): string {.inline.} =
template charData*(my: XmlParser): string =
## returns the character data for the events: ``xmlCharData``,
## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial``
assert(my.kind in {xmlCharData, xmlWhitespace, xmlComment, xmlCData,
xmlSpecial})
return my.a
my.a
proc elementName*(my: XmlParser): string {.inline.} =
template elementName*(my: XmlParser): string =
## returns the element name for the events: ``xmlElementStart``,
## ``xmlElementEnd``, ``xmlElementOpen``
assert(my.kind in {xmlElementStart, xmlElementEnd, xmlElementOpen})
return my.a
my.a
proc entityName*(my: XmlParser): string {.inline.} =
template entityName*(my: XmlParser): string =
## returns the entity name for the event: ``xmlEntity``
assert(my.kind == xmlEntity)
return my.a
my.a
proc attrKey*(my: XmlParser): string {.inline.} =
template attrKey*(my: XmlParser): string =
## returns the attribute key for the event ``xmlAttribute``
assert(my.kind == xmlAttribute)
return my.a
my.a
proc attrValue*(my: XmlParser): string {.inline.} =
template attrValue*(my: XmlParser): string =
## returns the attribute value for the event ``xmlAttribute``
assert(my.kind == xmlAttribute)
return my.b
my.b
proc piName*(my: XmlParser): string {.inline.} =
template piName*(my: XmlParser): string =
## returns the processing instruction name for the event ``xmlPI``
assert(my.kind == xmlPI)
return my.a
my.a
proc piRest*(my: XmlParser): string {.inline.} =
template piRest*(my: XmlParser): string =
## returns the rest of the processing instruction for the event ``xmlPI``
assert(my.kind == xmlPI)
return my.b
my.b
proc rawData*(my: XmlParser): string {.inline.} =
## returns the underlying 'data' string by reference.
@ -621,7 +622,7 @@ proc next*(my: var XmlParser) =
of stateEmptyElementTag:
my.state = stateNormal
my.kind = xmlElementEnd
if not isNil(my.c):
if not my.c.isNil:
my.a = my.c
of stateError:
my.kind = xmlError

View file

@ -902,7 +902,7 @@ proc replacef*(s: string, sub: Peg, by: string): string {.
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
##
## .. code-block:: nim
## "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2")
## "var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2")
##
## Results in:
##

View file

@ -798,6 +798,22 @@ proc zunionstore*(r: Redis, destination: string, numkeys: string,
return r.readInteger()
# HyperLogLog
proc pfadd*(r: Redis, key: string, elements: varargs[string]): RedisInteger =
## Add variable number of elements into special 'HyperLogLog' set type
r.sendCommand("PFADD", key, elements)
return r.readInteger()
proc pfcount*(r: Redis, key: string): RedisInteger =
## Count approximate number of elements in 'HyperLogLog'
r.sendCommand("PFCOUNT", key)
return r.readInteger()
proc pfmerge*(r: Redis, destination: string, sources: varargs[string]) =
## Merge several source HyperLogLog's into one specified by destKey
r.sendCommand("PFMERGE", destination, sources)
raiseNoOK(r.readStatus(), r.pipeline.enabled)
# Pub/Sub

View file

@ -224,10 +224,12 @@ proc ssReadData(s: Stream, buffer: pointer, bufLen: int): int =
proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) =
var s = StringStream(s)
if bufLen > 0:
setLen(s.data, s.data.len + bufLen)
copyMem(addr(s.data[s.pos]), buffer, bufLen)
inc(s.pos, bufLen)
if bufLen <= 0:
return
if s.pos + bufLen > s.data.len:
setLen(s.data, s.pos + bufLen)
copyMem(addr(s.data[s.pos]), buffer, bufLen)
inc(s.pos, bufLen)
proc ssClose(s: Stream) =
var s = StringStream(s)

View file

@ -499,26 +499,47 @@ proc parseEnum*[T: enum](s: string, default: T): T =
return e
result = default
proc repeatChar*(count: int, c: char = ' '): string {.noSideEffect,
proc repeat*(c: char, count: int): string {.noSideEffect,
rtl, extern: "nsuRepeatChar".} =
## Returns a string of length `count` consisting only of
## the character `c`. You can use this proc to left align strings. Example:
##
## .. code-block:: nim
## proc tabexpand(indent: int, text: string, tabsize: int = 4) =
## echo '\t'.repeat(indent div tabsize), ' '.repeat(indent mod tabsize), text
##
## tabexpand(4, "At four")
## tabexpand(5, "At five")
## tabexpand(6, "At six")
result = newString(count)
for i in 0..count-1: result[i] = c
proc repeat*(s: string, n: int): string {.noSideEffect,
rtl, extern: "nsuRepeatStr".} =
## Returns String `s` concatenated `n` times. Example:
##
## .. code-block:: nim
## echo "+++ STOP ".repeat(4), "+++"
result = newStringOfCap(n * s.len)
for i in 1..n: result.add(s)
template spaces*(n: int): string = repeat(' ',n)
## Returns a String with `n` space characters. You can use this proc
## to left align strings. Example:
##
## .. code-block:: nim
## let
## width = 15
## text1 = "Hello user!"
## text2 = "This is a very long string"
## echo text1 & repeatChar(max(0, width - text1.len)) & "|"
## echo text2 & repeatChar(max(0, width - text2.len)) & "|"
result = newString(count)
for i in 0..count-1: result[i] = c
## echo text1 & spaces(max(0, width - text1.len)) & "|"
## echo text2 & spaces(max(0, width - text2.len)) & "|"
proc repeatStr*(count: int, s: string): string {.noSideEffect,
rtl, extern: "nsuRepeatStr".} =
## Returns `s` concatenated `count` times.
result = newStringOfCap(count*s.len)
for i in 0..count-1: result.add(s)
proc repeatChar*(count: int, c: char = ' '): string {.deprecated.} = repeat(c, count)
## deprecated: use repeat() or spaces()
proc repeatStr*(count: int, s: string): string {.deprecated.} = repeat(s, count)
## deprecated: use repeat(string, count) or string.repeat(count)
proc align*(s: string, count: int, padding = ' '): string {.
noSideEffect, rtl, extern: "nsuAlignString".} =

View file

@ -45,7 +45,6 @@ when defined(windows):
var
oldAttr = getAttributes()
proc winGetch(): cint {.header: "<conio.h>", importc: "_getch".}
else:
import termios, unsigned
@ -364,12 +363,11 @@ macro styledEcho*(m: varargs[expr]): stmt =
result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n")))
result.add(newCall(bindSym"resetAttributes"))
proc getch*(): char =
## Read a single character from the terminal, blocking until it is entered.
## The character is not printed to the terminal.
when defined(windows):
result = winGetch().char
else:
when not defined(windows):
proc getch*(): char =
## Read a single character from the terminal, blocking until it is entered.
## The character is not printed to the terminal. This is not available for
## Windows.
let fd = getFileHandle(stdin)
var oldMode: Termios
discard fd.tcgetattr(addr oldMode)

View file

@ -628,25 +628,25 @@ proc formatToken(info: TimeInfo, token: string, buf: var string) =
var fr = ($info.year).len()-2
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear
if fyear.len != 2: fyear = repeat('0', 2-fyear.len()) & fyear
buf.add(fyear)
of "yyy":
var fr = ($info.year).len()-3
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear
if fyear.len != 3: fyear = repeat('0', 3-fyear.len()) & fyear
buf.add(fyear)
of "yyyy":
var fr = ($info.year).len()-4
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear
if fyear.len != 4: fyear = repeat('0', 4-fyear.len()) & fyear
buf.add(fyear)
of "yyyyy":
var fr = ($info.year).len()-5
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear
if fyear.len != 5: fyear = repeat('0', 5-fyear.len()) & fyear
buf.add(fyear)
of "z":
let hrs = (info.timezone div 60) div 60

View file

@ -118,21 +118,35 @@ proc toUTF8*(c: Rune): string {.rtl, extern: "nuc$1".} =
elif i <=% 0x07FF:
result = newString(2)
result[0] = chr((i shr 6) or 0b110_00000)
result[1] = chr((i and ones(6)) or 0b10_000000)
result[1] = chr((i and ones(6)) or 0b10_0000_00)
elif i <=% 0xFFFF:
result = newString(3)
result[0] = chr(i shr 12 or 0b1110_0000)
result[1] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[2] = chr(i and ones(6) or 0b10_0000_00)
elif i <=% 0x0010FFFF:
elif i <=% 0x001FFFFF:
result = newString(4)
result[0] = chr(i shr 18 or 0b1111_0000)
result[1] = chr(i shr 12 and ones(6) or 0b10_0000_00)
result[2] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[3] = chr(i and ones(6) or 0b10_0000_00)
elif i <=% 0x03FFFFFF:
result = newString(5)
result[0] = chr(i shr 24 or 0b111110_00)
result[1] = chr(i shr 18 and ones(6) or 0b10_0000_00)
result[2] = chr(i shr 12 and ones(6) or 0b10_0000_00)
result[3] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[4] = chr(i and ones(6) or 0b10_0000_00)
elif i <=% 0x7FFFFFFF:
result = newString(6)
result[0] = chr(i shr 30 or 0b1111110_0)
result[1] = chr(i shr 24 and ones(6) or 0b10_0000_00)
result[2] = chr(i shr 18 and ones(6) or 0b10_0000_00)
result[3] = chr(i shr 12 and ones(6) or 0b10_0000_00)
result[4] = chr(i shr 6 and ones(6) or 0b10_0000_00)
result[5] = chr(i and ones(6) or 0b10_0000_00)
else:
result = newString(1)
result[0] = chr(i)
discard # error, exception?
proc `$`*(rune: Rune): string =
## converts a rune to a string

View file

@ -285,6 +285,16 @@ proc `$`*(u: Uri): string =
result.add(u.anchor)
when isMainModule:
block:
let str = "http://localhost"
let test = parseUri(str)
doAssert test.path == ""
block:
let str = "http://localhost/"
let test = parseUri(str)
doAssert test.path == "/"
block:
let str = "http://localhost:8080/test"
let test = parseUri(str)

View file

@ -1083,7 +1083,7 @@ proc addEscaped(s: string): string =
else: result.add(c)
proc nodeToXml(n: PNode, indent: int = 0): string =
result = repeatChar(indent, ' ') & "<" & n.nodeName
result = spaces(indent) & "<" & n.nodeName
if not isNil(n.attributes):
for i in items(n.attributes):
result.add(" " & i.name & "=\"" & addEscaped(i.value) & "\"")
@ -1098,23 +1098,23 @@ proc nodeToXml(n: PNode, indent: int = 0): string =
of ElementNode:
result.add(nodeToXml(i, indent + 2))
of TextNode:
result.add(repeatChar(indent * 2, ' '))
result.add(spaces(indent * 2))
result.add(addEscaped(i.nodeValue))
of CDataSectionNode:
result.add(repeatChar(indent * 2, ' '))
result.add(spaces(indent * 2))
result.add("<![CDATA[" & i.nodeValue & "]]>")
of ProcessingInstructionNode:
result.add(repeatChar(indent * 2, ' '))
result.add(spaces(indent * 2))
result.add("<?" & PProcessingInstruction(i).target & " " &
PProcessingInstruction(i).data & " ?>")
of CommentNode:
result.add(repeatChar(indent * 2, ' '))
result.add(spaces(indent * 2))
result.add("<!-- " & i.nodeValue & " -->")
else:
continue
result.add("\n")
# Add the ending tag - </tag>
result.add(repeatChar(indent, ' ') & "</" & n.nodeName & ">")
result.add(spaces(indent) & "</" & n.nodeName & ">")
proc `$`*(doc: PDocument): string =
## Converts a PDocument object into a string representation of it's XML

View file

@ -2021,7 +2021,7 @@ proc `$`*[T: tuple|object](x: T): string =
result = "("
var firstElement = true
for name, value in fieldPairs(x):
if not(firstElement): result.add(", ")
if not firstElement: result.add(", ")
result.add(name)
result.add(": ")
result.add($value)

View file

@ -34,9 +34,9 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.benign.} =
proc copyDeepString(src: NimString): NimString {.inline.} =
if src != nil:
result = rawNewString(src.space)
result = rawNewStringNoInit(src.len)
result.len = src.len
c_memcpy(result.data, src.data, (src.len + 1) * sizeof(char))
c_memcpy(result.data, src.data, src.len + 1)
proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) =
var

View file

@ -1,7 +1,7 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -117,7 +117,7 @@ proc usrToCell(usr: pointer): PCell {.inline.} =
# convert pointer to userdata to object (=pointer to refcount)
result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell)))
proc canbeCycleRoot(c: PCell): bool {.inline.} =
proc canBeCycleRoot(c: PCell): bool {.inline.} =
result = ntfAcyclic notin c.typ.flags
proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
@ -461,6 +461,9 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer =
{.pop.}
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
result = rawNewObj(typ, size, gch)
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
result = rawNewObj(typ, size, gch)
zeroMem(result, size)
@ -1050,7 +1053,11 @@ when useMarkForDebug or useBackupGc:
markGlobals(gch)
proc collectCT(gch: var TGcHeap) =
if (gch.zct.len >= ZctThreshold or (cycleGC and
# stackMarkCosts prevents some pathological behaviour: Stack marking
# becomes more expensive with large stacks and large stacks mean that
# cells with RC=0 are more likely to be kept alive by the stack.
let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold)
if (gch.zct.len >= stackMarkCosts or (cycleGC and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
gch.recGcLock == 0:
when useMarkForDebug:

View file

@ -593,7 +593,7 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
return
add(gch.zct, res)
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1: bool): pointer =
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1 = false): pointer =
# generates a new object and sets its reference counter to 0
acquire(gch)
sysAssert(allocInv(gch.region), "rawNewObj begin")

View file

@ -261,6 +261,10 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
zeroMem(result, size)
when defined(memProfiler): nimProfile(size)
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
result = rawNewObj(typ, size, gch)
when defined(memProfiler): nimProfile(size)
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
# `newObj` already uses locks, so no need for them here.
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)

View file

@ -11,7 +11,7 @@ when declared(NimString):
# we are in system module:
{.pragma: codegenType, compilerproc.}
else:
{.pragma: codegenType.}
{.pragma: codegenType, importc.}
type
# This should be he same as ast.TTypeKind
@ -65,7 +65,7 @@ type
tyBigNum,
TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase
TNimNode {.codegenType, final.} = object
TNimNode {.codegenType.} = object
kind: TNimNodeKind
offset: int
typ: ptr TNimType
@ -78,7 +78,7 @@ type
ntfAcyclic = 1, # type cannot form a cycle
ntfEnumHole = 2 # enum has holes and thus `$` for them needs the slow
# version
TNimType {.codegenType, final.} = object
TNimType {.codegenType.} = object
size: int
kind: TNimKind
flags: set[TNimTypeFlag]

View file

@ -1,7 +1,7 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2013 Andreas Rumpf
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -240,6 +240,8 @@ elif defined(nogc) and defined(useMalloc):
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
proc newObjNoInit(typ: PNimType, size: int): pointer =
result = alloc(size)
proc growObj(old: pointer, newsize: int): pointer =
result = realloc(old, newsize)
@ -289,6 +291,10 @@ elif defined(nogc):
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
result = alloc0(size)
proc newObjNoInit(typ: PNimType, size: int): pointer =
result = alloc(size)
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
cast[PGenericSeq](result).len = len

View file

@ -145,8 +145,8 @@ proc readAllFile(file: File): string =
proc readAll(file: File): TaintedString =
# Separate handling needed because we need to buffer when we
# don't know the overall length of the File.
var len = rawFileSize(file)
if len >= 0:
let len = if file != stdin: rawFileSize(file) else: -1
if len > 0:
result = readAllFile(file, len).TaintedString
else:
result = readAllBuffer(file).TaintedString

View file

@ -30,18 +30,30 @@ proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
if a == b: return true
if a == nil or b == nil: return false
return a.len == b.len and
c_memcmp(a.data, b.data, a.len * sizeof(char)) == 0'i32
c_memcmp(a.data, b.data, a.len) == 0'i32
when declared(allocAtomic):
template allocStr(size: expr): expr =
cast[NimString](allocAtomic(size))
template allocStrNoInit(size: expr): expr =
cast[NimString](boehmAllocAtomic(size))
else:
template allocStr(size: expr): expr =
cast[NimString](newObj(addr(strDesc), size))
template allocStrNoInit(size: expr): expr =
cast[NimString](newObjNoInit(addr(strDesc), size))
proc rawNewStringNoInit(space: int): NimString {.compilerProc.} =
var s = space
if s < 7: s = 7
result = allocStrNoInit(sizeof(TGenericSeq) + s + 1)
result.reserved = s
proc rawNewString(space: int): NimString {.compilerProc.} =
var s = space
if s < 8: s = 7
if s < 7: s = 7
result = allocStr(sizeof(TGenericSeq) + s + 1)
result.reserved = s
@ -53,10 +65,10 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} =
var start = max(start, 0)
var len = min(last, s.len-1) - start + 1
if len > 0:
result = rawNewString(len)
result = rawNewStringNoInit(len)
result.len = len
c_memcpy(result.data, addr(s.data[start]), len * sizeof(char))
#result.data[len] = '\0'
c_memcpy(result.data, addr(s.data[start]), len)
result.data[len] = '\0'
else:
result = rawNewString(len)
@ -64,10 +76,9 @@ proc copyStr(s: NimString, start: int): NimString {.compilerProc.} =
result = copyStrLast(s, start, s.len-1)
proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} =
result = rawNewString(len)
result = rawNewStringNoInit(len)
result.len = len
c_memcpy(result.data, str, (len+1) * sizeof(char))
#result.data[len] = '\0' # readline relies on this!
c_memcpy(result.data, str, len + 1)
proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
result = toNimStr(str, c_strlen(str))
@ -77,23 +88,24 @@ proc copyString(src: NimString): NimString {.compilerRtl.} =
if (src.reserved and seqShallowFlag) != 0:
result = src
else:
result = rawNewString(src.space)
result = rawNewStringNoInit(src.len)
result.len = src.len
c_memcpy(result.data, src.data, (src.len + 1) * sizeof(char))
c_memcpy(result.data, src.data, src.len + 1)
proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
if src != nil:
var s = src.space
if s < 8: s = 7
when declared(newObjRC1):
var s = src.len
if s < 7: s = 7
result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) +
s+1))
result.reserved = s
else:
result = allocStr(sizeof(TGenericSeq) + s + 1)
result.reserved = s
result = rawNewStringNoInit(src.len)
result.len = src.len
c_memcpy(result.data, src.data, src.len + 1)
proc hashString(s: string): int {.compilerproc.} =
# the compiler needs exactly the same hash function!
# this used to be used for efficient generation of string case statements
@ -113,7 +125,7 @@ proc addChar(s: NimString, c: char): NimString =
if result.len >= result.space:
result.reserved = resize(result.space)
result = cast[NimString](growObj(result,
sizeof(TGenericSeq) + (result.reserved+1) * sizeof(char)))
sizeof(TGenericSeq) + result.reserved + 1))
result.data[result.len] = c
result.data[result.len+1] = '\0'
inc(result.len)
@ -157,7 +169,7 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1))
result.reserved = sp
#result = rawNewString(sp)
#copyMem(result, dest, dest.len * sizeof(char) + sizeof(TGenericSeq))
#copyMem(result, dest, dest.len + sizeof(TGenericSeq))
# DO NOT UPDATE LEN YET: dest.len = newLen
proc appendString(dest, src: NimString) {.compilerproc, inline.} =
@ -264,7 +276,17 @@ proc nimFloatToStr(f: float): string {.compilerproc.} =
buf[n] = '.'
buf[n+1] = '0'
buf[n+2] = '\0'
result = $buf
# On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' are produced.
# We want to get rid of these here:
if buf[n-1] == 'N':
result = "nan"
elif buf[n-1] == 'F':
if buf[0] == '-':
result = "-inf"
else:
result = "inf"
else:
result = $buf
proc strtod(buf: cstring, endptr: ptr cstring): float64 {.importc,
header: "<stdlib.h>", noSideEffect.}

View file

@ -50,7 +50,7 @@ when useWinVersion:
from winlean import SocketHandle
else:
const
versions = "(.10|.1.0.1|.1.0.0|.0.9.9|.0.9.8|.0.9.7|.0.9.6|.0.9.5|.0.9.4)"
versions = "(.10|.1.0.1|.1.0.0|.0.9.9|.0.9.8)"
when defined(macosx):
const
DLLSSLName = "libssl" & versions & ".dylib"
@ -141,6 +141,14 @@ const
SSL_CTRL_GET_MAX_CERT_LIST* = 50
SSL_CTRL_SET_MAX_CERT_LIST* = 51 #* Allow SSL_write(..., n) to return r with 0 < r < n (i.e. report success
# * when just a single record has been written): *
SSL_CTRL_SET_TLSEXT_SERVERNAME_CB = 53
SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG = 54
SSL_CTRL_SET_TLSEXT_HOSTNAME = 55
TLSEXT_NAMETYPE_host_name* = 0
SSL_TLSEXT_ERR_OK* = 0
SSL_TLSEXT_ERR_ALERT_WARNING* = 1
SSL_TLSEXT_ERR_ALERT_FATAL* = 2
SSL_TLSEXT_ERR_NOACK* = 3
SSL_MODE_ENABLE_PARTIAL_WRITE* = 1 #* Make it possible to retry SSL_write() with changed buffer location
# * (buffer contents must stay the same!); this is not the default to avoid
# * the misconception that non-blocking SSL_write() behaves like
@ -296,9 +304,41 @@ proc CRYPTO_malloc_init*() =
proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cInt, larg: int, parg: pointer): int{.
cdecl, dynlib: DLLSSLName, importc.}
proc SSL_CTX_callback_ctrl(ctx: SslCtx, typ: cInt, fp: PFunction): int{.
cdecl, dynlib: DLLSSLName, importc.}
proc SSLCTXSetMode*(ctx: SslCtx, mode: int): int =
result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil)
proc SSL_ctrl*(ssl: SslPtr, cmd: cInt, larg: int, parg: pointer): int{.
cdecl, dynlib: DLLSSLName, importc.}
proc SSL_set_tlsext_host_name*(ssl: SslPtr, name: cstring): int =
result = SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, name)
## Set the SNI server name extension to be used in a client hello.
## Returns 1 if SNI was set, 0 if current SSL configuration doesn't support SNI.
proc SSL_get_servername*(ssl: SslPtr, typ: cInt = TLSEXT_NAMETYPE_host_name): cstring {.cdecl, dynlib: DLLSSLName, importc.}
## Retrieve the server name requested in the client hello. This can be used
## in the callback set in `SSL_CTX_set_tlsext_servername_callback` to
## implement virtual hosting. May return `nil`.
proc SSL_CTX_set_tlsext_servername_callback*(ctx: SslCtx, cb: proc(ssl: SslPtr, cb_id: int, arg: pointer): int {.cdecl.}): int =
## Set the callback to be used on listening SSL connections when the client hello is received.
##
## The callback should return one of:
## * SSL_TLSEXT_ERR_OK
## * SSL_TLSEXT_ERR_ALERT_WARNING
## * SSL_TLSEXT_ERR_ALERT_FATAL
## * SSL_TLSEXT_ERR_NOACK
result = SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, cast[PFunction](cb))
proc SSL_CTX_set_tlsext_servername_arg*(ctx: SslCtx, arg: pointer): int =
## Set the pointer to be used in the callback registered to ``SSL_CTX_set_tlsext_servername_callback``.
result = SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG, 0, arg)
proc bioNew*(b: PBIO_METHOD): BIO{.cdecl, dynlib: DLLUtilName, importc: "BIO_new".}
proc bioFreeAll*(b: BIO){.cdecl, dynlib: DLLUtilName, importc: "BIO_free_all".}
proc bioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc: "BIO_s_mem".}
@ -341,8 +381,6 @@ else:
dynlib: DLLSSLName, importc.}
proc SslSetFd*(s: PSSL, fd: cInt): cInt{.cdecl, dynlib: DLLSSLName, importc.}
proc SslCtrl*(ssl: PSSL, cmd: cInt, larg: int, parg: Pointer): int{.cdecl,
dynlib: DLLSSLName, importc.}
proc SslCTXCtrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: Pointer): int{.
cdecl, dynlib: DLLSSLName, importc.}

View file

@ -8,10 +8,6 @@ the Nim compiler itself is written in the Nim programming language
the C source of an older version of the compiler are needed to bootstrap the
latest version. The C sources are available in a separate repo [here](http://github.com/nim-lang/csources).
Pre-compiled snapshots of the compiler are also available on
[Nimbuild](http://build.nim-lang.org/). Your platform however may not
currently be built for.
The compiler currently supports the following platform and architecture
combinations:
@ -39,8 +35,7 @@ $ bin/nim c koch
$ ./koch boot -d:release
```
``koch install [dir]`` may then be used to install Nim, or you can simply
add it to your PATH. More ``koch`` related options are documented in
``koch install [dir]`` may then be used to install Nim, but lots of things don't work then so don't do that. Add it to your PATH instead. More ``koch`` related options are documented in
[doc/koch.txt](doc/koch.txt).
The above steps can be performed on Windows in a similar fashion, the

View file

@ -1,6 +1,5 @@
discard """
line: 21
errormsg: "invalid type: 'Table[string, proc (string){.gcsafe.}]'"
output: "action 3 arg"
"""
import tables

View file

@ -3,14 +3,14 @@ discard """
WARNING: false first assertion from bar
ERROR: false second assertion from bar
-1
tests/assert/tfailedassert.nim:27 false assertion from foo
tfailedassert.nim:27 false assertion from foo
'''
"""
type
TLineInfo = tuple[filename: string, line: int]
TMyError = object of E_Base
TMyError = object of Exception
lineinfo: TLineInfo
EMyError = ref TMyError

View file

@ -0,0 +1,30 @@
discard """
output: '''255
1 1
0.5'''
"""
# bug #1181
type
TFoo = object
x: int32
proc mainowar =
var foo: TFoo
foo.x = 0xff
var arr1 = cast[ptr array[4, uint8]](addr foo)[] # Fails.
echo arr1[when cpuEndian == littleEndian: 0 else: 3]
var i = 1i32
let x = addr i
var arr2 = cast[ptr array[4, uint8]](x)[] # Fails.
echo arr2[when cpuEndian == littleEndian: 0 else: 3], " ", i
# bug #1715
var a: array[2, float32] = [0.5'f32, 0.7]
let p = addr a
var b = p[]
echo b[0]
mainowar()

View file

@ -0,0 +1,15 @@
discard """
disabled: "true"
"""
# Now the compiler fails with OOM. yay.
# bug #794
type TRange = range[0..3]
const str = "123456789"
for i in TRange.low .. TRange.high:
echo str[i] #This works fine
echo str[int(i) .. int(TRange.high)] #So does this
echo str[i .. TRange.high] #The compiler complains about this

View file

@ -0,0 +1,80 @@
# bug #2250
import
math, strutils
type
Meters = float
Point2[T] = tuple[x, y: T]
HexState* = enum
hsOn, hsOff
Index = uint16
HexGrid* = object
w, h: int ## Width and height of the hex grid.
radius: Meters ## Radius of circle that circumscribes a hexagon.
grid: seq[HexState] ## Information on what hexes are drawn.
HexVtxIndex = enum
hiA, hiB, hiC, hiD, hiE, hiF
HexCoord* = Point2[int]
const
HexDY = sqrt(1.0 - (0.5 * 0.5)) # dy from center to midpoint of 1-2
HexDX = sqrt(1.0 - (HexDY * HexDY)) # dx from center to midpoint of 1-5 (0.5)
let
hexOffsets : array[HexVtxIndex, Point2[float]] = [
(-1.0, 0.0),
(-HexDX, -HexDY),
(HexDX, -HexDY),
(1.0, 0.0),
(HexDX, HexDY),
(-HexDX, HexDY)]
evenSharingOffsets : array[HexVtxIndex, tuple[hc: HexCoord; idx: HexVtxIndex]] = [
((0,0), hiA),
((0,0), hiB),
((1,-1), hiA),
((1,0), hiB),
((1,0), hiA),
((0,1), hiB)]
oddSharingOffsets : array[HexVtxIndex, tuple[hc: HexCoord; idx: HexVtxIndex]] = [
((0,0), hiA),
((0,0), hiB),
((1,0), hiA),
((1,1), hiB),
((1,1), hiA),
((0,1), hiB)]
template odd*(i: int) : expr =
(i and 1) != 0
proc vidx(hg: HexGrid; col, row: int; i: HexVtxIndex) : Index =
#NOTE: this variation compiles
#var offset : type(evenSharingOffsets[i])
#
#if odd(col):
# offset = oddSharingOffsets[i]
#else:
# offset = evenSharingOffsets[i]
let
#NOTE: this line generates the bad code
offset = (if odd(col): oddSharingOffsets[i] else: evenSharingOffsets[i])
x = col + 1 + offset.hc.x
y = row + 1 + offset.hc.y
result = Index(x*2 + y * (hg.w + 2)*2 + int(offset.idx))
proc go() =
var hg : HexGrid
echo "vidx ", $vidx(hg, 1, 2, hiC)
go()

View file

@ -0,0 +1,8 @@
discard """
file: "tconsttypemismatch.nim"
line: 7
errormsg: "type mismatch"
"""
# bug #2252
const foo: int = 1000 / 30

View file

@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $target"
cmd: "nim cpp $file"
"""
import rawsockets

View file

@ -1,5 +1,5 @@
discard """
cmd: "nim cpp $target"
cmd: "nim cpp $file"
"""
import typeinfo

View file

@ -1,6 +1,5 @@
discard """
line: 9
errormsg: "'a' is deprecated [Deprecated]"
nimout: "a is deprecated [Deprecated]"
"""
var
@ -8,4 +7,3 @@ var
a[8] = 1

View file

@ -1,6 +1,6 @@
discard """
line: 20
errormsg: " usage of a type with a destructor in a non destructible context"
line: 23
nimout: " usage of a type with a destructor in a non destructible context"
"""
{.experimental.}
@ -19,5 +19,9 @@ proc open: TMyObj =
proc `$`(x: TMyObj): string = $x.y
echo open()
proc foo =
discard open()
# XXX doesn't trigger this yet:
#echo open()

View file

@ -1,6 +1,6 @@
discard """
line: 7
errormsg: "expression 'items' cannot be called"
errormsg: "undeclared identifier: 'items'"
"""
type a = enum b,c,d

View file

@ -0,0 +1,18 @@
discard """
output: '''hi
hi'''
"""
# bug #1742
template test(): expr =
let a = 0
defer: echo "hi"
a
let i = test()
import strutils
let x = try: parseInt("133a")
except: -1
finally: echo "hi"

View file

@ -0,0 +1,6 @@
discard """
nimout: "Special variable 'result' is shadowed. [ResultShadowed]"
"""
proc test(): string =
var result = "foo"

View file

@ -188,7 +188,7 @@ proc traceTree[T,D](root: PNode[T,D]) =
write stdout, space
proc doTrace(n: PNode[T,D], level: int) =
var space = repeatChar(2 * level)
var space = spaces(2 * level)
traceln(space)
write stdout, "node: "
if n == nil:

View file

@ -1,5 +1,11 @@
# Bug #2022
discard """
output: '''@[97, 45]
@[true, false]
@[false, false]'''
"""
## The goal of this snippet is to provide and test a construct for general-
## purpose, random-access mapping. I use an AST-manipulation-based approach
## because it's more efficient than using procedure pointers and less
@ -31,6 +37,7 @@ type Mapped[Input; predicate: static[string]] = object
input: Input
macro map(input, predicate: expr): expr =
let predicate = callsite()[2]
newNimNode(nnkObjConstr).add(
newNimNode(nnkBracketExpr).add(
ident"Mapped",

View file

@ -1,6 +1,6 @@
discard """
file: "toop1.nim"
output: "in globalaux2: 10\ntotal globals: 2\nint value: 100\nstring value: second"
disabled: "true"
"""
import globalaux, globalaux2

54
tests/init/tuninit2.nim Normal file
View file

@ -0,0 +1,54 @@
# bug #2316
type
EventType = enum
QuitEvent = 5
AppMain* = ref object of RootObj
width: int
height: int
title: string
running: bool
event_type: EventType
App* = ref object of AppMain
draw_proc: proc(app: AppMain): void {.closure.}
events_proc: proc(app: AppMain): void {.closure.}
update_proc: proc(app: AppMain, dt: float): void {.closure.}
load_proc: proc(app: AppMain): void {.closure.}
proc initApp*(t: string, w, h: int): App =
App(width: w, height: h, title: t, event_type: EventType.QuitEvent)
method getTitle*(self: AppMain): string = self.title
method getWidth*(self: AppMain): int = self.width
method getHeight*(self: AppMain): int = self.height
method draw*(self: App, draw: proc(app: AppMain)): void =
self.draw_proc = draw
method load*(self: App, load: proc(a: AppMain)): void =
self.load_proc = load
method events*(self: App, events: proc(app: AppMain)): void =
self.events_proc = events
method update*(self: App, update: proc(app: AppMain, delta: float)): void =
self.update_proc = update
method run*(self: App): void = discard
var mygame = initApp("Example", 800, 600)
mygame.load(proc(app: AppMain): void =
echo app.getTitle()
echo app.getWidth()
echo app.getHeight()
)
mygame.events(proc(app: AppMain): void =
discard
)
mygame.run()

View file

@ -471,7 +471,7 @@ proc build_help*(expected: seq[Tparameter_specification] = @[],
let width = prefixes.map(proc (x: string): int = 3 + len(x)).max
for line in zip(prefixes, helps):
result.add(line.a & repeatChar(width - line.a.len) & line.b)
result.add(line.a & spaces(width - line.a.len) & line.b)
proc echo_help*(expected: seq[Tparameter_specification] = @[],

View file

@ -1,5 +1,5 @@
import streams
from strutils import repeatChar
from strutils import repeat
proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString =
var lastChr = length
@ -10,7 +10,7 @@ proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString =
proc writePaddedStr*(s: PStream, str: string, length: int, padChar = '\0') =
if str.len < length:
s.write(str)
s.write(repeatChar(length - str.len, padChar))
s.write(repeat(padChar, length - str.len))
elif str.len > length:
s.write(str.substr(0, length - 1))
else:
@ -37,7 +37,7 @@ when isMainModule:
testStream.setPosition 0
testStream.writePaddedStr("Sup", 10)
echo(repr(testStream), testStream.data.len)
doAssert testStream.data == "Sup"&repeatChar(7, '\0')
doAssert testStream.data == "Sup"&repeat('\0', 7)
testStream.setPosition 0
res = testStream.readPaddedStr(10)

View file

@ -144,7 +144,7 @@ when isMainModule:
discard """block:
var
TestFile: FileChallengePair
contents = repeatStr(2, "abcdefghijklmnopqrstuvwxyz")
contents = repeat("abcdefghijklmnopqrstuvwxyz", 2)
testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32)
testFile.file = checksumStr(contents)
myAssets.add testFile"""

View file

@ -113,7 +113,7 @@ when defined(recordMode):
isRecording = false
proc zeroPad*(s: string; minLen: int): string =
if s.len < minLen:
result = repeatChar(minLen - s.len, '0')
result = repeat(0, minLen - s.len)
result.add s
else:
result = s

View file

@ -27,9 +27,9 @@ when isMainModule:
var res = t.map(proc(z: int): int = result = z * 10)
dumpSeq res
from strutils import toHex, repeatStr
from strutils import toHex
var foocakes = t.map(proc(z: int): string =
result = toHex((z * 23).biggestInt, 4))
result = toHex((z * 23).BiggestInt, 4))
dumpSeq foocakes
t.mapInPlace(proc(z: int): int = result = z * 30)

View file

@ -24,17 +24,17 @@ when isMainModule:
import strutils
var r = compress("Hello")
echo repr(r)
var l = "Hello".len
var rr = uncompress(r, l)
var ln = "Hello".len
var rr = uncompress(r, ln)
echo repr(rr)
assert rr == "Hello"
proc `*`(a: string; b: int): string {.inline.} = result = repeatStr(b, a)
proc `*`(a: string; b: int): string {.inline.} = result = repeat(a, b)
var s = "yo dude sup bruh homie" * 50
r = compress(s)
echo s.len, " -> ", r.len
l = s.len
rr = uncompress(r, l)
ln = s.len
rr = uncompress(r, ln)
echo r.len, " -> ", rr.len
assert rr == s

View file

@ -174,7 +174,7 @@ when isMainModule:
block:
var
TestFile: FileChallengePair
contents = repeatStr(2, "abcdefghijklmnopqrstuvwxyz")
contents = repeat("abcdefghijklmnopqrstuvwxyz", 2)
testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32)
testFile.file = checksumStr(contents)
myAssets.add testFile

105
tests/misc/parsecomb.nim Normal file
View file

@ -0,0 +1,105 @@
type Input[T] = object
toks: seq[T]
index: int
type
ResultKind* = enum rkSuccess, rkFailure
Result*[T, O] = object
case kind*: ResultKind
of rkSuccess:
output*: O
input: Input[T]
of rkFailure:
nil
type
Parser*[T, O] = distinct proc (input: Input[T]): Result[T, O]
proc unit*[T, O](v: O): Parser[T, O] =
Parser(proc (inp: Input[T]): Result[T, O] =
Result[T, O](kind: rkSuccess, output: v, input: inp))
proc fail*[T, O](): Parser[T, O] =
Parser(proc (inp: Input[T]): Result[T, O] =
Result(kind: rkFailure))
method runInput[T, O](self: Parser[T, O], inp: Input[T]): Result[T, O] =
# hmmm ..
type tmp = proc (input: Input[T]): Result[T, O]
# XXX: above needed for now, as without the `tmp` bit below, it compiles to invalid C.
tmp(self)(inp)
method run*[T, O](self: Parser[T, O], toks: seq[T]): Result[T, O] =
self.runInput(Input[T](toks: toks, index: 0))
method chain*[T, O1, O2](self: Parser[T, O1], nextp: proc (v: O1): Parser[T, O2]): Parser[T, O2] =
Parser(proc (inp: Input[T]): Result[T, O2] =
let r = self.runInput(inp)
case r.kind:
of rkSuccess:
nextp(r.output).runInput(r.input)
of rkFailure:
Result[T, O2](kind: rkFailure))
method skip[T](self: Input[T], n: int): Input[T] =
Input[T](toks: self.toks, index: self.index + n)
proc pskip*[T](n: int): Parser[T, tuple[]] =
Parser(proc (inp: Input[T]): Result[T, tuple[]] =
if inp.index + n <= inp.toks.len:
Result[T, tuple[]](kind: rkSuccess, output: (), input: inp.skip(n))
else:
Result[T, tuple[]](kind: rkFailure))
proc tok*[T](t: T): Parser[T, T] =
Parser(proc (inp: Input[T]): Result[T, T] =
if inp.index < inp.toks.len and inp.toks[inp.index] == t:
pskip[T](1).then(unit[T, T](t)).runInput(inp)
else:
Result[T, T](kind: rkFailure))
proc `+`*[T, O](first: Parser[T, O], second: Parser[T, O]): Parser[T, O] =
Parser(proc (inp: Input[T]): Result[T, O] =
let r = first.runInput(inp)
case r.kind
of rkSuccess:
r
else:
second.runInput(inp))
# end of primitives (definitions involving Parser(..))
method map*[T, O1, O2](self: Parser[T, O1], p: proc (v: O1): O2): Parser[T, O2] =
self.chain(proc (v: O1): Parser[T, O2] =
unit[T, O2](p(v)))
method then*[T, O1, O2](self: Parser[T, O1], next: Parser[T, O2]): Parser[T, O2] =
self.chain(proc (v: O1): Parser[T, O2] =
next)
proc `*`*[T, O1, O2](first: Parser[T, O1], second: Parser[T, O2]): Parser[T, (O1, O2)] =
first.chain(proc (v1: O1): Parser[T, (O1, O2)] =
second.map(proc (v2: O2): (O1, O2) =
(v1, v2)))
proc repeat0*[T, O](inner: Parser[T, O]): Parser[T, seq[O]] =
var nothing = unit[T, seq[O]](@[])
inner.chain(proc(v: O): Parser[T, seq[O]] =
repeat0(inner).map(proc(vs: seq[O]): seq[O] =
@[v] & vs)) + nothing
proc repeat1*[T, O](inner: Parser[T, O]): Parser[T, seq[O]] =
inner.chain(proc(v: O): Parser[T, seq[O]] =
repeat0(inner).map(proc(vs: seq[O]): seq[O] =
@[v] & vs))
proc leftRec*[T, O, A](inner: Parser[T, O], after: Parser[T, A], fold: proc(i: O, a: A): O): Parser[T, O] =
(inner*repeat0(after)).map(proc(ias: (O, seq[A])): O =
var (i, asx) = ias
for a in asx:
i = fold(i, a)
i)
proc lazy*[T, O](inner: proc(): Parser[T, O]): Parser[T, O] =
unit[T, tuple[]](()).chain(proc(v: tuple[]): Parser[T, O] =
inner())

View file

@ -1,7 +1,7 @@
discard """
file: "tissue710.nim"
line: 8
errorMsg: "expression '||' cannot be called"
errorMsg: "undeclared identifier: '||'"
"""
var sum = 0
for x in 3..1000:

Some files were not shown because too many files have changed in this diff Show more