Merge branch 'devel' of https://github.com/Araq/Nim into devel

Conflicts:
	compiler/semexprs.nim
This commit is contained in:
Araq 2015-01-07 02:44:22 +01:00
commit 65310266d8
46 changed files with 991 additions and 490 deletions

1
.gitignore vendored
View file

@ -41,3 +41,4 @@ xcuserdata/
/testresults.html /testresults.html
/testresults.json /testresults.json
testament.db testament.db
/csources/

View file

@ -399,6 +399,7 @@ const
tyPureObject* = tyTuple tyPureObject* = tyTuple
GcTypeKinds* = {tyRef, tySequence, tyString} GcTypeKinds* = {tyRef, tySequence, tyString}
tyError* = tyProxy # as an errornous node should match everything tyError* = tyProxy # as an errornous node should match everything
tyUnknown* = tyFromExpr
tyUnknownTypes* = {tyError, tyFromExpr} tyUnknownTypes* = {tyError, tyFromExpr}
@ -1340,6 +1341,10 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
result = t result = t
while result.kind in kinds: result = lastSon(result) while result.kind in kinds: result = lastSon(result)
proc safeSkipTypes*(t: PType, kinds: TTypeKinds): PType =
result = if t != nil: t.skipTypes(kinds)
else: nil
proc isGCedMem*(t: PType): bool {.inline.} = proc isGCedMem*(t: PType): bool {.inline.} =
result = t.kind in {tyString, tyRef, tySequence} or result = t.kind in {tyString, tyRef, tySequence} or
t.kind == tyProc and t.callConv == ccClosure t.kind == tyProc and t.callConv == ccClosure

View file

@ -23,6 +23,7 @@ type
id: int # for generating IDs id: int # for generating IDs
toc, section: TSections toc, section: TSections
indexValFilename: string indexValFilename: string
analytics: string # Google Analytics javascript, "" if doesn't exist
seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML. seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML.
PDoc* = ref TDocumentor ## Alias to type less. PDoc* = ref TDocumentor ## Alias to type less.
@ -61,6 +62,23 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex), initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex),
options.gConfigVars, filename, {roSupportRawDirective}, options.gConfigVars, filename, {roSupportRawDirective},
docgenFindFile, compilerMsgHandler) docgenFindFile, compilerMsgHandler)
if config.hasKey("doc.googleAnalytics"):
result.analytics = """
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', '$1', 'auto');
ga('send', 'pageview');
</script>
""" % [config["doc.googleAnalytics"]]
else:
result.analytics = ""
result.seenSymbols = newStringTable(modeCaseInsensitive) result.seenSymbols = newStringTable(modeCaseInsensitive)
result.id = 100 result.id = 100
@ -562,10 +580,10 @@ proc genOutFile(d: PDoc): PRope =
# XXX what is this hack doing here? 'optCompileOnly' means raw output!? # XXX what is this hack doing here? 'optCompileOnly' means raw output!?
code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time", "tableofcontents", "moduledesc", "date", "time",
"content", "author", "version"], "content", "author", "version", "analytics"],
[title.toRope, toc, d.modDesc, toRope(getDateStr()), [title.toRope, toc, d.modDesc, toRope(getDateStr()),
toRope(getClockStr()), content, d.meta[metaAuthor].toRope, toRope(getClockStr()), content, d.meta[metaAuthor].toRope,
d.meta[metaVersion].toRope]) d.meta[metaVersion].toRope, d.analytics.toRope])
else: else:
code = content code = content
result = code result = code
@ -630,7 +648,8 @@ proc commandBuildIndex*() =
let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time", "tableofcontents", "moduledesc", "date", "time",
"content", "author", "version"], "content", "author", "version", "analytics"],
["Index".toRope, nil, nil, toRope(getDateStr()), ["Index".toRope, nil, nil, toRope(getDateStr()),
toRope(getClockStr()), content, nil, nil]) toRope(getClockStr()), content, nil, nil, nil])
# no analytics because context is not available
writeRope(code, getOutFile("theindex", HtmlExt)) writeRope(code, getOutFile("theindex", HtmlExt))

View file

@ -25,16 +25,22 @@ proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
if ctx.instLines: result.info = b.info if ctx.instLines: result.info = b.info
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
case templ.kind template handleParam(param) =
of nkSym: let x = param
var s = templ.sym
if s.owner.id == c.owner.id:
if s.kind == skParam:
let x = actual.sons[s.position]
if x.kind == nkArgList: if x.kind == nkArgList:
for y in items(x): result.add(y) for y in items(x): result.add(y)
else: else:
result.add copyTree(x) result.add copyTree(x)
case templ.kind
of nkSym:
var s = templ.sym
if s.owner.id == c.owner.id:
case s.kind
of skParam:
handleParam actual.sons[s.position]
of skGenericParam:
handleParam actual.sons[s.owner.typ.len + s.position - 1]
else: else:
internalAssert sfGenSym in s.flags internalAssert sfGenSym in s.flags
var x = PSym(idTableGet(c.mapping, s)) var x = PSym(idTableGet(c.mapping, s))
@ -56,20 +62,30 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
proc evalTemplateArgs(n: PNode, s: PSym): PNode = proc evalTemplateArgs(n: PNode, s: PSym): PNode =
# if the template has zero arguments, it can be called without ``()`` # if the template has zero arguments, it can be called without ``()``
# `n` is then a nkSym or something similar # `n` is then a nkSym or something similar
var a: int var totalParams = case n.kind
case n.kind of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: <n.len
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: else: 0
a = sonsLen(n)
else: a = 0 var
var f = s.typ.sonsLen genericParams = s.ast[genericParamsPos].len
if a > f: globalError(n.info, errWrongNumberOfArguments) expectedRegularParams = <s.typ.len
givenRegularParams = totalParams - genericParams
if totalParams > expectedRegularParams + genericParams:
globalError(n.info, errWrongNumberOfArguments)
result = newNodeI(nkArgList, n.info) result = newNodeI(nkArgList, n.info)
for i in countup(1, f - 1): for i in 1 .. givenRegularParams:
var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast) result.addSon n.sons[i]
if arg == nil or arg.kind == nkEmpty:
for i in givenRegularParams+1 .. expectedRegularParams:
let default = s.typ.n.sons[i].sym.ast
if default.kind == nkEmpty:
localError(n.info, errWrongNumberOfArguments) localError(n.info, errWrongNumberOfArguments)
addSon(result, arg) result.addSon default.copyTree
for i in 1 .. genericParams:
result.addSon n.sons[givenRegularParams + i]
var evalTemplateCounter* = 0 var evalTemplateCounter* = 0
# to prevent endless recursion in templates instantiation # to prevent endless recursion in templates instantiation

View file

@ -277,16 +277,27 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
styleCheckUse(n.sons[0].info, finalCallee) styleCheckUse(n.sons[0].info, finalCallee)
if finalCallee.ast == nil: if finalCallee.ast == nil:
internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check! internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check!
if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty: if x.hasFauxMatch:
# a generic proc!
if not x.proxyMatch:
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
else:
result = x.call result = x.call
result.sons[0] = newSymNode(finalCallee, result.sons[0].info) result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
result.typ = finalCallee.typ.sons[0] if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
if containsGenericType(result.typ): result.typ = errorType(c) result.typ = newTypeS(x.fauxMatch, c)
return return
let gp = finalCallee.ast.sons[genericParamsPos]
if gp.kind != nkEmpty:
if x.calleeSym.kind notin {skMacro, skTemplate}:
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
else:
# For macros and templates, the resolved generic params
# are added as normal params.
for s in instantiateGenericParamList(c, gp, x.bindings):
case s.kind
of skConst:
x.call.add s.ast
of skType:
x.call.add newSymNode(s, n.info)
else:
internalAssert false
result = x.call result = x.call
instGenericConvertersSons(c, result, x) instGenericConvertersSons(c, result, x)
result.sons[0] = newSymNode(finalCallee, result.sons[0].info) result.sons[0] = newSymNode(finalCallee, result.sons[0].info)

View file

@ -268,7 +268,7 @@ proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
template rangeHasStaticIf*(t: PType): bool = template rangeHasStaticIf*(t: PType): bool =
# this accepts the ranges's node # this accepts the ranges's node
t.n[1].kind == nkStaticExpr t.n != nil and t.n.len > 1 and t.n[1].kind == nkStaticExpr
template getStaticTypeFromRange*(t: PType): PType = template getStaticTypeFromRange*(t: PType): PType =
t.n[1][0][1].typ t.n[1][0][1].typ

View file

@ -229,7 +229,7 @@ proc semConv(c: PContext, n: PNode): PNode =
return n return n
result = newNodeI(nkConv, n.info) result = newNodeI(nkConv, n.info)
var targetType = semTypeNode(c, n.sons[0], nil) var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
maybeLiftType(targetType, c, n[0].info) maybeLiftType(targetType, c, n[0].info)
result.addSon copyTree(n.sons[0]) result.addSon copyTree(n.sons[0])
var op = semExprWithType(c, n.sons[1]) var op = semExprWithType(c, n.sons[1])
@ -780,7 +780,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect) if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect)
elif t != nil and t.kind == tyTypeDesc: elif t != nil and t.kind == tyTypeDesc:
if n.len == 1: return semObjConstr(c, n, flags) if n.len == 1: return semObjConstr(c, n, flags)
let destType = t.skipTypes({tyTypeDesc, tyGenericInst})
return semConv(c, n) return semConv(c, n)
else: else:
result = overloadedCallOpr(c, n) result = overloadedCallOpr(c, n)
@ -926,8 +925,8 @@ const
proc readTypeParameter(c: PContext, typ: PType, proc readTypeParameter(c: PContext, typ: PType,
paramName: PIdent, info: TLineInfo): PNode = paramName: PIdent, info: TLineInfo): PNode =
let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias
else: (internalAssert(typ.kind == tyCompositeTypeClass); typ.sons[1]) else: (internalAssert(typ.kind == tyCompositeTypeClass);
#debug ty typ.sons[1].skipGenericAlias)
let tbody = ty.sons[0] let tbody = ty.sons[0]
for s in countup(0, tbody.len-2): for s in countup(0, tbody.len-2):
let tParam = tbody.sons[s] let tParam = tbody.sons[s]

View file

@ -10,14 +10,10 @@
# This module implements the instantiation of generic procs. # This module implements the instantiation of generic procs.
# included from sem.nim # included from sem.nim
proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym =
entry: var TInstantiation) = internalAssert n.kind == nkGenericParams
if n.kind != nkGenericParams:
internalError(n.info, "instantiateGenericParamList; no generic params")
newSeq(entry.concreteTypes, n.len)
for i, a in n.pairs: for i, a in n.pairs:
if a.kind != nkSym: internalAssert a.kind == nkSym
internalError(a.info, "instantiateGenericParamList; no symbol")
var q = a.sym var q = a.sym
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses: if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
continue continue
@ -42,8 +38,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
#t = ReplaceTypeVarsT(cl, t) #t = ReplaceTypeVarsT(cl, t)
s.typ = t s.typ = t
if t.kind == tyStatic: s.ast = t.n if t.kind == tyStatic: s.ast = t.n
addDecl(c, s) yield s
entry.concreteTypes[i] = t
proc sameInstantiation(a, b: TInstantiation): bool = proc sameInstantiation(a, b: TInstantiation): bool =
if a.concreteTypes.len == b.concreteTypes.len: if a.concreteTypes.len == b.concreteTypes.len:
@ -196,7 +191,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
## The `pt` parameter is a type-unsafe mapping table used to link generic ## The `pt` parameter is a type-unsafe mapping table used to link generic
## parameters to their concrete types within the generic instance. ## parameters to their concrete types within the generic instance.
# no need to instantiate generic templates/macros: # no need to instantiate generic templates/macros:
if fn.kind in {skTemplate, skMacro}: return fn internalAssert fn.kind notin {skMacro, skTemplate}
# generates an instantiated proc # generates an instantiated proc
if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep") if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
inc(c.instCounter) inc(c.instCounter)
@ -213,12 +208,18 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
result.ast = n result.ast = n
pushOwner(result) pushOwner(result)
openScope(c) openScope(c)
internalAssert n.sons[genericParamsPos].kind != nkEmpty let gp = n.sons[genericParamsPos]
internalAssert gp.kind != nkEmpty
n.sons[namePos] = newSymNode(result) n.sons[namePos] = newSymNode(result)
pushInfoContext(info) pushInfoContext(info)
var entry = TInstantiation.new var entry = TInstantiation.new
entry.sym = result entry.sym = result
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[]) newSeq(entry.concreteTypes, gp.len)
var i = 0
for s in instantiateGenericParamList(c, gp, pt):
addDecl(c, s)
entry.concreteTypes[i] = s.typ
inc i
pushProcCon(c, result) pushProcCon(c, result)
instantiateProcType(c, pt, result, info) instantiateProcType(c, pt, result, info)
n.sons[genericParamsPos] = ast.emptyNode n.sons[genericParamsPos] = ast.emptyNode

