Merge branch 'devel' of https://github.com/Araq/Nim into devel
Conflicts: compiler/semexprs.nim
This commit is contained in:
commit
65310266d8
46 changed files with 991 additions and 490 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -41,3 +41,4 @@ xcuserdata/
|
||||||
/testresults.html
|
/testresults.html
|
||||||
/testresults.json
|
/testresults.json
|
||||||
testament.db
|
testament.db
|
||||||
|
/csources/
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 []
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.'
|
||||||
|
|
||||||
|
|
|
||||||
14
doc/tut2.txt
14
doc/tut2.txt
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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))
|
|
||||||
|
|
|
||||||
228
lib/pure/md5.nim
228
lib/pure/md5.nim
|
|
@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) =
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 ""
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
21
tests/gc/cyclecollector.nim
Normal file
21
tests/gc/cyclecollector.nim
Normal 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()
|
||||||
|
|
@ -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
16
tests/generics/t1050.nim
Normal 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)
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 -----------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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}"
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue