diff options
author | Araq <rumpf_a@web.de> | 2013-08-30 12:44:27 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2013-08-30 12:44:27 +0200 |
commit | 8710118b2cb1428a6ddb7929edaeef060f360be9 (patch) | |
tree | 96772e55b0e6bb92c70044083bbb9db4d9918f13 | |
parent | a17076cf4fdfb27ee1d1f3be232b81584a99432a (diff) | |
parent | c934a33ccd85716b66d44bca6a1764204507f33e (diff) | |
download | Nim-8710118b2cb1428a6ddb7929edaeef060f360be9.tar.gz |
Merge branch 'master' of github.com:Araq/Nimrod
-rw-r--r-- | compiler/ast.nim | 7 | ||||
-rw-r--r-- | compiler/evals.nim | 21 | ||||
-rw-r--r-- | compiler/options.nim | 2 | ||||
-rw-r--r-- | compiler/semdata.nim | 10 | ||||
-rw-r--r-- | compiler/semexprs.nim | 2 | ||||
-rw-r--r-- | compiler/semfold.nim | 16 | ||||
-rw-r--r-- | compiler/seminst.nim | 49 | ||||
-rw-r--r-- | compiler/semmagic.nim | 2 | ||||
-rw-r--r-- | compiler/semstmts.nim | 16 | ||||
-rw-r--r-- | compiler/semtypes.nim | 35 | ||||
-rw-r--r-- | compiler/semtypinst.nim | 10 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 8 | ||||
-rw-r--r-- | doc/manual.txt | 33 | ||||
-rw-r--r-- | install.txt | 43 | ||||
-rw-r--r-- | lib/pure/httpclient.nim | 67 | ||||
-rw-r--r-- | lib/system.nim | 23 | ||||
-rw-r--r-- | readme.md | 5 | ||||
-rw-r--r-- | readme.txt | 5 | ||||
-rw-r--r-- | tests/compile/tbindtypedesc.nim | 87 | ||||
-rw-r--r-- | tests/compile/tgenericshardcases.nim | 30 | ||||
-rw-r--r-- | tests/run/tmacrogenerics.nim | 39 | ||||
-rw-r--r-- | tests/run/tstaticparams.nim | 9 |
22 files changed, 385 insertions, 134 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index bb015ea27..bb06e7163 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -626,6 +626,7 @@ type case kind*: TSymKind of skType: typeInstCache*: seq[PType] + typScope*: PScope of routineKinds: procInstCache*: seq[PInstantiation] scope*: PScope # the scope where the proc was defined @@ -799,9 +800,9 @@ const # imported via 'importc: "fullname"' and no format string. # creator procs: -proc NewSym*(symKind: TSymKind, Name: PIdent, owner: PSym, +proc newSym*(symKind: TSymKind, Name: PIdent, owner: PSym, info: TLineInfo): PSym -proc NewType*(kind: TTypeKind, owner: PSym): PType +proc newType*(kind: TTypeKind, owner: PSym): PType proc newNode*(kind: TNodeKind): PNode proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode @@ -1111,7 +1112,7 @@ proc copySym(s: PSym, keepId: bool = false): PSym = result.loc = s.loc result.annex = s.annex # BUGFIX -proc NewSym(symKind: TSymKind, Name: PIdent, owner: PSym, +proc newSym(symKind: TSymKind, Name: PIdent, owner: PSym, info: TLineInfo): PSym = # generates a symbol and initializes the hash field too new(result) diff --git a/compiler/evals.nim b/compiler/evals.nim index 3f09664a7..053068ea4 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -904,18 +904,15 @@ proc evalParseStmt(c: PEvalContext, n: PNode): PNode = code.info.line.int) #result.typ = newType(tyStmt, c.module) -proc evalTypeTrait*(n: PNode, context: PSym): PNode = - ## XXX: This should be pretty much guaranteed to be true - # by the type traits procs' signatures, but until the - # code is more mature it doesn't hurt to be extra safe - internalAssert n.sons.len >= 2 and n.sons[1].kind == nkSym - - let typ = n.sons[1].sym.typ.skipTypes({tyTypeDesc}) - case n.sons[0].sym.name.s.normalize +proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode = + InternalAssert operand.kind == nkSym + + let typ = operand.sym.typ.skipTypes({tyTypeDesc}) + case trait.sym.name.s.normalize of "name": - result = newStrNode(nkStrLit, typ.typeToString(preferExported)) + result = newStrNode(nkStrLit, typ.typeToString(preferName)) result.typ = newType(tyString, context) - result.info = n.info + result.info = trait.info else: internalAssert false @@ -1037,8 +1034,8 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = of mParseStmtToAst: result = evalParseStmt(c, n) of mExpandToAst: result = evalExpandToAst(c, n) of mTypeTrait: - n.sons[1] = evalAux(c, n.sons[1], {}) - result = evalTypeTrait(n, c.module) + let operand = evalAux(c, n.sons[1], {}) + result = evalTypeTrait(n[0], operand, c.module) of mIs: n.sons[1] = evalAux(c, n.sons[1], {}) result = evalIsOp(n) diff --git a/compiler/options.nim b/compiler/options.nim index 5f173d240..2b25b2650 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -156,6 +156,8 @@ var const oKeepVariableNames* = true +const oUseLateInstantiation* = false + proc mainCommandArg*: string = ## This is intended for commands like check or parse ## which will work on the main project file unless diff --git a/compiler/semdata.nim b/compiler/semdata.nim index b9c32a680..8b04f4af5 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -225,14 +225,14 @@ proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) = dest.owner = getCurrOwner() dest.size = - 1 -proc makeRangeType*(c: PContext, first, last: biggestInt, - info: TLineInfo): PType = +proc makeRangeType*(c: PContext; first, last: biggestInt; + info: TLineInfo; intType = getSysType(tyInt)): PType = var n = newNodeI(nkRange, info) - addSon(n, newIntNode(nkIntLit, first)) - addSon(n, newIntNode(nkIntLit, last)) + addSon(n, newIntTypeNode(nkIntLit, first, intType)) + addSon(n, newIntTypeNode(nkIntLit, last, intType)) result = newTypeS(tyRange, c) result.n = n - rawAddSon(result, getSysType(tyInt)) # basetype of range + addSonSkipIntLit(result, intType) # basetype of range proc markIndirect*(c: PContext, s: PSym) {.inline.} = if s.kind in {skProc, skConverter, skMethod, skIterator}: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 354cbcbd1..4d7ceffa9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -113,7 +113,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of skGenericParam: if s.typ.kind == tyExpr: result = newSymNode(s, n.info) - result.typ = s.typ.lastSon + result.typ = s.typ elif s.ast != nil: result = semExpr(c, s.ast) else: diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 634ea8395..ca06ea1b6 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -561,9 +561,10 @@ proc getConstExpr(m: PSym, n: PNode): PNode = case n.kind of nkSym: var s = n.sym - if s.kind == skEnumField: + case s.kind + of skEnumField: result = newIntNodeT(s.position, n) - elif s.kind == skConst: + of skConst: case s.magic of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n) of mCompileDate: result = newStrNodeT(times.getDateStr(), n) @@ -581,10 +582,17 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of mNegInf: result = newFloatNodeT(NegInf, n) else: if sfFakeConst notin s.flags: result = copyTree(s.ast) - elif s.kind in {skProc, skMethod}: # BUGFIX + of {skProc, skMethod}: result = n - elif s.kind in {skType, skGenericParam}: + of skType: result = newSymNodeTypeDesc(s, n.info) + of skGenericParam: + if s.typ.kind == tyExpr: + result = s.typ.n + result.typ = s.typ.sons[0] + else: + result = newSymNodeTypeDesc(s, n.info) + else: nil of nkCharLit..nkNilLit: result = copyNode(n) of nkIfExpr: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 601072833..0cf5086a8 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -130,13 +130,50 @@ proc sideEffectsCheck(c: PContext, s: PSym) = s.ast.sons[genericParamsPos].kind == nkEmpty: c.threadEntries.add(s) +proc lateInstantiateGeneric(c: PContext, invocation: PType, info: TLineInfo): PType = + InternalAssert invocation.kind == tyGenericInvokation + + let cacheHit = searchInstTypes(invocation) + if cacheHit != nil: + result = cacheHit + else: + let s = invocation.sons[0].sym + let oldScope = c.currentScope + c.currentScope = s.typScope + openScope(c) + pushInfoContext(info) + for i in 0 .. <s.typ.n.sons.len: + let genericParam = s.typ.n[i].sym + let symKind = if genericParam.typ.kind == tyExpr: skConst + else: skType + + var boundSym = newSym(symKind, s.typ.n[i].sym.name, s, info) + boundSym.typ = invocation.sons[i+1].skipTypes({tyExpr}) + boundSym.ast = invocation.sons[i+1].n + addDecl(c, boundSym) + # XXX: copyTree would have been unnecessary here if semTypeNode + # didn't modify its input parameters. Currently, it does modify + # at least the record lists of the passed object and tuple types + var instantiated = semTypeNode(c, copyTree(s.ast[2]), nil) + popInfoContext() + closeScope(c) + c.currentScope = oldScope + if instantiated != nil: + result = invocation + result.kind = tyGenericInst + result.sons.add instantiated + cacheTypeInst result + proc instGenericContainer(c: PContext, info: TLineInfo, header: PType): PType = - var cl: TReplTypeVars - InitIdTable(cl.symMap) - InitIdTable(cl.typeMap) - cl.info = info - cl.c = c - result = ReplaceTypeVarsT(cl, header) + when oUseLateInstantiation: + lateInstantiateGeneric(c, header, info) + else: + var cl: TReplTypeVars + InitIdTable(cl.symMap) + InitIdTable(cl.typeMap) + cl.info = info + cl.c = c + result = ReplaceTypeVarsT(cl, header) proc instGenericContainer(c: PContext, n: PNode, header: PType): PType = result = instGenericContainer(c, n.info, header) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 41c379133..b9ef8b008 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -40,7 +40,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode = (typArg.kind == skParam and typArg.typ.sonsLen > 0): # This is either a type known to sem or a typedesc # param to a regular proc (again, known at instantiation) - result = evalTypeTrait(n, GetCurrOwner()) + result = evalTypeTrait(n[0], n[1], GetCurrOwner()) else: # a typedesc variable, pass unmodified to evals result = n diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 33c0adac1..a15b3e10a 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -729,12 +729,16 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = # like: mydata.seq rawAddSon(s.typ, newTypeS(tyEmpty, c)) s.ast = a - inc c.InGenericContext - var body = semTypeNode(c, a.sons[2], nil) - dec c.InGenericContext - if body != nil: - body.sym = s - body.size = -1 # could not be computed properly + when oUseLateInstantiation: + var body: PType = nil + s.typScope = c.currentScope.parent + else: + inc c.InGenericContext + var body = semTypeNode(c, a.sons[2], nil) + dec c.InGenericContext + if body != nil: + body.sym = s + body.size = -1 # could not be computed properly s.typ.sons[sonsLen(s.typ) - 1] = body popOwner() closeScope(c) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 8ae23f851..b02fa7c31 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -156,7 +156,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = LocalError(n.Info, errRangeIsEmpty) var a = semConstExpr(c, n[1]) var b = semConstExpr(c, n[2]) - if not sameType(a.typ, b.typ): + if not sameType(a.typ, b.typ): LocalError(n.info, errPureTypeMismatch) elif a.typ.kind notin {tyInt..tyInt64,tyEnum,tyBool,tyChar, tyFloat..tyFloat128,tyUInt8..tyUInt32}: @@ -195,17 +195,19 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = else: let e = semExprWithType(c, n.sons[1], {efDetermineType}) if e.kind in {nkIntLit..nkUInt64Lit}: - indx = newTypeS(tyRange, c) - indx.n = newNodeI(nkRange, n.info) - addSon(indx.n, newIntTypeNode(e.kind, 0, e.typ)) - addSon(indx.n, newIntTypeNode(e.kind, e.intVal-1, e.typ)) - addSonSkipIntLit(indx, e.typ) + indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ) + elif e.kind == nkSym and e.typ.kind == tyExpr: + if e.sym.ast != nil: return semArray(c, e.sym.ast, nil) + InternalAssert c.InGenericContext > 0 + if not isOrdinalType(e.typ.lastSon): + localError(n[1].info, errOrdinalTypeExpected) + indx = e.typ else: indx = e.typ.skipTypes({tyTypeDesc}) addSonSkipIntLit(result, indx) if indx.kind == tyGenericInst: indx = lastSon(indx) - if indx.kind != tyGenericParam: - if not isOrdinalType(indx): + if indx.kind notin {tyGenericParam, tyExpr}: + if not isOrdinalType(indx): LocalError(n.sons[1].info, errOrdinalTypeExpected) elif enumHasHoles(indx): LocalError(n.sons[1].info, errEnumXHasHoles, indx.sym.name.s) @@ -587,6 +589,8 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = else: if sfGenSym notin param.flags: addDecl(c, param) +let typedescId = getIdent"typedesc" + proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramType: PType, paramName: string, info: TLineInfo, anon = false): PType = @@ -634,6 +638,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = addImplicitGeneric(c.newTypeWithSons(tyExpr, paramType.sons)) of tyTypeDesc: if tfUnresolved notin paramType.flags: + # naked typedescs are not bindOnce types + if paramType.sonsLen == 0 and paramTypId != nil and + paramTypId.id == typedescId.id: paramTypId = nil result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons)) of tyDistinct: if paramType.sonsLen == 1: @@ -760,7 +767,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, r.flags.incl tfRetType result.sons[0] = skipIntLit(r) res.typ = result.sons[0] - + proc semStmtListType(c: PContext, n: PNode, prev: PType): PType = checkMinSonsLen(n, 1) var length = sonsLen(n) @@ -846,7 +853,10 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = LocalError(n.info, errCannotInstantiateX, s.name.s) result = newOrPrevType(tyError, prev, c) else: - result = instGenericContainer(c, n, result) + when oUseLateInstantiation: + result = lateInstantiateGeneric(c, result, n.info) + else: + result = instGenericContainer(c, n, result) proc semTypeExpr(c: PContext, n: PNode): PType = var n = semExprWithType(c, n, {efDetermineType}) @@ -1073,8 +1083,9 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = if constraint.kind != nkEmpty: typ = semTypeNode(c, constraint, nil) if typ.kind != tyExpr or typ.len == 0: - if typ.len == 0 and typ.kind == tyTypeDesc: - typ = newTypeS(tyGenericParam, c) + if typ.kind == tyTypeDesc: + if typ.len == 0: + typ = newTypeS(tyTypeDesc, c) else: typ = semGenericConstraints(c, typ) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 31fbc33e1..0c15c7248 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -31,7 +31,7 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) = if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: localError(info, errInheritanceOnlyWithNonFinalObjects) -proc searchInstTypes(key: PType): PType = +proc searchInstTypes*(key: PType): PType = let genericTyp = key.sons[0] InternalAssert genericTyp.kind == tyGenericBody and key.sons[0] == genericTyp and @@ -55,7 +55,7 @@ proc searchInstTypes(key: PType): PType = return inst -proc cacheTypeInst(inst: PType) = +proc cacheTypeInst*(inst: PType) = # XXX: add to module's generics # update the refcount let genericTyp = inst.sons[0] @@ -208,6 +208,12 @@ proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = of tyInt: result = skipIntLit(t) else: + if t.kind == tyArray: + let idxt = t.sons[0] + if idxt.kind == tyExpr and + idxt.sym != nil and idxt.sym.kind == skGenericParam: + let value = lookupTypeVar(cl, idxt).n + t.sons[0] = makeRangeType(cl.c, 0, value.intVal - 1, value.info) if containsGenericType(t): result = copyType(t, t.owner, false) incl(result.flags, tfFromGeneric) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 27c780391..a37b47366 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -626,7 +626,8 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = of tyGenericParam, tyTypeClass: var x = PType(idTableGet(c.bindings, f)) if x == nil: - if c.calleeSym.kind == skType and f.kind == tyGenericParam and not c.typedescMatched: + if c.calleeSym != nil and c.calleeSym.kind == skType and + f.kind == tyGenericParam and not c.typedescMatched: # XXX: The fact that generic types currently use tyGenericParam for # their parameters is really a misnomer. tyGenericParam means "match # any value" and what we need is "match any type", which can be encoded @@ -670,7 +671,9 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = result = isNone else: InternalAssert prev.sonsLen == 1 - result = typeRel(c, prev.sons[0], a) + let toMatch = if tfUnresolved in f.flags: a + else: a.sons[0] + result = typeRel(c, prev.sons[0], toMatch) of tyExpr, tyStmt: result = isGeneric of tyProxy: @@ -772,6 +775,7 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, if evaluated != nil: r = isGeneric arg.typ = newTypeS(tyExpr, c) + arg.typ.sons = @[evaluated.typ] arg.typ.n = evaluated if r == isGeneric: diff --git a/doc/manual.txt b/doc/manual.txt index d2fc7e5c9..0a2009961 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -1451,7 +1451,7 @@ But it seems all this boilerplate code needs to be repeated for the ``TEuro`` currency. This can be solved with templates_. .. code-block:: nimrod - template Additive(typ: typeDesc): stmt = + template Additive(typ: typedesc): stmt = proc `+` *(x, y: typ): typ {.borrow.} proc `-` *(x, y: typ): typ {.borrow.} @@ -1459,13 +1459,13 @@ currency. This can be solved with templates_. proc `+` *(x: typ): typ {.borrow.} proc `-` *(x: typ): typ {.borrow.} - template Multiplicative(typ, base: typeDesc): stmt = + template Multiplicative(typ, base: typedesc): stmt = proc `*` *(x: typ, y: base): typ {.borrow.} proc `*` *(x: base, y: typ): typ {.borrow.} proc `div` *(x: typ, y: base): typ {.borrow.} proc `mod` *(x: typ, y: base): typ {.borrow.} - template Comparable(typ: typeDesc): stmt = + template Comparable(typ: typedesc): stmt = proc `<` * (x, y: typ): bool {.borrow.} proc `<=` * (x, y: typ): bool {.borrow.} proc `==` * (x, y: typ): bool {.borrow.} @@ -3323,10 +3323,10 @@ The template body does not open a new scope. To open a new scope a ``block`` statement can be used: .. code-block:: nimrod - template declareInScope(x: expr, t: typeDesc): stmt {.immediate.} = + template declareInScope(x: expr, t: typedesc): stmt {.immediate.} = var x: t - template declareInNewScope(x: expr, t: typeDesc): stmt {.immediate.} = + template declareInNewScope(x: expr, t: typedesc): stmt {.immediate.} = # open a new scope: block: var x: t @@ -3419,7 +3419,7 @@ In templates identifiers can be constructed with the backticks notation: .. code-block:: nimrod - template typedef(name: expr, typ: typeDesc) {.immediate.} = + template typedef(name: expr, typ: typedesc) {.immediate.} = type `T name`* {.inject.} = typ `P name`* {.inject.} = ref `T name` @@ -3480,7 +3480,7 @@ template cannot be accessed in the instantiation context: .. code-block:: nimrod - template newException*(exceptn: typeDesc, message: string): expr = + template newException*(exceptn: typedesc, message: string): expr = var e: ref exceptn # e is implicitly gensym'ed here new(e) @@ -3728,6 +3728,25 @@ instantiation type using the param name: var n = TNode.new var tree = new(TBinaryTree[int]) +When multiple typedesc params are present, they act like a distinct type class +(i.e. they will bind freely to different types). To force a bind-once behavior +one can use a named alias or an explicit `typedesc` generic param: + +.. code-block:: nimrod + + # `type1` and `type2` are aliases for typedesc available from system.nim + proc acceptOnlyTypePairs(A, B: type1; C, D: type2) + proc acceptOnlyTypePairs[T: typedesc, U: typedesc](A, B: T; C, D: U) + +Once bound, typedesc params can appear in the rest of the proc signature: + +.. code-block:: nimrod + + template declareVariableWithType(T: typedesc, value: T) = + var x: T = value + + declareVariableWithType int, 42 + When used with macros and .compileTime. procs on the other hand, the compiler does not need to instantiate the code multiple times, because types then can be manipulated using the unified internal symbol representation. In such context diff --git a/install.txt b/install.txt index eefdfb682..11c502235 100644 --- a/install.txt +++ b/install.txt @@ -62,46 +62,3 @@ Currently, the following C compilers are supported under Windows: | http://www.digitalmars.com/download/freecompiler.html However, most testing is done with GCC. - - - -Bootstrapping from github -------------------------- - -To get the source code you need either of these: - -* A working web browser + tar(or equivalent): - https://github.com/Araq/Nimrod/tarball/master -* wget + tar: - ``wget --no-check-certificate "https://github.com/Araq/Nimrod/tarball/master"`` -* git: ``git clone git://github.com/Araq/Nimrod.git`` - -After downloading the source (and extracting it), you need to -extract ``build/csources.zip``: - -* ``cd build`` -* ``unzip csources.zip`` -* ``cd ..`` - -and then you can bootstrap with: - -On Windows -~~~~~~~~~~ - -* ``build.bat`` -* ``bin\nimrod c koch`` -* ``koch boot -d:release`` - -If you want a 64 bit build, make sure that you have a GCC built for Win64 and -execute ``build64.bat`` instead of ``build.bat``. - - -On UNIX -~~~~~~~ - -* ``./build.sh`` -* ``bin/nimrod c koch`` -* ``./koch boot -d:release`` - -Installation on UNIX can then be done with ``koch install [dir]``. - diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 15cc6abb8..2c0e7b835 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -10,6 +10,11 @@ ## This module implements a simple HTTP client that can be used to retrieve ## webpages/other data. ## +## +## **Note**: This module is not ideal, connection is not kept alive so sites with +## many redirects are expensive. As such in the future this module may change, +## and the current procedures will be deprecated. +## ## Retrieving a website ## ==================== ## @@ -62,8 +67,15 @@ ## that as long as the server is sending data an exception will not be raised, ## if however data does not reach client within the specified timeout an ETimeout ## exception will then be raised. +## +## Proxy +## ===== +## +## A proxy can be specified as a param to any of these procedures, the ``newProxy`` +## constructor should be used for this purpose. However, +## currently only basic authentication is supported. -import sockets, strutils, parseurl, parseutils, strtabs +import sockets, strutils, parseurl, parseutils, strtabs, base64 type TResponse* = tuple[ @@ -72,6 +84,10 @@ type headers: PStringTable, body: string] + PProxy* = ref object + url*: TUrl + auth*: string + EInvalidProtocol* = object of ESynch ## exception that is raised when server ## does not conform to the implemented ## protocol @@ -239,23 +255,34 @@ when not defined(ssl): else: let defaultSSLContext = newContext(verifyMode = CVerifyNone) +proc newProxy*(url: string, auth = ""): PProxy = + ## Constructs a new ``TProxy`` object. + result = PProxy(url: parseUrl(url), auth: auth) + proc request*(url: string, httpMethod = httpGET, extraHeaders = "", body = "", sslContext: PSSLContext = defaultSSLContext, - timeout = -1, userAgent = defUserAgent): TResponse = + timeout = -1, userAgent = defUserAgent, + proxy: PProxy = nil): TResponse = ## | Requests ``url`` with the specified ``httpMethod``. ## | Extra headers can be specified and must be seperated by ``\c\L`` ## | An optional timeout can be specified in miliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. - var r = parseUrl(url) + var r = if proxy == nil: parseUrl(url) else: proxy.url var headers = substr($httpMethod, len("http")) - headers.add(" /" & r.path & r.query) + if proxy == nil: + headers.add(" /" & r.path & r.query) + else: + headers.add(" " & url) headers.add(" HTTP/1.1\c\L") add(headers, "Host: " & r.hostname & "\c\L") if userAgent != "": add(headers, "User-Agent: " & userAgent & "\c\L") + if proxy != nil and proxy.auth != "": + let auth = base64.encode(proxy.auth, newline = "") + add(headers, "Proxy-Authorization: basic " & auth & "\c\L") add(headers, extraHeaders) add(headers, "\c\L") @@ -299,30 +326,34 @@ proc getNewLocation(lastUrl: string, headers: PStringTable): string = proc get*(url: string, extraHeaders = "", maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext, - timeout = -1, userAgent = defUserAgent): TResponse = + timeout = -1, userAgent = defUserAgent, + proxy: PProxy = nil): TResponse = ## | GETs the ``url`` and returns a ``TResponse`` object ## | This proc also handles redirection ## | Extra headers can be specified and must be separated by ``\c\L``. ## | An optional timeout can be specified in miliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. - result = request(url, httpGET, extraHeaders, "", sslContext, timeout, userAgent) + result = request(url, httpGET, extraHeaders, "", sslContext, timeout, + userAgent, proxy) var lastURL = url for i in 1..maxRedirects: if result.status.redirection(): let redirectTo = getNewLocation(lastURL, result.headers) result = request(redirectTo, httpGET, extraHeaders, "", sslContext, - timeout, userAgent) + timeout, userAgent, proxy) lastUrl = redirectTo proc getContent*(url: string, extraHeaders = "", maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext, - timeout = -1, userAgent = defUserAgent): string = + timeout = -1, userAgent = defUserAgent, + proxy: PProxy = nil): string = ## | GETs the body and returns it as a string. ## | Raises exceptions for the status codes ``4xx`` and ``5xx`` ## | Extra headers can be specified and must be separated by ``\c\L``. ## | An optional timeout can be specified in miliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. - var r = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent) + var r = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent, + proxy) if r.status[0] in {'4','5'}: raise newException(EHTTPRequestErr, r.status) else: @@ -331,7 +362,8 @@ proc getContent*(url: string, extraHeaders = "", maxRedirects = 5, proc post*(url: string, extraHeaders = "", body = "", maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext, - timeout = -1, userAgent = defUserAgent): TResponse = + timeout = -1, userAgent = defUserAgent, + proxy: PProxy = nil): TResponse = ## | POSTs ``body`` to the ``url`` and returns a ``TResponse`` object. ## | This proc adds the necessary Content-Length header. ## | This proc also handles redirection. @@ -339,27 +371,29 @@ proc post*(url: string, extraHeaders = "", body = "", ## | An optional timeout can be specified in miliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. var xh = extraHeaders & "Content-Length: " & $len(body) & "\c\L" - result = request(url, httpPOST, xh, body, sslContext, timeout, userAgent) + result = request(url, httpPOST, xh, body, sslContext, timeout, userAgent, + proxy) var lastUrl = "" for i in 1..maxRedirects: if result.status.redirection(): let redirectTo = getNewLocation(lastURL, result.headers) var meth = if result.status != "307": httpGet else: httpPost result = request(redirectTo, meth, xh, body, sslContext, timeout, - userAgent) + userAgent, proxy) lastUrl = redirectTo proc postContent*(url: string, extraHeaders = "", body = "", maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext, - timeout = -1, userAgent = defUserAgent): string = + timeout = -1, userAgent = defUserAgent, + proxy: PProxy = nil): string = ## | POSTs ``body`` to ``url`` and returns the response's body as a string ## | Raises exceptions for the status codes ``4xx`` and ``5xx`` ## | Extra headers can be specified and must be separated by ``\c\L``. ## | An optional timeout can be specified in miliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout, - userAgent) + userAgent, proxy) if r.status[0] in {'4','5'}: raise newException(EHTTPRequestErr, r.status) else: @@ -367,14 +401,15 @@ proc postContent*(url: string, extraHeaders = "", body = "", proc downloadFile*(url: string, outputFilename: string, sslContext: PSSLContext = defaultSSLContext, - timeout = -1, userAgent = defUserAgent) = + timeout = -1, userAgent = defUserAgent, + proxy: PProxy = nil) = ## | Downloads ``url`` and saves it to ``outputFilename`` ## | An optional timeout can be specified in miliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. var f: TFile if open(f, outputFilename, fmWrite): f.write(getContent(url, sslContext = sslContext, timeout = timeout, - userAgent = userAgent)) + userAgent = userAgent, proxy = proxy)) f.close() else: fileError("Unable to open file") diff --git a/lib/system.nim b/lib/system.nim index 98c79de5f..40a9be2d4 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -54,7 +54,7 @@ type `nil` {.magic: "Nil".} expr* {.magic: Expr.} ## meta type to denote an expression (for templates) stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates) - typeDesc* {.magic: TypeDesc.} ## meta type to denote a type description + typedesc* {.magic: TypeDesc.} ## meta type to denote a type description void* {.magic: "VoidType".} ## meta type to denote the absense of any type auto* = expr any* = distinct auto @@ -78,6 +78,17 @@ type TNumber* = TInteger|TReal ## type class matching all number types +type + ## helper types for writing implicitly generic procs + T1* = expr + T2* = expr + T3* = expr + T4* = expr + T5* = expr + type1* = typedesc + type2* = typedesc + type3* = typedesc + proc defined*(x: expr): bool {.magic: "Defined", noSideEffect.} ## Special compile-time procedure that checks whether `x` is ## defined. `x` has to be an identifier or a qualified identifier. @@ -1487,7 +1498,7 @@ when not defined(NimrodVM): proc seqToPtr[T](x: seq[T]): pointer {.noStackFrame, nosideeffect.} = asm """return `x`""" - proc `==` *[T: typeDesc](x, y: seq[T]): bool {.noSideEffect.} = + proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} = ## Generic equals operator for sequences: relies on a equals operator for ## the element type `T`. if seqToPtr(x) == seqToPtr(y): @@ -1499,7 +1510,7 @@ when not defined(NimrodVM): if x[i] != y[i]: return false result = true -proc find*[T, S: typeDesc](a: T, item: S): int {.inline.}= +proc find*[T, S](a: T, item: S): int {.inline.}= ## Returns the first index of `item` in `a` or -1 if not found. This requires ## appropriate `items` and `==` operations to work. for i in items(a): @@ -1802,7 +1813,7 @@ proc debugEcho*[T](x: varargs[T, `$`]) {.magic: "Echo", noSideEffect, ## to be free of side effects, so that it can be used for debugging routines ## marked as ``noSideEffect``. -template newException*(exceptn: typeDesc, message: string): expr = +template newException*(exceptn: typedesc, message: string): expr = ## creates an exception object of type ``exceptn`` and sets its ``msg`` field ## to `message`. Returns the new exception object. var @@ -1815,7 +1826,7 @@ when hostOS == "standalone": include panicoverride when not defined(sysFatal): - template sysFatal(exceptn: typeDesc, message: string) = + template sysFatal(exceptn: typedesc, message: string) = when hostOS == "standalone": panic(message) else: @@ -1824,7 +1835,7 @@ when not defined(sysFatal): e.msg = message raise e - template sysFatal(exceptn: typeDesc, message, arg: string) = + template sysFatal(exceptn: typedesc, message, arg: string) = when hostOS == "standalone": rawoutput(message) panic(arg) diff --git a/readme.md b/readme.md index 59546653b..f74f81283 100644 --- a/readme.md +++ b/readme.md @@ -6,8 +6,7 @@ documentation. Compiling the Nimrod compiler is quite straightforward. Because the Nimrod compiler itself is written in the Nimrod programming language the C source of an older version of the compiler are needed to bootstrap the -latest version. The C sources are however included with this repository under -the build directory. +latest version. The C sources are available in a separate repo [here](http://github.com/nimrod-code/csources). Pre-compiled snapshots of the compiler are also available on [Nimbuild](http://build.nimrod-code.org/). Your platform however may not @@ -53,7 +52,7 @@ and you can also get help in the IRC channel on [Freenode](irc://irc.freenode.net/nimrod) in #nimrod. ## License -The compiler and the standard library is licensed under the MIT license, +The compiler and the standard library are licensed under the MIT license, except for some modules where the documentation suggests otherwise. This means that you can use any license for your own programs developed with Nimrod, allowing you to create commercial applications. diff --git a/readme.txt b/readme.txt index 59546653b..f74f81283 100644 --- a/readme.txt +++ b/readme.txt @@ -6,8 +6,7 @@ documentation. Compiling the Nimrod compiler is quite straightforward. Because the Nimrod compiler itself is written in the Nimrod programming language the C source of an older version of the compiler are needed to bootstrap the -latest version. The C sources are however included with this repository under -the build directory. +latest version. The C sources are available in a separate repo [here](http://github.com/nimrod-code/csources). Pre-compiled snapshots of the compiler are also available on [Nimbuild](http://build.nimrod-code.org/). Your platform however may not @@ -53,7 +52,7 @@ and you can also get help in the IRC channel on [Freenode](irc://irc.freenode.net/nimrod) in #nimrod. ## License -The compiler and the standard library is licensed under the MIT license, +The compiler and the standard library are licensed under the MIT license, except for some modules where the documentation suggests otherwise. This means that you can use any license for your own programs developed with Nimrod, allowing you to create commercial applications. diff --git a/tests/compile/tbindtypedesc.nim b/tests/compile/tbindtypedesc.nim new file mode 100644 index 000000000..dd4ef854c --- /dev/null +++ b/tests/compile/tbindtypedesc.nim @@ -0,0 +1,87 @@ +discard """ + msg: ''' +int +float +TFoo +TFoo +''' +""" + +import typetraits + +type + TFoo = object + x, y: int + + TBar = tuple + x, y: int + +template good(e: expr) = + static: assert(compiles(e)) + +template bad(e: expr) = + static: assert(not compiles(e)) + +proc genericParamRepeated[T: typedesc](a: T, b: T) = + static: + echo a.name + echo b.name + +good(genericParamRepeated(int, int)) +good(genericParamRepeated(float, float)) + +bad(genericParamRepeated(string, int)) +bad(genericParamRepeated(int, float)) + +proc genericParamOnce[T: typedesc](a, b: T) = + static: + echo a.name + echo b.name + +good(genericParamOnce(int, int)) +good(genericParamOnce(TFoo, TFoo)) + +bad(genericParamOnce(string, int)) +bad(genericParamOnce(TFoo, float)) + +proc typePairs(A, B: type1; C, D: type2) = nil + +good(typePairs(int, int, TFoo, TFOO)) +good(typePairs(TBAR, TBar, TBAR, TBAR)) +good(typePairs(int, int, string, string)) + +bad(typePairs(TBAR, TBar, TBar, TFoo)) +bad(typePairs(string, int, TBAR, TBAR)) + +proc typePairs2[T: typedesc, U: typedesc](A, B: T; C, D: U) = nil + +good(typePairs2(int, int, TFoo, TFOO)) +good(typePairs2(TBAR, TBar, TBAR, TBAR)) +good(typePairs2(int, int, string, string)) + +bad(typePairs2(TBAR, TBar, TBar, TFoo)) +bad(typePairs2(string, int, TBAR, TBAR)) + +proc dontBind(a: typedesc, b: typedesc) = + static: + echo a.name + echo b.name + +good(dontBind(int, float)) +good(dontBind(TFoo, TFoo)) + +proc dontBind2(a, b: typedesc) = nil + +good(dontBind2(int, float)) +good(dontBind2(TBar, int)) + +proc bindArg(T: typedesc, U: typedesc, a, b: T, c, d: U) = nil + +good(bindArg(int, string, 10, 20, "test", "nest")) +good(bindArg(int, int, 10, 20, 30, 40)) + +bad(bindArg(int, string, 10, "test", "test", "nest")) +bad(bindArg(int, int, 10, 20, 30, "test")) +bad(bindArg(int, string, 10.0, 20, "test", "nest")) +bad(bindArg(int, string, "test", "nest", 10, 20)) + diff --git a/tests/compile/tgenericshardcases.nim b/tests/compile/tgenericshardcases.nim new file mode 100644 index 000000000..90981c701 --- /dev/null +++ b/tests/compile/tgenericshardcases.nim @@ -0,0 +1,30 @@ +discard """ + file: "tgenericshardcases.nim" + output: "int\nfloat\nint\nstring" +""" + +import typetraits + +proc typeNameLen(x: typedesc): int {.compileTime.} = + result = x.name.len + +macro selectType(a, b: typedesc): typedesc = + result = a + +type + Foo[T] = object + data1: array[high(T), int] + data2: array[1..typeNameLen(T), selectType(float, string)] + + MyEnum = enum A, B, C,D + +var f1: Foo[MyEnum] +var f2: Foo[int8] + +static: + assert high(f1.data1) == D + assert high(f1.data2) == 6 # length of MyEnum + + assert high(f2.data1) == 127 + assert high(f2.data2) == 4 # length of int8 + diff --git a/tests/run/tmacrogenerics.nim b/tests/run/tmacrogenerics.nim new file mode 100644 index 000000000..5ae59e0da --- /dev/null +++ b/tests/run/tmacrogenerics.nim @@ -0,0 +1,39 @@ +discard """ + file: "tmacrogenerics.nim" + msg: ''' +instantiation 1 with int and float +instantiation 2 with float and string +instantiation 3 with string and string +counter: 3 +''' + output: "int\nfloat\nint\nstring" +""" + +import typetraits, macros + +var counter {.compileTime.} = 0 + +macro makeBar(A, B: typedesc): typedesc = + inc counter + echo "instantiation ", counter, " with ", A.name, " and ", B.name + result = A + +type + Bar[T, U] = makeBar(T, U) + +var bb1: Bar[int, float] +var bb2: Bar[float, string] +var bb3: Bar[int, float] +var bb4: Bar[string, string] + +proc match(a: int) = echo "int" +proc match(a: string) = echo "string" +proc match(a: float) = echo "float" + +match(bb1) +match(bb2) +match(bb3) +match(bb4) + +static: + echo "counter: ", counter diff --git a/tests/run/tstaticparams.nim b/tests/run/tstaticparams.nim index 3e501ed8b..232748356 100644 --- a/tests/run/tstaticparams.nim +++ b/tests/run/tstaticparams.nim @@ -1,17 +1,22 @@ discard """ file: "tstaticparams.nim" - output: "abracadabra\ntest" + output: "abracadabra\ntest\n3" """ type TFoo[T; Val: expr[string]] = object data: array[4, T] + TBar[T; I: expr[int]] = object + data: array[I, T] + proc takeFoo(x: TFoo) = echo "abracadabra" echo TFoo.Val var x: TFoo[int, "test"] - takeFoo(x) +var y: TBar[float, 4] +echo high(y.data) + |