View file

@ -1262,10 +1262,14 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
return return
else: else:
n.sons[i] = semExpr(c, n.sons[i]) n.sons[i] = semExpr(c, n.sons[i])
if c.inTypeClass > 0 and n[i].typ != nil and n[i].typ.kind == tyBool: if c.inTypeClass > 0 and n[i].typ != nil:
case n[i].typ.kind
of tyBool:
let verdict = semConstExpr(c, n[i]) let verdict = semConstExpr(c, n[i])
if verdict.intVal == 0: if verdict.intVal == 0:
localError(result.info, "type class predicate failed") localError(result.info, "type class predicate failed")
of tyUnknown: continue
else: discard
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]): if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
voidContext = true voidContext = true
n.typ = enforceVoidContext n.typ = enforceVoidContext

View file

@ -225,11 +225,10 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
elif e.kind == nkSym and e.typ.kind == tyStatic: elif e.kind == nkSym and e.typ.kind == tyStatic:
if e.sym.ast != nil: if e.sym.ast != nil:
return semArrayIndex(c, e.sym.ast) return semArrayIndex(c, e.sym.ast)
internalAssert c.inGenericContext > 0
if not isOrdinalType(e.typ.lastSon): if not isOrdinalType(e.typ.lastSon):
localError(n[1].info, errOrdinalTypeExpected) localError(n[1].info, errOrdinalTypeExpected)
result = makeRangeWithStaticExpr(c, e) result = makeRangeWithStaticExpr(c, e)
result.flags.incl tfUnresolved if c.inGenericContext >0: result.flags.incl tfUnresolved
elif e.kind in nkCallKinds and hasGenericArguments(e): elif e.kind in nkCallKinds and hasGenericArguments(e):
if not isOrdinalType(e.typ): if not isOrdinalType(e.typ):
localError(n[1].info, errOrdinalTypeExpected) localError(n[1].info, errOrdinalTypeExpected)
@ -782,9 +781,11 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
of tyGenericBody: of tyGenericBody:
result = newTypeS(tyGenericInvokation, c) result = newTypeS(tyGenericInvokation, c)
result.rawAddSon(paramType) result.rawAddSon(paramType)
for i in 0 .. paramType.sonsLen - 2: for i in 0 .. paramType.sonsLen - 2:
result.rawAddSon newTypeS(tyAnything, c) let dummyType = if paramType.sons[i].kind == tyStatic: tyUnknown
# result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) else: tyAnything
result.rawAddSon newTypeS(dummyType, c)
if paramType.lastSon.kind == tyUserTypeClass: if paramType.lastSon.kind == tyUserTypeClass:
result.kind = tyUserTypeClassInst result.kind = tyUserTypeClassInst

View file

@ -39,7 +39,9 @@ type
bindings*: TIdTable # maps types to types bindings*: TIdTable # maps types to types
baseTypeMatch: bool # needed for conversions from T to openarray[T] baseTypeMatch: bool # needed for conversions from T to openarray[T]
# for example # for example
proxyMatch*: bool # to prevent instantiations fauxMatch*: TTypeKind # the match was successful only due to the use
# of error or wildcard (unknown) types.
# this is used to prevent instantiations.
genericConverter*: bool # true if a generic converter needs to genericConverter*: bool # true if a generic converter needs to
# be instantiated # be instantiated
coerceDistincts*: bool # this is an explicit coercion that can strip away coerceDistincts*: bool # this is an explicit coercion that can strip away
@ -66,6 +68,8 @@ const
proc markUsed*(info: TLineInfo, s: PSym) proc markUsed*(info: TLineInfo, s: PSym)
template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone
proc initCandidateAux(ctx: PContext, proc initCandidateAux(ctx: PContext,
c: var TCandidate, callee: PType) {.inline.} = c: var TCandidate, callee: PType) {.inline.} =
c.c = ctx c.c = ctx
@ -109,9 +113,12 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1): for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
var formalTypeParam = typeParams.sons[i-1].typ var formalTypeParam = typeParams.sons[i-1].typ
var bound = binding[i].typ var bound = binding[i].typ
if bound != nil and formalTypeParam.kind != tyTypeDesc: internalAssert bound != nil
if formalTypeParam.kind == tyTypeDesc:
if bound.kind != tyTypeDesc:
bound = makeTypeDesc(ctx, bound)
else:
bound = bound.skipTypes({tyTypeDesc}) bound = bound.skipTypes({tyTypeDesc})
assert bound != nil
put(c.bindings, formalTypeParam, bound) put(c.bindings, formalTypeParam, bound)
proc newCandidate*(ctx: PContext, callee: PSym, proc newCandidate*(ctx: PContext, callee: PSym,
@ -462,9 +469,23 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
var var
typeParamName = ff.base.sons[i-1].sym.name typeParamName = ff.base.sons[i-1].sym.name
typ = ff.sons[i] typ = ff.sons[i]
param = newSym(skType, typeParamName, body.sym, body.sym.info) param: PSym
template paramSym(kind): expr =
newSym(kind, typeParamName, body.sym, body.sym.info)
case typ.kind
of tyStatic:
param = paramSym skConst
param.typ = typ.base
param.ast = typ.n
of tyUnknown:
param = paramSym skVar
param.typ = typ
else:
param = paramSym skType
param.typ = makeTypeDesc(c, typ) param.typ = makeTypeDesc(c, typ)
addDecl(c, param) addDecl(c, param)
for param in body.n[0]: for param in body.n[0]:
@ -626,6 +647,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype
of tyRange: of tyRange:
if a.kind == f.kind: if a.kind == f.kind:
if f.base.kind == tyNone: return isGeneric
result = typeRel(c, base(f), base(a)) result = typeRel(c, base(f), base(a))
# bugfix: accept integer conversions here # bugfix: accept integer conversions here
#if result < isGeneric: result = isNone #if result < isGeneric: result = isNone
@ -672,10 +694,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: else:
fRange = prev fRange = prev
result = typeRel(c, f.sons[1], a.sons[1]) result = typeRel(c, f.sons[1], a.sons[1])
if result < isGeneric: if result < isGeneric: return isNone
result = isNone if rangeHasStaticIf(fRange):
elif tfUnresolved in fRange.flags and if tfUnresolved in fRange.flags:
rangeHasStaticIf(fRange):
# This is a range from an array instantiated with a generic # This is a range from an array instantiated with a generic
# static param. We must extract the static param here and bind # static param. We must extract the static param here and bind
# it to the size of the currently supplied array. # it to the size of the currently supplied array.
@ -687,7 +708,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# ranges and static params: # ranges and static params:
replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1) replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1)
put(c.bindings, rangeStaticT, replacementT) put(c.bindings, rangeStaticT, replacementT)
result = isGeneric return isGeneric
let len = tryResolvingStaticExpr(c, fRange.n[1])
if len.kind == nkIntLit and len.intVal+1 == lengthOrd(a):
return # if we get this far, the result is already good
else:
return isNone
elif lengthOrd(fRange) != lengthOrd(a): elif lengthOrd(fRange) != lengthOrd(a):
result = isNone result = isNone
else: discard else: discard
@ -945,7 +972,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: else:
internalAssert a.sons != nil and a.sons.len > 0 internalAssert a.sons != nil and a.sons.len > 0
c.typedescMatched = true c.typedescMatched = true
result = typeRel(c, f.base, a.base) result = typeRel(c, f.base, a.skipTypes({tyGenericParam, tyTypeDesc}))
else: else:
result = isNone result = isNone
else: else:
@ -977,14 +1004,18 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = typeRel(c, x, a) # check if it fits result = typeRel(c, x, a) # check if it fits
of tyStatic: of tyStatic:
let prev = PType(idTableGet(c.bindings, f))
if prev == nil:
if aOrig.kind == tyStatic: if aOrig.kind == tyStatic:
result = typeRel(c, f.lastSon, a) result = typeRel(c, f.lastSon, a)
if result != isNone and f.n != nil: if result != isNone and f.n != nil:
if not exprStructuralEquivalent(f.n, a.n): if not exprStructuralEquivalent(f.n, aOrig.n):
result = isNone result = isNone
if result != isNone: put(c.bindings, f, aOrig) if result != isNone: put(c.bindings, f, aOrig)
else: else:
result = isNone result = isNone
else:
result = typeRel(c, prev, aOrig)
of tyTypeDesc: of tyTypeDesc:
var prev = PType(idTableGet(c.bindings, f)) var prev = PType(idTableGet(c.bindings, f))
@ -1024,6 +1055,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyFromExpr: of tyFromExpr:
# fix the expression, so it contains the already instantiated types # fix the expression, so it contains the already instantiated types
if f.n == nil: return isGeneric
let reevaluated = tryResolvingStaticExpr(c, f.n) let reevaluated = tryResolvingStaticExpr(c, f.n)
case reevaluated.typ.kind case reevaluated.typ.kind
of tyTypeDesc: of tyTypeDesc:
@ -1058,7 +1090,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
c: PContext): PNode = c: PContext): PNode =
result = newNodeI(kind, arg.info) result = newNodeI(kind, arg.info)
if containsGenericType(f): if containsGenericType(f):
if not m.proxyMatch: if not m.hasFauxMatch:
result.typ = getInstantiatedType(c, arg, m, f) result.typ = getInstantiatedType(c, arg, m, f)
else: else:
result.typ = errorType(c) result.typ = errorType(c)
@ -1144,7 +1176,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
return argSemantized return argSemantized
if argType.kind == tyStatic: if argType.kind == tyStatic:
if m.callee.kind == tyGenericBody: if m.callee.kind == tyGenericBody and tfGenericTypeParam notin argType.flags:
result = newNodeI(nkType, argOrig.info) result = newNodeI(nkType, argOrig.info)
result.typ = makeTypeFromExpr(c, arg) result.typ = makeTypeFromExpr(c, arg)
return return
@ -1237,9 +1269,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
of isNone: of isNone:
# do not do this in ``typeRel`` as it then can't infere T in ``ref T``: # do not do this in ``typeRel`` as it then can't infere T in ``ref T``:
if a.kind == tyProxy: if a.kind in {tyProxy, tyUnknown}:
inc(m.genericMatches) inc(m.genericMatches)
m.proxyMatch = true m.fauxMatch = a.kind
return copyTree(arg) return copyTree(arg)
result = userConvMatch(c, m, f, a, arg) result = userConvMatch(c, m, f, a, arg)
# check for a base type match, which supports varargs[T] without [] # check for a base type match, which supports varargs[T] without []

View file

@ -1417,12 +1417,20 @@ proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) =
proc setupCompileTimeVar*(module: PSym, n: PNode) = proc setupCompileTimeVar*(module: PSym, n: PNode) =
discard evalConstExprAux(module, nil, n, emStaticStmt) discard evalConstExprAux(module, nil, n, emStaticStmt)
proc setupMacroParam(x: PNode): PNode = proc setupMacroParam(x: PNode, typ: PType): TFullReg =
result = x case typ.kind
if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1] of tyStatic:
result = canonValue(result) putIntoReg(result, x)
result.flags.incl nfIsRef of tyTypeDesc:
result.typ = x.typ putIntoReg(result, x)
else:
result.kind = rkNode
var n = x
if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n.sons[1]
n = n.canonValue
n.flags.incl nfIsRef
n.typ = x.typ
result.node = n
var evalMacroCounter: int var evalMacroCounter: int
@ -1442,6 +1450,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
c.callsite = nOrig c.callsite = nOrig
let start = genProc(c, sym) let start = genProc(c, sym)
# c.echoCode start
var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
let maxSlots = sym.offset let maxSlots = sym.offset
@ -1457,9 +1466,14 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
tos.slots[0].kind = rkNode tos.slots[0].kind = rkNode
tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0]) tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0])
# setup parameters: # setup parameters:
for i in 1 .. < min(tos.slots.len, L): for i in 1.. <sym.typ.len:
tos.slots[i].kind = rkNode tos.slots[i] = setupMacroParam(n.sons[i], sym.typ.sons[i])
tos.slots[i].node = setupMacroParam(n.sons[i])
let gp = sym.ast[genericParamsPos]
for i in 0 .. <gp.len:
let idx = sym.typ.len + i
tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
# temporary storage: # temporary storage:
#for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty) #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
result = rawExecute(c, start, tos).regToNode result = rawExecute(c, start, tos).regToNode

View file

@ -164,7 +164,8 @@ proc getSlotKind(t: PType): TSlotKind =
const const
HighRegisterPressure = 40 HighRegisterPressure = 40
proc getTemp(c: PCtx; typ: PType): TRegister = proc getTemp(c: PCtx; tt: PType): TRegister =
let typ = tt.safeSkipTypes({tyStatic})
let c = c.prc let c = c.prc
# we prefer the same slot kind here for efficiency. Unfortunately for # we prefer the same slot kind here for efficiency. Unfortunately for
# discardable return types we may not know the desired type. This can happen # discardable return types we may not know the desired type. This can happen
@ -685,7 +686,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opc, dest, tmp) c.gABC(n, opc, dest, tmp)
c.gABx(n, opc, 0, genType(c, n.typ)) c.gABx(n, opc, 0, genType(c, n.typ))
c.gABx(n, opc, 0, genType(c, arg.typ)) c.gABx(n, opc, 0, genType(c, arg.typ.skipTypes({tyStatic})))
c.freeTemp(tmp) c.freeTemp(tmp)
proc genCard(c: PCtx; n: PNode; dest: var TDest) = proc genCard(c: PCtx; n: PNode; dest: var TDest) =
@ -1085,7 +1086,8 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
c.freeTemp(tmp) c.freeTemp(tmp)
proc whichAsgnOpc(n: PNode): TOpcode = proc whichAsgnOpc(n: PNode): TOpcode =
case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind let toSkip = abstractRange-{tyTypeDesc}
case n.typ.skipTypes(toSkip).kind
of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
opcAsgnInt opcAsgnInt
of tyString, tyCString: of tyString, tyCString:
@ -1559,6 +1561,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
c.gABx(n, opcLdConst, dest, lit) c.gABx(n, opcLdConst, dest, lit)
of skType: of skType:
genTypeLit(c, s.typ, dest) genTypeLit(c, s.typ, dest)
of skGenericParam:
if c.prc.sym.kind == skMacro:
genRdVar(c, n, dest, flags)
else:
internalError(n.info, "cannot generate code for: " & s.name.s)
else: else:
internalError(n.info, "cannot generate code for: " & s.name.s) internalError(n.info, "cannot generate code for: " & s.name.s)
of nkCallKinds: of nkCallKinds:
@ -1690,6 +1697,14 @@ proc genParams(c: PCtx; params: PNode) =
c.prc.slots[i] = (inUse: true, kind: slotFixedLet) c.prc.slots[i] = (inUse: true, kind: slotFixedLet)
c.prc.maxSlots = max(params.len, 1) c.prc.maxSlots = max(params.len, 1)
proc genGenericParams(c: PCtx; gp: PNode) =
var base = c.prc.maxSlots
for i in 0.. <gp.len:
var param = gp.sons[i].sym
param.position = base + i # XXX: fix this earlier; make it consistent with templates
c.prc.slots[base + i] = (inUse: true, kind: slotFixedLet)
c.prc.maxSlots = base + gp.len
proc finalJumpTarget(c: PCtx; pc, diff: int) = proc finalJumpTarget(c: PCtx; pc, diff: int) =
internalAssert(-0x7fff < diff and diff < 0x7fff) internalAssert(-0x7fff < diff and diff < 0x7fff)
let oldInstr = c.code[pc] let oldInstr = c.code[pc]
@ -1761,6 +1776,8 @@ proc genProc(c: PCtx; s: PSym): int =
c.prc = p c.prc = p
# iterate over the parameters and allocate space for them: # iterate over the parameters and allocate space for them:
genParams(c, s.typ.n) genParams(c, s.typ.n)
if s.kind == skMacro and s.ast[genericParamsPos].kind != nkEmpty:
genGenericParams(c, s.ast[genericParamsPos])
if tfCapturesEnv in s.typ.flags: if tfCapturesEnv in s.typ.flags:
#let env = s.ast.sons[paramsPos].lastSon.sym #let env = s.ast.sons[paramsPos].lastSon.sym
#assert env.position == 2 #assert env.position == 2

View file

@ -86,6 +86,7 @@ $moduledesc
$content $content
""" """
# * $analytics: Google analytics location, includes <script> tags
doc.file = """<?xml version="1.0" encoding="utf-8" ?> doc.file = """<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
@ -1243,6 +1244,7 @@ dt pre > span.Operator ~ span.Identifier, dt pre > span.Operator ~ span.Operator
</div> </div>
</div> </div>
</div> </div>
$analytics
</body> </body>
</html> </html>
""" """

View file

@ -37,4 +37,4 @@ Options:
--advanced show advanced command line switches --advanced show advanced command line switches
-h, --help show this help -h, --help show this help
Note: Even single letter options require the colon: -p:PATH. Note, single letter options that take an argument require a colon. E.g. -p:PATH.

View file

@ -145,7 +145,7 @@ The ``--usages`` idetools switch lists all usages of the symbol at
a position. IDEs can use this to find all the places in the file a position. IDEs can use this to find all the places in the file
where the symbol is used and offer the user to rename it in all where the symbol is used and offer the user to rename it in all
places at the same time. Again, a pure string based search and places at the same time. Again, a pure string based search and
replace may catch symbols out of the scope of a funcion/loop. replace may catch symbols out of the scope of a function/loop.
For this kind of query the IDE will most likely ignore all the For this kind of query the IDE will most likely ignore all the
type/signature info provided by idetools and concentrate on the type/signature info provided by idetools and concentrate on the

View file

@ -390,7 +390,7 @@ string from a cstring:
.. code-block:: nim .. code-block:: nim
var str: string = "Hello!" var str: string = "Hello!"
var cstr: cstring = s var cstr: cstring = str
var newstr: string = $cstr var newstr: string = $cstr

View file

@ -1070,7 +1070,7 @@ Operation Comment
``dec(x, n)`` decrements `x` by `n`; `n` is an integer ``dec(x, n)`` decrements `x` by `n`; `n` is an integer
``succ(x)`` returns the successor of `x` ``succ(x)`` returns the successor of `x`
``succ(x, n)`` returns the `n`'th successor of `x` ``succ(x, n)`` returns the `n`'th successor of `x`
``prec(x)`` returns the predecessor of `x` ``pred(x)`` returns the predecessor of `x`
``pred(x, n)`` returns the `n`'th predecessor of `x` ``pred(x, n)`` returns the `n`'th predecessor of `x`
----------------- -------------------------------------------------------- ----------------- --------------------------------------------------------
@ -1323,7 +1323,7 @@ define operators which accept TSlice objects to define ranges.
a = "Nim is a progamming language" a = "Nim is a progamming language"
b = "Slices are useless." b = "Slices are useless."
echo a[10..15] # --> 'a prog' echo a[7..12] # --> 'a prog'
b[11.. -2] = "useful" b[11.. -2] = "useful"
echo b # --> 'Slices are useful.' echo b # --> 'Slices are useful.'

View file

@ -35,7 +35,7 @@ Object Oriented Programming
=========================== ===========================
While Nim's support for object oriented programming (OOP) is minimalistic, While Nim's support for object oriented programming (OOP) is minimalistic,
powerful OOP technics can be used. OOP is seen as *one* way to design a powerful OOP techniques can be used. OOP is seen as *one* way to design a
program, not *the only* way. Often a procedural approach leads to simpler program, not *the only* way. Often a procedural approach leads to simpler
and more efficient code. In particular, prefering composition over inheritance and more efficient code. In particular, prefering composition over inheritance
is often the better design. is often the better design.
@ -56,7 +56,7 @@ Objects have access to their type at runtime. There is an
.. code-block:: nim .. code-block:: nim
type type
TPerson = object of TObject TPerson = object of RootObj
name*: string # the * means that `name` is accessible from other modules name*: string # the * means that `name` is accessible from other modules
age: int # no * means that the field is hidden from other modules age: int # no * means that the field is hidden from other modules
@ -76,10 +76,10 @@ never *equivalent*. New object types can only be defined within a type
section. section.
Inheritance is done with the ``object of`` syntax. Multiple inheritance is Inheritance is done with the ``object of`` syntax. Multiple inheritance is
currently not supported. If an object type has no suitable ancestor, ``TObject`` currently not supported. If an object type has no suitable ancestor, ``RootObj``
can be used as its ancestor, but this is only a convention. Objects that have can be used as its ancestor, but this is only a convention. Objects that have
no ancestor are implicitly ``final``. You can use the ``inheritable`` pragma no ancestor are implicitly ``final``. You can use the ``inheritable`` pragma
to introduce new object roots apart from ``system.TObject``. (This is used to introduce new object roots apart from ``system.RootObj``. (This is used
in the GTK wrapper for instance.) in the GTK wrapper for instance.)
@ -228,7 +228,7 @@ is needed:
.. code-block:: nim .. code-block:: nim
type type
TSocket* = object of TObject TSocket* = object of RootObj
FHost: int # cannot be accessed from the outside of the module FHost: int # cannot be accessed from the outside of the module
# the `F` prefix is a convention to avoid clashes since # the `F` prefix is a convention to avoid clashes since
# the accessors are named `host` # the accessors are named `host`
@ -284,7 +284,7 @@ Procedures always use static dispatch. For dynamic dispatch replace the
.. code-block:: nim .. code-block:: nim
type type
PExpr = ref object of TObject ## abstract base class for an expression PExpr = ref object of RootObj ## abstract base class for an expression
PLiteral = ref object of PExpr PLiteral = ref object of PExpr
x: int x: int
PPlusExpr = ref object of PExpr PPlusExpr = ref object of PExpr
@ -313,7 +313,7 @@ dispatching:
.. code-block:: nim .. code-block:: nim
type type
TThing = object of TObject TThing = object of RootObj
TUnit = object of TThing TUnit = object of TThing
x: int x: int

View file

@ -878,6 +878,10 @@ else:
let data = PData(info.key.data) let data = PData(info.key.data)
assert data.fd == info.key.fd.TAsyncFD assert data.fd == info.key.fd.TAsyncFD
#echo("In poll ", data.fd.cint) #echo("In poll ", data.fd.cint)
if EvError in info.events:
closeSocket(data.fd)
continue
if EvRead in info.events: if EvRead in info.events:
# Callback may add items to ``data.readCBs`` which causes issues if # Callback may add items to ``data.readCBs`` which causes issues if
# we are iterating over ``data.readCBs`` at the same time. We therefore # we are iterating over ``data.readCBs`` at the same time. We therefore

View file

@ -17,6 +17,8 @@
## as the response body. ## as the response body.
## ##
## .. code-block::nim ## .. code-block::nim
## import asynchttpserver, asyncdispatch
##
## var server = newAsyncHttpServer() ## var server = newAsyncHttpServer()
## proc cb(req: Request) {.async.} = ## proc cb(req: Request) {.async.} =
## await req.respond(Http200, "Hello World") ## await req.respond(Http200, "Hello World")

View file

@ -32,21 +32,12 @@
## the server. ## the server.
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L" ## var data = newMultipartData()
## var body: string = "--xyz\c\L" ## data["output"] = "soap12"
## # soap 1.2 output ## data["uploaded_file"] = ("test.html", "text/html",
## body.add("Content-Disposition: form-data; name=\"output\"\c\L") ## "<html><head></head><body><p>test</p></body></html>")
## body.add("\c\Lsoap12\c\L")
## ##
## # html ## echo postContent("http://validator.w3.org/check", multipart=data)
## body.add("--xyz\c\L")
## body.add("Content-Disposition: form-data; name=\"uploaded_file\";" &
## " filename=\"test.html\"\c\L")
## body.add("Content-Type: text/html\c\L")
## body.add("\c\L<html><head></head><body><p>test</p></body></html>\c\L")
## body.add("--xyz--")
##
## echo(postContent("http://validator.w3.org/check", headers, body))
## ##
## Asynchronous HTTP requests ## Asynchronous HTTP requests
## ========================== ## ==========================
@ -88,7 +79,7 @@
## constructor should be used for this purpose. However, ## constructor should be used for this purpose. However,
## currently only basic authentication is supported. ## currently only basic authentication is supported.
import net, strutils, uri, parseutils, strtabs, base64, os import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes, math
import asyncnet, asyncdispatch import asyncnet, asyncdispatch
import rawsockets import rawsockets
@ -103,6 +94,10 @@ type
url*: Uri url*: Uri
auth*: string auth*: string
MultipartEntries* = openarray[tuple[name, content: string]]
MultipartData* = ref object
content: seq[string]
ProtocolError* = object of IOError ## exception that is raised when server ProtocolError* = object of IOError ## exception that is raised when server
## does not conform to the implemented ## does not conform to the implemented
## protocol ## protocol
@ -282,6 +277,109 @@ proc newProxy*(url: string, auth = ""): Proxy =
## Constructs a new ``TProxy`` object. ## Constructs a new ``TProxy`` object.
result = Proxy(url: parseUri(url), auth: auth) result = Proxy(url: parseUri(url), auth: auth)
proc newMultipartData*: MultipartData =
## Constructs a new ``MultipartData`` object.
MultipartData(content: @[])
proc add*(p: var MultipartData, name, content: string, filename: string = nil,
contentType: string = nil) =
## Add a value to the multipart data. Raises a `ValueError` exception if
## `name`, `filename` or `contentType` contain newline characters.
if {'\c','\L'} in name:
raise newException(ValueError, "name contains a newline character")
if filename != nil and {'\c','\L'} in filename:
raise newException(ValueError, "filename contains a newline character")
if contentType != nil and {'\c','\L'} in contentType:
raise newException(ValueError, "contentType contains a newline character")
var str = "Content-Disposition: form-data; name=\"" & name & "\""
if filename != nil:
str.add("; filename=\"" & filename & "\"")
str.add("\c\L")
if contentType != nil:
str.add("Content-Type: " & contentType & "\c\L")
str.add("\c\L" & content & "\c\L")
p.content.add(str)
proc add*(p: var MultipartData, xs: MultipartEntries): MultipartData
{.discardable.} =
## Add a list of multipart entries to the multipart data `p`. All values are
## added without a filename and without a content type.
##
## .. code-block:: Nim
## data.add({"action": "login", "format": "json"})
for name, content in xs.items:
p.add(name, content)
result = p
proc newMultipartData*(xs: MultipartEntries): MultipartData =
## Create a new multipart data object and fill it with the entries `xs`
## directly.
##
## .. code-block:: Nim
## var data = newMultipartData({"action": "login", "format": "json"})
result = MultipartData(content: @[])
result.add(xs)
proc addFiles*(p: var MultipartData, xs: openarray[tuple[name, file: string]]):
MultipartData {.discardable.} =
## Add files to a multipart data object. The file will be opened from your
## disk, read and sent with the automatically determined MIME type. Raises an
## `IOError` if the file cannot be opened or reading fails. To manually
## specify file content, filename and MIME type, use `[]=` instead.
##
## .. code-block:: Nim
## data.addFiles({"uploaded_file": "public/test.html"})
var m = newMimetypes()
for name, file in xs.items:
var contentType: string
let (dir, fName, ext) = splitFile(file)
if ext.len > 0:
contentType = m.getMimetype(ext[1..ext.high], nil)
p.add(name, readFile(file), fName & ext, contentType)
result = p
proc `[]=`*(p: var MultipartData, name, content: string) =
## Add a multipart entry to the multipart data `p`. The value is added
## without a filename and without a content type.
##
## .. code-block:: Nim
## data["username"] = "NimUser"
p.add(name, content)
proc `[]=`*(p: var MultipartData, name: string,
file: tuple[name, contentType, content: string]) =
## Add a file to the multipart data `p`, specifying filename, contentType and
## content manually.
##
## .. code-block:: Nim
## data["uploaded_file"] = ("test.html", "text/html",
## "<html><head></head><body><p>test</p></body></html>")
p.add(name, file.content, file.name, file.contentType)
proc format(p: MultipartData): tuple[header, body: string] =
if p == nil or p.content == nil or p.content.len == 0:
return ("", "")
# Create boundary that is not in the data to be formatted
var bound: string
while true:
bound = $random(int.high)
var found = false
for s in p.content:
if bound in s:
found = true
if not found:
break
result.header = "Content-Type: multipart/form-data; boundary=" & bound & "\c\L"
result.body = ""
for s in p.content:
result.body.add("--" & bound & "\c\L" & s)
result.body.add("--" & bound & "--\c\L")
proc request*(url: string, httpMethod = httpGET, extraHeaders = "", proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
body = "", body = "",
sslContext: SSLContext = defaultSSLContext, sslContext: SSLContext = defaultSSLContext,
@ -294,7 +392,9 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
var r = if proxy == nil: parseUri(url) else: proxy.url var r = if proxy == nil: parseUri(url) else: proxy.url
var headers = substr($httpMethod, len("http")) var headers = substr($httpMethod, len("http"))
if proxy == nil: if proxy == nil:
headers.add(" /" & r.path & r.query) headers.add(" " & r.path)
if r.query.len > 0:
headers.add("?" & r.query)
else: else:
headers.add(" " & url) headers.add(" " & url)
@ -387,15 +487,30 @@ proc post*(url: string, extraHeaders = "", body = "",
maxRedirects = 5, maxRedirects = 5,
sslContext: SSLContext = defaultSSLContext, sslContext: SSLContext = defaultSSLContext,
timeout = -1, userAgent = defUserAgent, timeout = -1, userAgent = defUserAgent,
proxy: Proxy = nil): Response = proxy: Proxy = nil,
multipart: MultipartData = nil): Response =
## | POSTs ``body`` to the ``url`` and returns a ``Response`` object. ## | POSTs ``body`` to the ``url`` and returns a ``Response`` object.
## | This proc adds the necessary Content-Length header. ## | This proc adds the necessary Content-Length header.
## | This proc also handles redirection. ## | This proc also handles redirection.
## | Extra headers can be specified and must be separated by ``\c\L``. ## | Extra headers can be specified and must be separated by ``\c\L``.
## | An optional timeout can be specified in miliseconds, if reading from the ## | An optional timeout can be specified in miliseconds, if reading from the
## server takes longer than specified an ETimeout exception will be raised. ## server takes longer than specified an ETimeout exception will be raised.
var xh = extraHeaders & "Content-Length: " & $len(body) & "\c\L" ## | The optional ``multipart`` parameter can be used to create
result = request(url, httpPOST, xh, body, sslContext, timeout, userAgent, ## ``multipart/form-data`` POSTs comfortably.
let (mpHeaders, mpBody) = format(multipart)
template withNewLine(x): expr =
if x.len > 0 and not x.endsWith("\c\L"):
x & "\c\L"
else:
x
var xb = mpBody.withNewLine() & body.withNewLine()
var xh = extraHeaders.withNewLine() & mpHeaders.withNewLine() &
withNewLine("Content-Length: " & $len(xb))
result = request(url, httpPOST, xh, xb, sslContext, timeout, userAgent,
proxy) proxy)
var lastUrl = "" var lastUrl = ""
for i in 1..maxRedirects: for i in 1..maxRedirects:
@ -410,14 +525,17 @@ proc postContent*(url: string, extraHeaders = "", body = "",
maxRedirects = 5, maxRedirects = 5,
sslContext: SSLContext = defaultSSLContext, sslContext: SSLContext = defaultSSLContext,
timeout = -1, userAgent = defUserAgent, timeout = -1, userAgent = defUserAgent,
proxy: Proxy = nil): string = proxy: Proxy = nil,
multipart: MultipartData = nil): string =
## | POSTs ``body`` to ``url`` and returns the response's body as a string ## | POSTs ``body`` to ``url`` and returns the response's body as a string
## | Raises exceptions for the status codes ``4xx`` and ``5xx`` ## | Raises exceptions for the status codes ``4xx`` and ``5xx``
## | Extra headers can be specified and must be separated by ``\c\L``. ## | Extra headers can be specified and must be separated by ``\c\L``.
## | An optional timeout can be specified in miliseconds, if reading from the ## | An optional timeout can be specified in miliseconds, if reading from the
## server takes longer than specified an ETimeout exception will be raised. ## server takes longer than specified an ETimeout exception will be raised.
## | The optional ``multipart`` parameter can be used to create
## ``multipart/form-data`` POSTs comfortably.
var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout, var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout,
userAgent, proxy) userAgent, proxy, multipart)
if r.status[0] in {'4','5'}: if r.status[0] in {'4','5'}:
raise newException(HttpRequestError, r.status) raise newException(HttpRequestError, r.status)
else: else:
@ -442,7 +560,9 @@ proc generateHeaders(r: Uri, httpMethod: HttpMethod,
headers: StringTableRef): string = headers: StringTableRef): string =
result = substr($httpMethod, len("http")) result = substr($httpMethod, len("http"))
# TODO: Proxies # TODO: Proxies
result.add(" /" & r.path & r.query) result.add(" " & r.path)
if r.query.len > 0:
result.add("?" & r.query)
result.add(" HTTP/1.1\c\L") result.add(" HTTP/1.1\c\L")
add(result, "Host: " & r.hostname & "\c\L") add(result, "Host: " & r.hostname & "\c\L")
@ -706,18 +826,9 @@ when isMainModule:
#var r = get("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com& #var r = get("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com&
# charset=%28detect+automatically%29&doctype=Inline&group=0") # charset=%28detect+automatically%29&doctype=Inline&group=0")
var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L" var data = newMultipartData()
var body: string = "--xyz\c\L" data["output"] = "soap12"
# soap 1.2 output data["uploaded_file"] = ("test.html", "text/html",
body.add("Content-Disposition: form-data; name=\"output\"\c\L") "<html><head></head><body><p>test</p></body></html>")
body.add("\c\Lsoap12\c\L")
# html echo postContent("http://validator.w3.org/check", multipart=data)
body.add("--xyz\c\L")
body.add("Content-Disposition: form-data; name=\"uploaded_file\";" &
" filename=\"test.html\"\c\L")
body.add("Content-Type: text/html\c\L")
body.add("\c\L<html><head></head><body><p>test</p></body></html>\c\L")
body.add("--xyz--")
echo(postContent("http://validator.w3.org/check", headers, body))

View file

@ -9,15 +9,17 @@
## Module for computing MD5 checksums. ## Module for computing MD5 checksums.
import unsigned
type type
MD5State = array[0..3, int32] MD5State = array[0..3, uint32]
MD5Block = array[0..15, int32] MD5Block = array[0..15, uint32]
MD5CBits = array[0..7, int8] MD5CBits = array[0..7, uint8]
MD5Digest* = array[0..15, int8] MD5Digest* = array[0..15, uint8]
MD5Buffer = array[0..63, int8] MD5Buffer = array[0..63, uint8]
MD5Context* {.final.} = object MD5Context* {.final.} = object
state: MD5State state: MD5State
count: array[0..1, int32] count: array[0..1, uint32]
buffer: MD5Buffer buffer: MD5Buffer
const const
@ -31,57 +33,57 @@ const
"\0\0\0\0\0\0\0\0" & "\0\0\0\0\0\0\0\0" &
"\0\0\0\0" "\0\0\0\0"
proc F(x, y, z: int32): int32 {.inline.} = proc F(x, y, z: uint32): uint32 {.inline.} =
result = (x and y) or ((not x) and z) result = (x and y) or ((not x) and z)
proc G(x, y, z: int32): int32 {.inline.} = proc G(x, y, z: uint32): uint32 {.inline.} =
result = (x and z) or (y and (not z)) result = (x and z) or (y and (not z))
proc H(x, y, z: int32): int32 {.inline.} = proc H(x, y, z: uint32): uint32 {.inline.} =
result = x xor y xor z result = x xor y xor z
proc I(x, y, z: int32): int32 {.inline.} = proc I(x, y, z: uint32): uint32 {.inline.} =
result = y xor (x or (not z)) result = y xor (x or (not z))
proc rot(x: var int32, n: int8) {.inline.} = proc rot(x: var uint32, n: uint8) {.inline.} =
x = toU32(x shl ze(n)) or (x shr toU32(32 -% ze(n))) x = (x shl n) or (x shr (32'u32 - n))
proc FF(a: var int32, b, c, d, x: int32, s: int8, ac: int32) = proc FF(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
a = a +% F(b, c, d) +% x +% ac a = a + F(b, c, d) + x + ac
rot(a, s) rot(a, s)
a = a +% b a = a + b
proc GG(a: var int32, b, c, d, x: int32, s: int8, ac: int32) = proc GG(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
a = a +% G(b, c, d) +% x +% ac a = a + G(b, c, d) + x + ac
rot(a, s) rot(a, s)
a = a +% b a = a + b
proc HH(a: var int32, b, c, d, x: int32, s: int8, ac: int32) = proc HH(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
a = a +% H(b, c, d) +% x +% ac a = a + H(b, c, d) + x + ac
rot(a, s) rot(a, s)
a = a +% b a = a + b
proc II(a: var int32, b, c, d, x: int32, s: int8, ac: int32) = proc II(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
a = a +% I(b, c, d) +% x +% ac a = a + I(b, c, d) + x + ac
rot(a, s) rot(a, s)
a = a +% b a = a + b
proc encode(dest: var MD5Block, src: cstring) = proc encode(dest: var MD5Block, src: cstring) =
var j = 0 var j = 0
for i in 0..high(dest): for i in 0..high(dest):
dest[i] = toU32(ord(src[j]) or dest[i] = uint32(ord(src[j])) or
ord(src[j+1]) shl 8 or uint32(ord(src[j+1])) shl 8 or
ord(src[j+2]) shl 16 or uint32(ord(src[j+2])) shl 16 or
ord(src[j+3]) shl 24) uint32(ord(src[j+3])) shl 24
inc(j, 4) inc(j, 4)
proc decode(dest: var openArray[int8], src: openArray[int32]) = proc decode(dest: var openArray[uint8], src: openArray[uint32]) =
var i = 0 var i = 0
for j in 0..high(src): for j in 0..high(src):
dest[i] = toU8(src[j] and 0xff'i32) dest[i] = src[j] and 0xff'u32
dest[i+1] = toU8(src[j] shr 8'i32 and 0xff'i32) dest[i+1] = src[j] shr 8 and 0xff'u32
dest[i+2] = toU8(src[j] shr 16'i32 and 0xff'i32) dest[i+2] = src[j] shr 16 and 0xff'u32
dest[i+3] = toU8(src[j] shr 24'i32 and 0xff'i32) dest[i+3] = src[j] shr 24 and 0xff'u32
inc(i, 4) inc(i, 4)
proc transform(buffer: pointer, state: var MD5State) = proc transform(buffer: pointer, state: var MD5State) =
@ -92,92 +94,92 @@ proc transform(buffer: pointer, state: var MD5State) =
var b = state[1] var b = state[1]
var c = state[2] var c = state[2]
var d = state[3] var d = state[3]
FF(a, b, c, d, myBlock[0], 7'i8, 0xD76AA478'i32) FF(a, b, c, d, myBlock[0], 7'u8, 0xD76AA478'u32)
FF(d, a, b, c, myBlock[1], 12'i8, 0xE8C7B756'i32) FF(d, a, b, c, myBlock[1], 12'u8, 0xE8C7B756'u32)
FF(c, d, a, b, myBlock[2], 17'i8, 0x242070DB'i32) FF(c, d, a, b, myBlock[2], 17'u8, 0x242070DB'u32)
FF(b, c, d, a, myBlock[3], 22'i8, 0xC1BDCEEE'i32) FF(b, c, d, a, myBlock[3], 22'u8, 0xC1BDCEEE'u32)
FF(a, b, c, d, myBlock[4], 7'i8, 0xF57C0FAF'i32) FF(a, b, c, d, myBlock[4], 7'u8, 0xF57C0FAF'u32)
FF(d, a, b, c, myBlock[5], 12'i8, 0x4787C62A'i32) FF(d, a, b, c, myBlock[5], 12'u8, 0x4787C62A'u32)
FF(c, d, a, b, myBlock[6], 17'i8, 0xA8304613'i32) FF(c, d, a, b, myBlock[6], 17'u8, 0xA8304613'u32)
FF(b, c, d, a, myBlock[7], 22'i8, 0xFD469501'i32) FF(b, c, d, a, myBlock[7], 22'u8, 0xFD469501'u32)
FF(a, b, c, d, myBlock[8], 7'i8, 0x698098D8'i32) FF(a, b, c, d, myBlock[8], 7'u8, 0x698098D8'u32)
FF(d, a, b, c, myBlock[9], 12'i8, 0x8B44F7AF'i32) FF(d, a, b, c, myBlock[9], 12'u8, 0x8B44F7AF'u32)
FF(c, d, a, b, myBlock[10], 17'i8, 0xFFFF5BB1'i32) FF(c, d, a, b, myBlock[10], 17'u8, 0xFFFF5BB1'u32)
FF(b, c, d, a, myBlock[11], 22'i8, 0x895CD7BE'i32) FF(b, c, d, a, myBlock[11], 22'u8, 0x895CD7BE'u32)
FF(a, b, c, d, myBlock[12], 7'i8, 0x6B901122'i32) FF(a, b, c, d, myBlock[12], 7'u8, 0x6B901122'u32)
FF(d, a, b, c, myBlock[13], 12'i8, 0xFD987193'i32) FF(d, a, b, c, myBlock[13], 12'u8, 0xFD987193'u32)
FF(c, d, a, b, myBlock[14], 17'i8, 0xA679438E'i32) FF(c, d, a, b, myBlock[14], 17'u8, 0xA679438E'u32)
FF(b, c, d, a, myBlock[15], 22'i8, 0x49B40821'i32) FF(b, c, d, a, myBlock[15], 22'u8, 0x49B40821'u32)
GG(a, b, c, d, myBlock[1], 5'i8, 0xF61E2562'i32) GG(a, b, c, d, myBlock[1], 5'u8, 0xF61E2562'u32)
GG(d, a, b, c, myBlock[6], 9'i8, 0xC040B340'i32) GG(d, a, b, c, myBlock[6], 9'u8, 0xC040B340'u32)
GG(c, d, a, b, myBlock[11], 14'i8, 0x265E5A51'i32) GG(c, d, a, b, myBlock[11], 14'u8, 0x265E5A51'u32)
GG(b, c, d, a, myBlock[0], 20'i8, 0xE9B6C7AA'i32) GG(b, c, d, a, myBlock[0], 20'u8, 0xE9B6C7AA'u32)
GG(a, b, c, d, myBlock[5], 5'i8, 0xD62F105D'i32) GG(a, b, c, d, myBlock[5], 5'u8, 0xD62F105D'u32)
GG(d, a, b, c, myBlock[10], 9'i8, 0x02441453'i32) GG(d, a, b, c, myBlock[10], 9'u8, 0x02441453'u32)
GG(c, d, a, b, myBlock[15], 14'i8, 0xD8A1E681'i32) GG(c, d, a, b, myBlock[15], 14'u8, 0xD8A1E681'u32)
GG(b, c, d, a, myBlock[4], 20'i8, 0xE7D3FBC8'i32) GG(b, c, d, a, myBlock[4], 20'u8, 0xE7D3FBC8'u32)
GG(a, b, c, d, myBlock[9], 5'i8, 0x21E1CDE6'i32) GG(a, b, c, d, myBlock[9], 5'u8, 0x21E1CDE6'u32)
GG(d, a, b, c, myBlock[14], 9'i8, 0xC33707D6'i32) GG(d, a, b, c, myBlock[14], 9'u8, 0xC33707D6'u32)
GG(c, d, a, b, myBlock[3], 14'i8, 0xF4D50D87'i32) GG(c, d, a, b, myBlock[3], 14'u8, 0xF4D50D87'u32)
GG(b, c, d, a, myBlock[8], 20'i8, 0x455A14ED'i32) GG(b, c, d, a, myBlock[8], 20'u8, 0x455A14ED'u32)
GG(a, b, c, d, myBlock[13], 5'i8, 0xA9E3E905'i32) GG(a, b, c, d, myBlock[13], 5'u8, 0xA9E3E905'u32)
GG(d, a, b, c, myBlock[2], 9'i8, 0xFCEFA3F8'i32) GG(d, a, b, c, myBlock[2], 9'u8, 0xFCEFA3F8'u32)
GG(c, d, a, b, myBlock[7], 14'i8, 0x676F02D9'i32) GG(c, d, a, b, myBlock[7], 14'u8, 0x676F02D9'u32)
GG(b, c, d, a, myBlock[12], 20'i8, 0x8D2A4C8A'i32) GG(b, c, d, a, myBlock[12], 20'u8, 0x8D2A4C8A'u32)
HH(a, b, c, d, myBlock[5], 4'i8, 0xFFFA3942'i32) HH(a, b, c, d, myBlock[5], 4'u8, 0xFFFA3942'u32)
HH(d, a, b, c, myBlock[8], 11'i8, 0x8771F681'i32) HH(d, a, b, c, myBlock[8], 11'u8, 0x8771F681'u32)
HH(c, d, a, b, myBlock[11], 16'i8, 0x6D9D6122'i32) HH(c, d, a, b, myBlock[11], 16'u8, 0x6D9D6122'u32)
HH(b, c, d, a, myBlock[14], 23'i8, 0xFDE5380C'i32) HH(b, c, d, a, myBlock[14], 23'u8, 0xFDE5380C'u32)
HH(a, b, c, d, myBlock[1], 4'i8, 0xA4BEEA44'i32) HH(a, b, c, d, myBlock[1], 4'u8, 0xA4BEEA44'u32)
HH(d, a, b, c, myBlock[4], 11'i8, 0x4BDECFA9'i32) HH(d, a, b, c, myBlock[4], 11'u8, 0x4BDECFA9'u32)
HH(c, d, a, b, myBlock[7], 16'i8, 0xF6BB4B60'i32) HH(c, d, a, b, myBlock[7], 16'u8, 0xF6BB4B60'u32)
HH(b, c, d, a, myBlock[10], 23'i8, 0xBEBFBC70'i32) HH(b, c, d, a, myBlock[10], 23'u8, 0xBEBFBC70'u32)
HH(a, b, c, d, myBlock[13], 4'i8, 0x289B7EC6'i32) HH(a, b, c, d, myBlock[13], 4'u8, 0x289B7EC6'u32)
HH(d, a, b, c, myBlock[0], 11'i8, 0xEAA127FA'i32) HH(d, a, b, c, myBlock[0], 11'u8, 0xEAA127FA'u32)
HH(c, d, a, b, myBlock[3], 16'i8, 0xD4EF3085'i32) HH(c, d, a, b, myBlock[3], 16'u8, 0xD4EF3085'u32)
HH(b, c, d, a, myBlock[6], 23'i8, 0x04881D05'i32) HH(b, c, d, a, myBlock[6], 23'u8, 0x04881D05'u32)
HH(a, b, c, d, myBlock[9], 4'i8, 0xD9D4D039'i32) HH(a, b, c, d, myBlock[9], 4'u8, 0xD9D4D039'u32)
HH(d, a, b, c, myBlock[12], 11'i8, 0xE6DB99E5'i32) HH(d, a, b, c, myBlock[12], 11'u8, 0xE6DB99E5'u32)
HH(c, d, a, b, myBlock[15], 16'i8, 0x1FA27CF8'i32) HH(c, d, a, b, myBlock[15], 16'u8, 0x1FA27CF8'u32)
HH(b, c, d, a, myBlock[2], 23'i8, 0xC4AC5665'i32) HH(b, c, d, a, myBlock[2], 23'u8, 0xC4AC5665'u32)
II(a, b, c, d, myBlock[0], 6'i8, 0xF4292244'i32) II(a, b, c, d, myBlock[0], 6'u8, 0xF4292244'u32)
II(d, a, b, c, myBlock[7], 10'i8, 0x432AFF97'i32) II(d, a, b, c, myBlock[7], 10'u8, 0x432AFF97'u32)
II(c, d, a, b, myBlock[14], 15'i8, 0xAB9423A7'i32) II(c, d, a, b, myBlock[14], 15'u8, 0xAB9423A7'u32)
II(b, c, d, a, myBlock[5], 21'i8, 0xFC93A039'i32) II(b, c, d, a, myBlock[5], 21'u8, 0xFC93A039'u32)
II(a, b, c, d, myBlock[12], 6'i8, 0x655B59C3'i32) II(a, b, c, d, myBlock[12], 6'u8, 0x655B59C3'u32)
II(d, a, b, c, myBlock[3], 10'i8, 0x8F0CCC92'i32) II(d, a, b, c, myBlock[3], 10'u8, 0x8F0CCC92'u32)
II(c, d, a, b, myBlock[10], 15'i8, 0xFFEFF47D'i32) II(c, d, a, b, myBlock[10], 15'u8, 0xFFEFF47D'u32)
II(b, c, d, a, myBlock[1], 21'i8, 0x85845DD1'i32) II(b, c, d, a, myBlock[1], 21'u8, 0x85845DD1'u32)
II(a, b, c, d, myBlock[8], 6'i8, 0x6FA87E4F'i32) II(a, b, c, d, myBlock[8], 6'u8, 0x6FA87E4F'u32)
II(d, a, b, c, myBlock[15], 10'i8, 0xFE2CE6E0'i32) II(d, a, b, c, myBlock[15], 10'u8, 0xFE2CE6E0'u32)
II(c, d, a, b, myBlock[6], 15'i8, 0xA3014314'i32) II(c, d, a, b, myBlock[6], 15'u8, 0xA3014314'u32)
II(b, c, d, a, myBlock[13], 21'i8, 0x4E0811A1'i32) II(b, c, d, a, myBlock[13], 21'u8, 0x4E0811A1'u32)
II(a, b, c, d, myBlock[4], 6'i8, 0xF7537E82'i32) II(a, b, c, d, myBlock[4], 6'u8, 0xF7537E82'u32)
II(d, a, b, c, myBlock[11], 10'i8, 0xBD3AF235'i32) II(d, a, b, c, myBlock[11], 10'u8, 0xBD3AF235'u32)
II(c, d, a, b, myBlock[2], 15'i8, 0x2AD7D2BB'i32) II(c, d, a, b, myBlock[2], 15'u8, 0x2AD7D2BB'u32)
II(b, c, d, a, myBlock[9], 21'i8, 0xEB86D391'i32) II(b, c, d, a, myBlock[9], 21'u8, 0xEB86D391'u32)
state[0] = state[0] +% a state[0] = state[0] + a
state[1] = state[1] +% b state[1] = state[1] + b
state[2] = state[2] +% c state[2] = state[2] + c
state[3] = state[3] +% d state[3] = state[3] + d
proc md5Init*(c: var MD5Context) = proc md5Init*(c: var MD5Context) =
## initializes a MD5Context ## initializes a MD5Context
c.state[0] = 0x67452301'i32 c.state[0] = 0x67452301'u32
c.state[1] = 0xEFCDAB89'i32 c.state[1] = 0xEFCDAB89'u32
c.state[2] = 0x98BADCFE'i32 c.state[2] = 0x98BADCFE'u32
c.state[3] = 0x10325476'i32 c.state[3] = 0x10325476'u32
c.count[0] = 0'i32 c.count[0] = 0'u32
c.count[1] = 0'i32 c.count[1] = 0'u32
zeroMem(addr(c.buffer), sizeof(MD5buffer)) zeroMem(addr(c.buffer), sizeof(MD5buffer))
proc md5Update*(c: var MD5Context, input: cstring, len: int) = proc md5Update*(c: var MD5Context, input: cstring, len: int) =
## updates the MD5Context with the `input` data of length `len` ## updates the MD5Context with the `input` data of length `len`
var input = input var input = input
var Index = (c.count[0] shr 3) and 0x3F var Index = int((c.count[0] shr 3) and 0x3F)
c.count[0] = c.count[0] +% toU32(len shl 3) c.count[0] = c.count[0] + (uint32(len) shl 3)
if c.count[0] < (len shl 3): c.count[1] = c.count[1] +% 1'i32 if c.count[0] < (uint32(len) shl 3): c.count[1] = c.count[1] + 1'u32
c.count[1] = c.count[1] +% toU32(len shr 29) c.count[1] = c.count[1] + (uint32(len) shr 29)
var PartLen = 64 - Index var PartLen = 64 - Index
if len >= PartLen: if len >= PartLen:
copyMem(addr(c.buffer[Index]), input, PartLen) copyMem(addr(c.buffer[Index]), input, PartLen)
@ -196,7 +198,7 @@ proc md5Final*(c: var MD5Context, digest: var MD5Digest) =
Bits: MD5CBits Bits: MD5CBits
PadLen: int PadLen: int
decode(Bits, c.count) decode(Bits, c.count)
var Index = (c.count[0] shr 3) and 0x3F var Index = int((c.count[0] shr 3) and 0x3F)
if Index < 56: PadLen = 56 - Index if Index < 56: PadLen = 56 - Index
else: PadLen = 120 - Index else: PadLen = 120 - Index
md5Update(c, padding, PadLen) md5Update(c, padding, PadLen)
@ -241,5 +243,3 @@ when isMainModule:
assert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") == assert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") ==
"7e716d0e702df0505fc72e2b89467910") "7e716d0e702df0505fc72e2b89467910")
assert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e") assert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e")

View file

@ -23,7 +23,7 @@ proc `$`*(x: SocketHandle): string {.borrow.}
type type
Event* = enum Event* = enum
EvRead, EvWrite EvRead, EvWrite, EvError
SelectorKey* = ref object SelectorKey* = ref object
fd*: SocketHandle fd*: SocketHandle
@ -146,12 +146,19 @@ elif defined(linux):
## on the ``fd``. ## on the ``fd``.
result = @[] result = @[]
let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint) let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
if evNum < 0: raiseOSError(osLastError()) if evNum < 0:
let err = osLastError()
if err.cint == EINTR:
return @[]
raiseOSError(osLastError())
if evNum == 0: return @[] if evNum == 0: return @[]
for i in 0 .. <evNum: for i in 0 .. <evNum:
let fd = s.events[i].data.fd.SocketHandle let fd = s.events[i].data.fd.SocketHandle
var evSet: set[Event] = {} var evSet: set[Event] = {}
if (s.events[i].events and EPOLLERR) != 0: evSet = evSet + {EvError}
if (s.events[i].events and EPOLLHUP) != 0: evSet = evSet + {EvError}
if (s.events[i].events and EPOLLRDHUP) != 0: evSet = evSet + {EvError}
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead} if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite} if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
let selectorKey = s.fds[fd] let selectorKey = s.fds[fd]

View file

@ -1,7 +1,7 @@
# #
# #
# Nim's Runtime Library # Nim's Runtime Library
# (c) Copyright 2012 Nim Contributors # (c) Copyright 2015 Nim Contributors
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -9,12 +9,27 @@
## :Author: Zahary Karadjov ## :Author: Zahary Karadjov
## ##
## This module implements the standard unit testing facilities such as ## This module implements boilerplate to make testing easy.
## suites, fixtures and test cases as well as facilities for combinatorial
## and randomzied test case generation (not yet available)
## and object mocking (not yet available)
## ##
## It is loosely based on C++'s boost.test and Haskell's QuickTest ## Example:
##
## .. code:: nim
##
## suite "description for this stuff":
## test "essential truths":
## # give up and stop if this fails
## require(true)
##
## test "slightly less obvious stuff":
## # print a nasty message and move on, skipping
## # the remainder of this block
## check(1 != 1)
## check("asd"[2] == 'd')
##
## test "out of bounds error is thrown on bad access":
## let v = @[1, 2, 3] # you can do initialization here
## expect(IndexError):
## discard v[4]
import import
macros macros
@ -209,3 +224,5 @@ if envOutLvl.len > 0:
if $opt == envOutLvl: if $opt == envOutLvl:
outputLevel = opt outputLevel = opt
break break
system.addQuitProc(resetAttributes)

View file

@ -16,6 +16,7 @@ type
Uri* = object Uri* = object
scheme*, username*, password*: string scheme*, username*, password*: string
hostname*, port*, path*, query*, anchor*: string hostname*, port*, path*, query*, anchor*: string
opaque*: bool
{.deprecated: [TUrl: Url, TUri: Uri].} {.deprecated: [TUrl: Url, TUri: Uri].}
@ -115,6 +116,8 @@ proc parseUri*(uri: string): Uri =
if authority == "": if authority == "":
raise newException(ValueError, "Expected authority got nothing.") raise newException(ValueError, "Expected authority got nothing.")
parseAuthority(authority, result) parseAuthority(authority, result)
else:
result.opaque = true
# Path # Path
parsePath(uri, i, result) parsePath(uri, i, result)
@ -256,6 +259,9 @@ proc `$`*(u: Uri): string =
result = "" result = ""
if u.scheme.len > 0: if u.scheme.len > 0:
result.add(u.scheme) result.add(u.scheme)
if u.opaque:
result.add(":")
else:
result.add("://") result.add("://")
if u.username.len > 0: if u.username.len > 0:
result.add(u.username) result.add(u.username)
@ -268,22 +274,28 @@ proc `$`*(u: Uri): string =
result.add(":") result.add(":")
result.add(u.port) result.add(u.port)
if u.path.len > 0: if u.path.len > 0:
if u.path[0] != '/': result.add("/")
result.add(u.path) result.add(u.path)
if u.query.len > 0:
result.add("?")
result.add(u.query) result.add(u.query)
if u.anchor.len > 0:
result.add("#")
result.add(u.anchor) result.add(u.anchor)
when isMainModule: when isMainModule:
block: block:
let test = parseUri("http://localhost:8080/test") let str = "http://localhost:8080/test"
let test = parseUri(str)
doAssert test.scheme == "http" doAssert test.scheme == "http"
doAssert test.port == "8080" doAssert test.port == "8080"
doAssert test.path == "/test" doAssert test.path == "/test"
doAssert test.hostname == "localhost" doAssert test.hostname == "localhost"
doAssert($test == str)
block: block:
let test = parseUri("foo://username:password@example.com:8042/over/there" & let str = "foo://username:password@example.com:8042/over/there" &
"/index.dtb?type=animal&name=narwhal#nose") "/index.dtb?type=animal&name=narwhal#nose"
let test = parseUri(str)
doAssert test.scheme == "foo" doAssert test.scheme == "foo"
doAssert test.username == "username" doAssert test.username == "username"
doAssert test.password == "password" doAssert test.password == "password"
@ -292,34 +304,45 @@ when isMainModule:
doAssert test.path == "/over/there/index.dtb" doAssert test.path == "/over/there/index.dtb"
doAssert test.query == "type=animal&name=narwhal" doAssert test.query == "type=animal&name=narwhal"
doAssert test.anchor == "nose" doAssert test.anchor == "nose"
doAssert($test == str)
block: block:
let test = parseUri("urn:example:animal:ferret:nose") let str = "urn:example:animal:ferret:nose"
let test = parseUri(str)
doAssert test.scheme == "urn" doAssert test.scheme == "urn"
doAssert test.path == "example:animal:ferret:nose" doAssert test.path == "example:animal:ferret:nose"
doAssert($test == str)
block: block:
let test = parseUri("mailto:username@example.com?subject=Topic") let str = "mailto:username@example.com?subject=Topic"
let test = parseUri(str)
doAssert test.scheme == "mailto" doAssert test.scheme == "mailto"
doAssert test.username == "username" doAssert test.username == "username"
doAssert test.hostname == "example.com" doAssert test.hostname == "example.com"
doAssert test.query == "subject=Topic" doAssert test.query == "subject=Topic"
doAssert($test == str)
block: block:
let test = parseUri("magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar") let str = "magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar"
let test = parseUri(str)
doAssert test.scheme == "magnet" doAssert test.scheme == "magnet"
doAssert test.query == "xt=urn:sha1:72hsga62ba515sbd62&dn=foobar" doAssert test.query == "xt=urn:sha1:72hsga62ba515sbd62&dn=foobar"
doAssert($test == str)
block: block:
let test = parseUri("/test/foo/bar?q=2#asdf") let str = "/test/foo/bar?q=2#asdf"
let test = parseUri(str)
doAssert test.scheme == "" doAssert test.scheme == ""
doAssert test.path == "/test/foo/bar" doAssert test.path == "/test/foo/bar"
doAssert test.query == "q=2" doAssert test.query == "q=2"
doAssert test.anchor == "asdf" doAssert test.anchor == "asdf"
doAssert($test == str)
block: block:
let test = parseUri("test/no/slash") let str = "test/no/slash"
let test = parseUri(str)
doAssert test.path == "test/no/slash" doAssert test.path == "test/no/slash"
doAssert($test == str)
# Remove dot segments tests # Remove dot segments tests
block: block:
@ -371,5 +394,3 @@ when isMainModule:
block: block:
let test = parseUri("http://example.com/foo/") / "/bar/asd" let test = parseUri("http://example.com/foo/") / "/bar/asd"
doAssert test.path == "/foo/bar/asd" doAssert test.path == "/foo/bar/asd"

View file

@ -49,7 +49,7 @@ type
waMarkGlobal, # part of the backup/debug mark&sweep waMarkGlobal, # part of the backup/debug mark&sweep
waMarkPrecise, # part of the backup/debug mark&sweep waMarkPrecise, # part of the backup/debug mark&sweep
waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack, waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack,
waCollectWhite, waCollectWhite #, waDebug
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
# A ref type can have a finalizer that is called before the object's # A ref type can have a finalizer that is called before the object's
@ -597,7 +597,13 @@ proc scan(s: PCell) =
forAllChildren(s, waScan) forAllChildren(s, waScan)
proc collectWhite(s: PCell) = proc collectWhite(s: PCell) =
if s.color == rcWhite and s notin gch.cycleRoots: # This is a hacky way to deal with the following problem (bug #1796)
# Consider this content in cycleRoots:
# x -> a; y -> a where 'a' is an acyclic object so not included in
# cycleRoots itself. Then 'collectWhite' used to free 'a' twice. The
# 'isAllocatedPtr' check prevents this. This also means we do not need
# to query 's notin gch.cycleRoots' at all.
if isAllocatedPtr(gch.region, s) and s.color == rcWhite:
s.setColor(rcBlack) s.setColor(rcBlack)
forAllChildren(s, waCollectWhite) forAllChildren(s, waCollectWhite)
freeCyclicCell(gch, s) freeCyclicCell(gch, s)
@ -648,6 +654,28 @@ when useMarkForDebug or useBackupGc:
if objStart != nil: if objStart != nil:
markS(gch, objStart) markS(gch, objStart)
when logGC:
var
cycleCheckA: array[100, PCell]
cycleCheckALen = 0
proc alreadySeen(c: PCell): bool =
for i in 0 .. <cycleCheckALen:
if cycleCheckA[i] == c: return true
if cycleCheckALen == len(cycleCheckA):
gcAssert(false, "cycle detection overflow")
quit 1
cycleCheckA[cycleCheckALen] = c
inc cycleCheckALen
proc debugGraph(s: PCell) =
if alreadySeen(s):
writeCell("child cell (already seen) ", s)
else:
writeCell("cell {", s)
forAllChildren(s, waDebug)
c_fprintf(c_stdout, "}\n")
proc doOperation(p: pointer, op: TWalkOp) = proc doOperation(p: pointer, op: TWalkOp) =
if p == nil: return if p == nil: return
var c: PCell = usrToCell(p) var c: PCell = usrToCell(p)
@ -690,6 +718,7 @@ proc doOperation(p: pointer, op: TWalkOp) =
of waMarkPrecise: of waMarkPrecise:
when useMarkForDebug or useBackupGc: when useMarkForDebug or useBackupGc:
add(gch.tempStack, c) add(gch.tempStack, c)
#of waDebug: debugGraph(c)
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
doOperation(d, TWalkOp(op)) doOperation(d, TWalkOp(op))
@ -702,7 +731,6 @@ when useMarkForDebug or useBackupGc:
proc collectRoots(gch: var TGcHeap) = proc collectRoots(gch: var TGcHeap) =
for s in elements(gch.cycleRoots): for s in elements(gch.cycleRoots):
excl(gch.cycleRoots, s)
collectWhite(s) collectWhite(s)
proc collectCycles(gch: var TGcHeap) = proc collectCycles(gch: var TGcHeap) =

View file

@ -1344,7 +1344,7 @@ when not defined(useNimRtl):
else: else:
dec(gch.recGcLock) dec(gch.recGcLock)
proc GC_setStrategy(strategy: TGC_Strategy) = proc GC_setStrategy(strategy: GC_Strategy) =
case strategy case strategy
of gcThroughput: nil of gcThroughput: nil
of gcResponsiveness: nil of gcResponsiveness: nil

View file

@ -144,7 +144,7 @@ when defined(boehmgc):
proc GC_disable() = boehmGC_disable() proc GC_disable() = boehmGC_disable()
proc GC_enable() = boehmGC_enable() proc GC_enable() = boehmGC_enable()
proc GC_fullCollect() = boehmGCfullCollect() proc GC_fullCollect() = boehmGCfullCollect()
proc GC_setStrategy(strategy: TGC_Strategy) = discard proc GC_setStrategy(strategy: GC_Strategy) = discard
proc GC_enableMarkAndSweep() = discard proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return "" proc GC_getStatistics(): string = return ""
@ -221,7 +221,7 @@ elif defined(nogc) and defined(useMalloc):
proc GC_disable() = discard proc GC_disable() = discard
proc GC_enable() = discard proc GC_enable() = discard
proc GC_fullCollect() = discard proc GC_fullCollect() = discard
proc GC_setStrategy(strategy: TGC_Strategy) = discard proc GC_setStrategy(strategy: GC_Strategy) = discard
proc GC_enableMarkAndSweep() = discard proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return "" proc GC_getStatistics(): string = return ""
@ -281,7 +281,7 @@ elif defined(nogc):
proc GC_disable() = discard proc GC_disable() = discard
proc GC_enable() = discard proc GC_enable() = discard
proc GC_fullCollect() = discard proc GC_fullCollect() = discard
proc GC_setStrategy(strategy: TGC_Strategy) = discard proc GC_setStrategy(strategy: GC_Strategy) = discard
proc GC_enableMarkAndSweep() = discard proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return "" proc GC_getStatistics(): string = return ""

View file

@ -12,7 +12,7 @@
when defined(Unix): when defined(Unix):
const const
lib = "libmysqlclient.so.15" lib = "libmysqlclient.so.(15|16|17|18)"
when defined(Windows): when defined(Windows):
const const
lib = "libmysql.dll" lib = "libmysql.dll"

View file

@ -50,7 +50,7 @@ when useWinVersion:
from winlean import SocketHandle from winlean import SocketHandle
else: else:
const const
versions = "(|.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|.0.9.7|.0.9.6|.0.9.5|.0.9.4)"
when defined(macosx): when defined(macosx):
const const
DLLSSLName = "libssl" & versions & ".dylib" DLLSSLName = "libssl" & versions & ".dylib"

View file

@ -7,7 +7,7 @@ from strutils import join
type type
TFoo * = object TFoo * = object
id: int id: int
func: proc(){.closure.} fn: proc(){.closure.}
var foo_counter = 0 var foo_counter = 0
var alive_foos = newseq[int](0) var alive_foos = newseq[int](0)
@ -26,7 +26,7 @@ for i in 0 .. <10:
for i in 0 .. <10: for i in 0 .. <10:
let f = newFoo() let f = newFoo()
f.func = proc = f.fn = proc =
echo f.id echo f.id
GC_fullcollect() GC_fullcollect()

View file

@ -0,0 +1,21 @@
# Program to detect bug #1796 reliably
type
Node = ref object
a, b: Node
leaf: string
proc createCycle(leaf: string): Node =
new result
result.a = result
shallowCopy result.leaf, leaf
proc main =
for i in 0 .. 100_000:
var leaf = "this is the leaf. it allocates"
let x = createCycle(leaf)
let y = createCycle(leaf)
echo "done ", getOccupiedMem()
main()

View file

@ -196,7 +196,8 @@ write(stdout, "starting main...\n")
main() main()
GC_fullCollect() GC_fullCollect()
# the M&S GC fails with this call and it's unclear why. Definitely something
# we need to fix!
GC_fullCollect() GC_fullCollect()
writeln(stdout, GC_getStatistics()) writeln(stdout, GC_getStatistics())
write(stdout, "finished\n") write(stdout, "finished\n")

16
tests/generics/t1050.nim Normal file
View file

@ -0,0 +1,16 @@
discard """
msg: "int"
output: "4"
"""
import typetraits
type ArrayType[T] = distinct T
proc arrayItem(a: ArrayType): auto =
static: echo(name(type(a).T))
result = (type(a).T)(4)
var arr: ArrayType[int]
echo arrayItem(arr)

View file

@ -1,9 +1,15 @@
discard """ discard """
msg: "static 10\ndynamic\nstatic 20\n" msg: "static 10\ndynamic\nstatic 20\n"
output: "s\nd\nd\ns" output: "s\nd\nd\ns"
disabled: "true"
""" """
type
semistatic[T] =
static[T] or T
template isStatic*(x): expr =
compiles(static(x))
proc foo(x: semistatic[int]) = proc foo(x: semistatic[int]) =
when isStatic(x): when isStatic(x):
static: echo "static ", x static: echo "static ", x

View file

@ -1,6 +1,6 @@
discard """ discard """
file: "tstaticparams.nim" file: "tstaticparams.nim"
output: "abracadabra\ntest\n3\n15\n4\n2" output: "abracadabra\ntest\n3\n15\n4\n2\nfloat\n3\nfloat\nyin\nyang"
""" """
type type
@ -56,3 +56,87 @@ type TTestSub[N: static[int]] = TTest[1, N]
var z: TTestSub[2] var z: TTestSub[2]
echo z.high echo z.high
# issue 1049
proc matrix_1*[M, N, T](mat: Matrix[M,N,T], a: array[N, int]) = discard
proc matrix_2*[M, N, T](mat: Matrix[M,N,T], a: array[N+1, int]) = discard
proc matrix_3*[M, N: static[int]; T](mat: Matrix[M,N,T], a: array[N, int]) = discard
proc matrix_4*[M, N: static[int]; T](mat: Matrix[M,N,T], a: array[N+1, int]) = discard
var
tmat: Matrix[4,4,int]
ar1: array[4, int]
ar2: array[5, int]
matrix_1(tmat, ar1)
matrix_2(tmat, ar2)
matrix_3(tmat, ar1)
matrix_4(tmat, ar2)
template reject(x): stmt =
static: assert(not compiles(x))
# test with arrays of wrong size
reject matrix_1(tmat, ar2)
reject matrix_2(tmat, ar1)
reject matrix_3(tmat, ar2)
reject matrix_4(tmat, ar1)
# bug 1820
type
T1820_1[T; Y: static[int]] = object
bar: T
proc intOrFloat*[Y](f: T1820_1[int, Y]) = echo "int"
proc intOrFloat*[Y](f: T1820_1[float, Y]) = echo "float"
proc threeOrFour*[T](f: T1820_1[T, 3]) = echo "3"
proc threeOrFour*[T](f: T1820_1[T, 4]) = echo "4"
var foo_1: T1820_1[float, 3]
foo_1.intOrFloat
foo_1.threeOrFour
type
YinAndYang = enum
Yin,
Yang
T1820_2[T; Y: static[YinAndYang]] = object
bar: T
proc intOrFloat*[Y](f: T1820_2[int, Y]) = echo "int"
proc intOrFloat*[Y](f: T1820_2[float, Y]) = echo "float"
proc yinOrYang*[T](f: T1820_2[T, YinAndYang.Yin]) = echo "yin"
proc yinOrYang*[T](f: T1820_2[T, Yang]) = echo "yang"
var foo_2: T1820_2[float, Yin]
var foo_3: T1820_2[float, YinAndYang.Yang]
foo_2.intOrFloat
foo_2.yinOrYang
foo_3.yinOrYang
# bug 1859
type
TypeWith2Params[N, M: static[int]] = object
proc bindBothParams[N](x: TypeWith2Params[N, N]) = discard
proc dontBind1[N,M](x: TypeWith2Params[N, M]) = discard
proc dontBind2(x: TypeWith2Params) = discard
var bb_1: TypeWith2Params[2, 2]
var bb_2: TypeWith2Params[2, 3]
bindBothParams(bb_1)
reject bindBothParams(bb_2)
dontBind1 bb_1
dontBind1 bb_2
dontBind2 bb_1
dontBind2 bb_2

View file

@ -1,5 +1,13 @@
discard """ discard """
output: "Sortable\nSortable\nContainer" output: '''Sortable
Sortable
Container
true
true
false
false
false
'''
""" """
import typetraits import typetraits
@ -41,3 +49,20 @@ proc y(x: TObj): int = 10
proc testFoo(x: TFoo) = discard proc testFoo(x: TFoo) = discard
testFoo(TObj(x: 10)) testFoo(TObj(x: 10))
type
Matrix[Rows, Cols: static[int]; T] = generic M
M.M == Rows
M.N == Cols
M.T is T
MyMatrix[M, N: static[int]; T] = object
data: array[M*N, T]
var x: MyMatrix[3, 3, int]
echo x is Matrix
echo x is Matrix[3, 3, int]
echo x is Matrix[3, 3, float]
echo x is Matrix[4, 3, int]
echo x is Matrix[3, 4, int]

View file

@ -51,7 +51,7 @@ while i < s.len:
write(stdout, "Du heißt " & s) write(stdout, "Du heißt " & s)
# bug #544 # bug #544
when false:
# yay, fails again # yay, fails again
type Bar [T; I:range] = array[I, T] type Bar [T; I:range] = array[I, T]
proc foo*[T; I:range](a, b: Bar[T, I]): Bar[T, I] = proc foo*[T; I:range](a, b: Bar[T, I]): Bar[T, I] =
@ -60,7 +60,7 @@ when false:
{.fatal:"Dimensions have to be 3".} {.fatal:"Dimensions have to be 3".}
#... #...
block: block:
var a, b: Bar[int, 0..2] var a, b: Bar[int, range[0..2]]
discard foo(a, b) discard foo(a, b)
# bug #1788 # bug #1788

View file

@ -10,6 +10,9 @@ AST a
AST b AST b
(e: [55, 66], f: [77, 88]) (e: [55, 66], f: [77, 88])
55 55
10
20Test
20
''' '''
""" """
@ -50,3 +53,22 @@ macro mB(data: static[Tb]): stmt =
mA(a) mA(a)
mB(b) mB(b)
type
Foo[N: static[int], Z: static[string]] = object
macro staticIntMacro(f: static[int]): stmt = echo f
staticIntMacro 10
var
x: Foo[20, "Test"]
macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12): stmt =
echo N, Z
genericMacro x
template genericTemplate[N, Z](f: Foo[N, Z], ll = 3, zz = 12): int = N
static:
echo genericTemplate(x) # Error: internal error: (filename: compiler/evaltempl.nim, line: 39)

View file

@ -107,12 +107,15 @@ proc dllTests(r: var TResults, cat: Category, options: string) =
# ------------------------------ GC tests ------------------------------------- # ------------------------------ GC tests -------------------------------------
proc gcTests(r: var TResults, cat: Category, options: string) = proc gcTests(r: var TResults, cat: Category, options: string) =
template test(filename: expr): stmt = template testWithoutMs(filename: expr): stmt =
testSpec r, makeTest("tests/gc" / filename, options, cat, actionRun) testSpec r, makeTest("tests/gc" / filename, options, cat, actionRun)
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" -d:release", cat, actionRun) " -d:release", cat, actionRun)
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" -d:release -d:useRealtimeGC", cat, actionRun) " -d:release -d:useRealtimeGC", cat, actionRun)
template test(filename: expr): stmt =
testWithoutMs filename
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
" --gc:markAndSweep", cat, actionRun) " --gc:markAndSweep", cat, actionRun)
testSpec r, makeTest("tests/gc" / filename, options & testSpec r, makeTest("tests/gc" / filename, options &
@ -124,13 +127,15 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
test "gctest" test "gctest"
test "gcleak3" test "gcleak3"
test "gcleak4" test "gcleak4"
test "gcleak5" # Disabled because it works and takes too long to run:
#test "gcleak5"
test "weakrefs" test "weakrefs"
test "cycleleak" test "cycleleak"
test "closureleak" test "closureleak"
test "refarrayleak" testWithoutMs "refarrayleak"
test "stackrefleak"
test "stackrefleak"
test "cyclecollector"
# ------------------------- threading tests ----------------------------------- # ------------------------- threading tests -----------------------------------

View file

@ -128,7 +128,7 @@
; Write application registry keys ; Write application registry keys
WriteRegStr HKCU "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\bin\?{c.name}.exe" WriteRegStr HKCU "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\bin\?{c.name}.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninstaller.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\bin\?{c.name}.exe" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\bin\?{c.name}.exe"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"

View file

@ -23,6 +23,7 @@ type
gitCommit: string gitCommit: string
quotations: Table[string, tuple[quote, author: string]] quotations: Table[string, tuple[quote, author: string]]
numProcessors: int # Set by parallelBuild:n, only works for values > 0. numProcessors: int # Set by parallelBuild:n, only works for values > 0.
gaId: string # google analytics ID, nil means analytics are disabled
TRssItem = object TRssItem = object
year, month, day, title: string year, month, day, title: string
TAction = enum TAction = enum
@ -144,7 +145,12 @@ proc parseCmdLine(c: var TConfigData) =
c.vars[substr(val, 0, idx-1)] = substr(val, idx+1) c.vars[substr(val, 0, idx-1)] = substr(val, idx+1)
of "website": action = actOnlyWebsite of "website": action = actOnlyWebsite
of "pdf": action = actPdf of "pdf": action = actPdf
else: quit(usage) of "googleanalytics":
c.gaId = val
c.nimArgs.add("--doc.googleAnalytics:" & val & " ")
else:
echo("Invalid argument $1" % [key])
quit(usage)
of cmdEnd: break of cmdEnd: break
if c.infile.len == 0: quit(usage) if c.infile.len == 0: quit(usage)

View file

@ -192,13 +192,15 @@ View at: localhost:5000
</footer> </footer>
<script> <script>
# if c.gaId != nil:
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-48159761-1', 'nim-lang.org'); ga('create', '${c.gaId}', 'nim-lang.org');
ga('send', 'pageview'); ga('send', 'pageview');
# end if
var timer; var timer;
var prevIndex = 0; var prevIndex = 0;

View file

@ -42,7 +42,7 @@ Nim is efficient
Nim is expressive Nim is expressive
================= =================
* **The Nim compiler and all of the standard library are implemented in * **The Nim compiler and all of the standard libraries are implemented in
Nim.** Nim.**
* Built-in high level datatypes: strings, sets, sequences, etc. * Built-in high level datatypes: strings, sets, sequences, etc.
* Modern type system with local type inference, tuples, variants, * Modern type system with local type inference, tuples, variants,

View file

@ -110,6 +110,7 @@ General
- Scite: Included - Scite: Included
- Gedit: The `Aporia .lang file <https://github.com/nimrod-code/Aporia/blob/master/share/gtksourceview-2.0/language-specs/nimrod.lang>`_ - Gedit: The `Aporia .lang file <https://github.com/nimrod-code/Aporia/blob/master/share/gtksourceview-2.0/language-specs/nimrod.lang>`_
- jEdit: https://github.com/exhu/nimrod-misc/tree/master/jedit - jEdit: https://github.com/exhu/nimrod-misc/tree/master/jedit
- TextMate: Available in bundle installer (`Repository <https://github.com/textmate/nim.tmbundle>`_)
.. container:: standout .. container:: standout