Merge branch 'master' of github.com:Araq/Nimrod

This commit is contained in:
Araq 2013-08-30 12:44:27 +02:00
commit 8710118b2c
22 changed files with 384 additions and 133 deletions

View file

@ -626,6 +626,7 @@ type
case kind*: TSymKind case kind*: TSymKind
of skType: of skType:
typeInstCache*: seq[PType] typeInstCache*: seq[PType]
typScope*: PScope
of routineKinds: of routineKinds:
procInstCache*: seq[PInstantiation] procInstCache*: seq[PInstantiation]
scope*: PScope # the scope where the proc was defined scope*: PScope # the scope where the proc was defined
@ -799,9 +800,9 @@ const
# imported via 'importc: "fullname"' and no format string. # imported via 'importc: "fullname"' and no format string.
# creator procs: # creator procs:
proc NewSym*(symKind: TSymKind, Name: PIdent, owner: PSym, proc newSym*(symKind: TSymKind, Name: PIdent, owner: PSym,
info: TLineInfo): PSym info: TLineInfo): PSym
proc NewType*(kind: TTypeKind, owner: PSym): PType proc newType*(kind: TTypeKind, owner: PSym): PType
proc newNode*(kind: TNodeKind): PNode proc newNode*(kind: TNodeKind): PNode
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode
proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): 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.loc = s.loc
result.annex = s.annex # BUGFIX result.annex = s.annex # BUGFIX
proc NewSym(symKind: TSymKind, Name: PIdent, owner: PSym, proc newSym(symKind: TSymKind, Name: PIdent, owner: PSym,
info: TLineInfo): PSym = info: TLineInfo): PSym =
# generates a symbol and initializes the hash field too # generates a symbol and initializes the hash field too
new(result) new(result)

View file

@ -904,18 +904,15 @@ proc evalParseStmt(c: PEvalContext, n: PNode): PNode =
code.info.line.int) code.info.line.int)
#result.typ = newType(tyStmt, c.module) #result.typ = newType(tyStmt, c.module)
proc evalTypeTrait*(n: PNode, context: PSym): PNode = proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode =
## XXX: This should be pretty much guaranteed to be true InternalAssert operand.kind == nkSym
# 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}) let typ = operand.sym.typ.skipTypes({tyTypeDesc})
case n.sons[0].sym.name.s.normalize case trait.sym.name.s.normalize
of "name": of "name":
result = newStrNode(nkStrLit, typ.typeToString(preferExported)) result = newStrNode(nkStrLit, typ.typeToString(preferName))
result.typ = newType(tyString, context) result.typ = newType(tyString, context)
result.info = n.info result.info = trait.info
else: else:
internalAssert false internalAssert false
@ -1037,8 +1034,8 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
of mParseStmtToAst: result = evalParseStmt(c, n) of mParseStmtToAst: result = evalParseStmt(c, n)
of mExpandToAst: result = evalExpandToAst(c, n) of mExpandToAst: result = evalExpandToAst(c, n)
of mTypeTrait: of mTypeTrait:
n.sons[1] = evalAux(c, n.sons[1], {}) let operand = evalAux(c, n.sons[1], {})
result = evalTypeTrait(n, c.module) result = evalTypeTrait(n[0], operand, c.module)
of mIs: of mIs:
n.sons[1] = evalAux(c, n.sons[1], {}) n.sons[1] = evalAux(c, n.sons[1], {})
result = evalIsOp(n) result = evalIsOp(n)

View file

@ -156,6 +156,8 @@ var
const oKeepVariableNames* = true const oKeepVariableNames* = true
const oUseLateInstantiation* = false
proc mainCommandArg*: string = proc mainCommandArg*: string =
## This is intended for commands like check or parse ## This is intended for commands like check or parse
## which will work on the main project file unless ## which will work on the main project file unless

View file

@ -225,14 +225,14 @@ proc fillTypeS(dest: PType, kind: TTypeKind, c: PContext) =
dest.owner = getCurrOwner() dest.owner = getCurrOwner()
dest.size = - 1 dest.size = - 1
proc makeRangeType*(c: PContext, first, last: biggestInt, proc makeRangeType*(c: PContext; first, last: biggestInt;
info: TLineInfo): PType = info: TLineInfo; intType = getSysType(tyInt)): PType =
var n = newNodeI(nkRange, info) var n = newNodeI(nkRange, info)
addSon(n, newIntNode(nkIntLit, first)) addSon(n, newIntTypeNode(nkIntLit, first, intType))
addSon(n, newIntNode(nkIntLit, last)) addSon(n, newIntTypeNode(nkIntLit, last, intType))
result = newTypeS(tyRange, c) result = newTypeS(tyRange, c)
result.n = n result.n = n
rawAddSon(result, getSysType(tyInt)) # basetype of range addSonSkipIntLit(result, intType) # basetype of range
proc markIndirect*(c: PContext, s: PSym) {.inline.} = proc markIndirect*(c: PContext, s: PSym) {.inline.} =
if s.kind in {skProc, skConverter, skMethod, skIterator}: if s.kind in {skProc, skConverter, skMethod, skIterator}:

View file

@ -113,7 +113,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
of skGenericParam: of skGenericParam:
if s.typ.kind == tyExpr: if s.typ.kind == tyExpr:
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
result.typ = s.typ.lastSon result.typ = s.typ
elif s.ast != nil: elif s.ast != nil:
result = semExpr(c, s.ast) result = semExpr(c, s.ast)
else: else:

View file

@ -561,9 +561,10 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
case n.kind case n.kind
of nkSym: of nkSym:
var s = n.sym var s = n.sym
if s.kind == skEnumField: case s.kind
of skEnumField:
result = newIntNodeT(s.position, n) result = newIntNodeT(s.position, n)
elif s.kind == skConst: of skConst:
case s.magic case s.magic
of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n) of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n)
of mCompileDate: result = newStrNodeT(times.getDateStr(), 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) of mNegInf: result = newFloatNodeT(NegInf, n)
else: else:
if sfFakeConst notin s.flags: result = copyTree(s.ast) if sfFakeConst notin s.flags: result = copyTree(s.ast)
elif s.kind in {skProc, skMethod}: # BUGFIX of {skProc, skMethod}:
result = n result = n
elif s.kind in {skType, skGenericParam}: of skType:
result = newSymNodeTypeDesc(s, n.info) 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: of nkCharLit..nkNilLit:
result = copyNode(n) result = copyNode(n)
of nkIfExpr: of nkIfExpr:

View file

@ -130,7 +130,44 @@ proc sideEffectsCheck(c: PContext, s: PSym) =
s.ast.sons[genericParamsPos].kind == nkEmpty: s.ast.sons[genericParamsPos].kind == nkEmpty:
c.threadEntries.add(s) 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 = proc instGenericContainer(c: PContext, info: TLineInfo, header: PType): PType =
when oUseLateInstantiation:
lateInstantiateGeneric(c, header, info)
else:
var cl: TReplTypeVars var cl: TReplTypeVars
InitIdTable(cl.symMap) InitIdTable(cl.symMap)
InitIdTable(cl.typeMap) InitIdTable(cl.typeMap)

View file

@ -40,7 +40,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
(typArg.kind == skParam and typArg.typ.sonsLen > 0): (typArg.kind == skParam and typArg.typ.sonsLen > 0):
# This is either a type known to sem or a typedesc # This is either a type known to sem or a typedesc
# param to a regular proc (again, known at instantiation) # param to a regular proc (again, known at instantiation)
result = evalTypeTrait(n, GetCurrOwner()) result = evalTypeTrait(n[0], n[1], GetCurrOwner())
else: else:
# a typedesc variable, pass unmodified to evals # a typedesc variable, pass unmodified to evals
result = n result = n

View file

@ -729,6 +729,10 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
# like: mydata.seq # like: mydata.seq
rawAddSon(s.typ, newTypeS(tyEmpty, c)) rawAddSon(s.typ, newTypeS(tyEmpty, c))
s.ast = a s.ast = a
when oUseLateInstantiation:
var body: PType = nil
s.typScope = c.currentScope.parent
else:
inc c.InGenericContext inc c.InGenericContext
var body = semTypeNode(c, a.sons[2], nil) var body = semTypeNode(c, a.sons[2], nil)
dec c.InGenericContext dec c.InGenericContext

View file

@ -195,16 +195,18 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
else: else:
let e = semExprWithType(c, n.sons[1], {efDetermineType}) let e = semExprWithType(c, n.sons[1], {efDetermineType})
if e.kind in {nkIntLit..nkUInt64Lit}: if e.kind in {nkIntLit..nkUInt64Lit}:
indx = newTypeS(tyRange, c) indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
indx.n = newNodeI(nkRange, n.info) elif e.kind == nkSym and e.typ.kind == tyExpr:
addSon(indx.n, newIntTypeNode(e.kind, 0, e.typ)) if e.sym.ast != nil: return semArray(c, e.sym.ast, nil)
addSon(indx.n, newIntTypeNode(e.kind, e.intVal-1, e.typ)) InternalAssert c.InGenericContext > 0
addSonSkipIntLit(indx, e.typ) if not isOrdinalType(e.typ.lastSon):
localError(n[1].info, errOrdinalTypeExpected)
indx = e.typ
else: else:
indx = e.typ.skipTypes({tyTypeDesc}) indx = e.typ.skipTypes({tyTypeDesc})
addSonSkipIntLit(result, indx) addSonSkipIntLit(result, indx)
if indx.kind == tyGenericInst: indx = lastSon(indx) if indx.kind == tyGenericInst: indx = lastSon(indx)
if indx.kind != tyGenericParam: if indx.kind notin {tyGenericParam, tyExpr}:
if not isOrdinalType(indx): if not isOrdinalType(indx):
LocalError(n.sons[1].info, errOrdinalTypeExpected) LocalError(n.sons[1].info, errOrdinalTypeExpected)
elif enumHasHoles(indx): elif enumHasHoles(indx):
@ -587,6 +589,8 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
else: else:
if sfGenSym notin param.flags: addDecl(c, param) if sfGenSym notin param.flags: addDecl(c, param)
let typedescId = getIdent"typedesc"
proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
paramType: PType, paramName: string, paramType: PType, paramName: string,
info: TLineInfo, anon = false): PType = info: TLineInfo, anon = false): PType =
@ -634,6 +638,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
result = addImplicitGeneric(c.newTypeWithSons(tyExpr, paramType.sons)) result = addImplicitGeneric(c.newTypeWithSons(tyExpr, paramType.sons))
of tyTypeDesc: of tyTypeDesc:
if tfUnresolved notin paramType.flags: 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)) result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons))
of tyDistinct: of tyDistinct:
if paramType.sonsLen == 1: if paramType.sonsLen == 1:
@ -845,6 +852,9 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
if s.ast == nil: if s.ast == nil:
LocalError(n.info, errCannotInstantiateX, s.name.s) LocalError(n.info, errCannotInstantiateX, s.name.s)
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
else:
when oUseLateInstantiation:
result = lateInstantiateGeneric(c, result, n.info)
else: else:
result = instGenericContainer(c, n, result) result = instGenericContainer(c, n, result)
@ -1073,8 +1083,9 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
if constraint.kind != nkEmpty: if constraint.kind != nkEmpty:
typ = semTypeNode(c, constraint, nil) typ = semTypeNode(c, constraint, nil)
if typ.kind != tyExpr or typ.len == 0: if typ.kind != tyExpr or typ.len == 0:
if typ.len == 0 and typ.kind == tyTypeDesc: if typ.kind == tyTypeDesc:
typ = newTypeS(tyGenericParam, c) if typ.len == 0:
typ = newTypeS(tyTypeDesc, c)
else: else:
typ = semGenericConstraints(c, typ) typ = semGenericConstraints(c, typ)

View file

@ -31,7 +31,7 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
localError(info, errInheritanceOnlyWithNonFinalObjects) localError(info, errInheritanceOnlyWithNonFinalObjects)
proc searchInstTypes(key: PType): PType = proc searchInstTypes*(key: PType): PType =
let genericTyp = key.sons[0] let genericTyp = key.sons[0]
InternalAssert genericTyp.kind == tyGenericBody and InternalAssert genericTyp.kind == tyGenericBody and
key.sons[0] == genericTyp and key.sons[0] == genericTyp and
@ -55,7 +55,7 @@ proc searchInstTypes(key: PType): PType =
return inst return inst
proc cacheTypeInst(inst: PType) = proc cacheTypeInst*(inst: PType) =
# XXX: add to module's generics # XXX: add to module's generics
# update the refcount # update the refcount
let genericTyp = inst.sons[0] let genericTyp = inst.sons[0]
@ -208,6 +208,12 @@ proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
of tyInt: of tyInt:
result = skipIntLit(t) result = skipIntLit(t)
else: 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): if containsGenericType(t):
result = copyType(t, t.owner, false) result = copyType(t, t.owner, false)
incl(result.flags, tfFromGeneric) incl(result.flags, tfFromGeneric)

View file

@ -626,7 +626,8 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
of tyGenericParam, tyTypeClass: of tyGenericParam, tyTypeClass:
var x = PType(idTableGet(c.bindings, f)) var x = PType(idTableGet(c.bindings, f))
if x == nil: 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 # XXX: The fact that generic types currently use tyGenericParam for
# their parameters is really a misnomer. tyGenericParam means "match # their parameters is really a misnomer. tyGenericParam means "match
# any value" and what we need is "match any type", which can be encoded # 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 result = isNone
else: else:
InternalAssert prev.sonsLen == 1 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: of tyExpr, tyStmt:
result = isGeneric result = isGeneric
of tyProxy: of tyProxy:
@ -772,6 +775,7 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
if evaluated != nil: if evaluated != nil:
r = isGeneric r = isGeneric
arg.typ = newTypeS(tyExpr, c) arg.typ = newTypeS(tyExpr, c)
arg.typ.sons = @[evaluated.typ]
arg.typ.n = evaluated arg.typ.n = evaluated
if r == isGeneric: if r == isGeneric:

View file

@ -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_. currency. This can be solved with templates_.
.. code-block:: nimrod .. 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.}
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.}
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: typ, y: base): typ {.borrow.}
proc `*` *(x: base, y: typ): typ {.borrow.} proc `*` *(x: base, y: typ): typ {.borrow.}
proc `div` *(x: typ, y: base): typ {.borrow.} proc `div` *(x: typ, y: base): typ {.borrow.}
proc `mod` *(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.} 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: statement can be used:
.. code-block:: nimrod .. code-block:: nimrod
template declareInScope(x: expr, t: typeDesc): stmt {.immediate.} = template declareInScope(x: expr, t: typedesc): stmt {.immediate.} =
var x: t var x: t
template declareInNewScope(x: expr, t: typeDesc): stmt {.immediate.} = template declareInNewScope(x: expr, t: typedesc): stmt {.immediate.} =
# open a new scope: # open a new scope:
block: block:
var x: t var x: t
@ -3419,7 +3419,7 @@ In templates identifiers can be constructed with the backticks notation:
.. code-block:: nimrod .. code-block:: nimrod
template typedef(name: expr, typ: typeDesc) {.immediate.} = template typedef(name: expr, typ: typedesc) {.immediate.} =
type type
`T name`* {.inject.} = typ `T name`* {.inject.} = typ
`P name`* {.inject.} = ref `T name` `P name`* {.inject.} = ref `T name`
@ -3480,7 +3480,7 @@ template cannot be accessed in the instantiation context:
.. code-block:: nimrod .. code-block:: nimrod
template newException*(exceptn: typeDesc, message: string): expr = template newException*(exceptn: typedesc, message: string): expr =
var var
e: ref exceptn # e is implicitly gensym'ed here e: ref exceptn # e is implicitly gensym'ed here
new(e) new(e)
@ -3728,6 +3728,25 @@ instantiation type using the param name:
var n = TNode.new var n = TNode.new
var tree = new(TBinaryTree[int]) 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 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 does not need to instantiate the code multiple times, because types then can be
manipulated using the unified internal symbol representation. In such context manipulated using the unified internal symbol representation. In such context

View file

@ -62,46 +62,3 @@ Currently, the following C compilers are supported under Windows:
| http://www.digitalmars.com/download/freecompiler.html | http://www.digitalmars.com/download/freecompiler.html
However, most testing is done with GCC. 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]``.

View file

@ -10,6 +10,11 @@
## This module implements a simple HTTP client that can be used to retrieve ## This module implements a simple HTTP client that can be used to retrieve
## webpages/other data. ## 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 ## Retrieving a website
## ==================== ## ====================
## ##
@ -62,8 +67,15 @@
## that as long as the server is sending data an exception will not be raised, ## 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 ## if however data does not reach client within the specified timeout an ETimeout
## exception will then be raised. ## 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 type
TResponse* = tuple[ TResponse* = tuple[
@ -72,6 +84,10 @@ type
headers: PStringTable, headers: PStringTable,
body: string] body: string]
PProxy* = ref object
url*: TUrl
auth*: string
EInvalidProtocol* = object of ESynch ## exception that is raised when server EInvalidProtocol* = object of ESynch ## exception that is raised when server
## does not conform to the implemented ## does not conform to the implemented
## protocol ## protocol
@ -239,23 +255,34 @@ when not defined(ssl):
else: else:
let defaultSSLContext = newContext(verifyMode = CVerifyNone) 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 = "", proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
body = "", body = "",
sslContext: PSSLContext = defaultSSLContext, sslContext: PSSLContext = defaultSSLContext,
timeout = -1, userAgent = defUserAgent): TResponse = timeout = -1, userAgent = defUserAgent,
proxy: PProxy = nil): TResponse =
## | Requests ``url`` with the specified ``httpMethod``. ## | Requests ``url`` with the specified ``httpMethod``.
## | Extra headers can be specified and must be seperated by ``\c\L`` ## | Extra headers can be specified and must be seperated 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 r = parseUrl(url) var r = if proxy == nil: parseUrl(url) else: proxy.url
var headers = substr($httpMethod, len("http")) var headers = substr($httpMethod, len("http"))
if proxy == nil:
headers.add(" /" & r.path & r.query) headers.add(" /" & r.path & r.query)
else:
headers.add(" " & url)
headers.add(" HTTP/1.1\c\L") headers.add(" HTTP/1.1\c\L")
add(headers, "Host: " & r.hostname & "\c\L") add(headers, "Host: " & r.hostname & "\c\L")
if userAgent != "": if userAgent != "":
add(headers, "User-Agent: " & userAgent & "\c\L") 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, extraHeaders)
add(headers, "\c\L") add(headers, "\c\L")
@ -299,30 +326,34 @@ proc getNewLocation(lastUrl: string, headers: PStringTable): string =
proc get*(url: string, extraHeaders = "", maxRedirects = 5, proc get*(url: string, extraHeaders = "", maxRedirects = 5,
sslContext: PSSLContext = defaultSSLContext, sslContext: PSSLContext = defaultSSLContext,
timeout = -1, userAgent = defUserAgent): TResponse = timeout = -1, userAgent = defUserAgent,
proxy: PProxy = nil): TResponse =
## | GETs the ``url`` and returns a ``TResponse`` object ## | GETs the ``url`` and returns a ``TResponse`` object
## | 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.
result = request(url, httpGET, extraHeaders, "", sslContext, timeout, userAgent) result = request(url, httpGET, extraHeaders, "", sslContext, timeout,
userAgent, proxy)
var lastURL = url var lastURL = url
for i in 1..maxRedirects: for i in 1..maxRedirects:
if result.status.redirection(): if result.status.redirection():
let redirectTo = getNewLocation(lastURL, result.headers) let redirectTo = getNewLocation(lastURL, result.headers)
result = request(redirectTo, httpGET, extraHeaders, "", sslContext, result = request(redirectTo, httpGET, extraHeaders, "", sslContext,
timeout, userAgent) timeout, userAgent, proxy)
lastUrl = redirectTo lastUrl = redirectTo
proc getContent*(url: string, extraHeaders = "", maxRedirects = 5, proc getContent*(url: string, extraHeaders = "", maxRedirects = 5,
sslContext: PSSLContext = defaultSSLContext, 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. ## | GETs the body and returns it 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.
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'}: if r.status[0] in {'4','5'}:
raise newException(EHTTPRequestErr, r.status) raise newException(EHTTPRequestErr, r.status)
else: else:
@ -331,7 +362,8 @@ proc getContent*(url: string, extraHeaders = "", maxRedirects = 5,
proc post*(url: string, extraHeaders = "", body = "", proc post*(url: string, extraHeaders = "", body = "",
maxRedirects = 5, maxRedirects = 5,
sslContext: PSSLContext = defaultSSLContext, 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. ## | POSTs ``body`` to the ``url`` and returns a ``TResponse`` 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.
@ -339,27 +371,29 @@ proc post*(url: string, extraHeaders = "", body = "",
## | 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" 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 = "" var lastUrl = ""
for i in 1..maxRedirects: for i in 1..maxRedirects:
if result.status.redirection(): if result.status.redirection():
let redirectTo = getNewLocation(lastURL, result.headers) let redirectTo = getNewLocation(lastURL, result.headers)
var meth = if result.status != "307": httpGet else: httpPost var meth = if result.status != "307": httpGet else: httpPost
result = request(redirectTo, meth, xh, body, sslContext, timeout, result = request(redirectTo, meth, xh, body, sslContext, timeout,
userAgent) userAgent, proxy)
lastUrl = redirectTo lastUrl = redirectTo
proc postContent*(url: string, extraHeaders = "", body = "", proc postContent*(url: string, extraHeaders = "", body = "",
maxRedirects = 5, maxRedirects = 5,
sslContext: PSSLContext = defaultSSLContext, 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 ## | 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.
var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout, var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout,
userAgent) userAgent, proxy)
if r.status[0] in {'4','5'}: if r.status[0] in {'4','5'}:
raise newException(EHTTPRequestErr, r.status) raise newException(EHTTPRequestErr, r.status)
else: else:
@ -367,14 +401,15 @@ proc postContent*(url: string, extraHeaders = "", body = "",
proc downloadFile*(url: string, outputFilename: string, proc downloadFile*(url: string, outputFilename: string,
sslContext: PSSLContext = defaultSSLContext, sslContext: PSSLContext = defaultSSLContext,
timeout = -1, userAgent = defUserAgent) = timeout = -1, userAgent = defUserAgent,
proxy: PProxy = nil) =
## | Downloads ``url`` and saves it to ``outputFilename`` ## | Downloads ``url`` and saves it to ``outputFilename``
## | 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 f: TFile var f: TFile
if open(f, outputFilename, fmWrite): if open(f, outputFilename, fmWrite):
f.write(getContent(url, sslContext = sslContext, timeout = timeout, f.write(getContent(url, sslContext = sslContext, timeout = timeout,
userAgent = userAgent)) userAgent = userAgent, proxy = proxy))
f.close() f.close()
else: else:
fileError("Unable to open file") fileError("Unable to open file")

View file

@ -54,7 +54,7 @@ type
`nil` {.magic: "Nil".} `nil` {.magic: "Nil".}
expr* {.magic: Expr.} ## meta type to denote an expression (for templates) expr* {.magic: Expr.} ## meta type to denote an expression (for templates)
stmt* {.magic: Stmt.} ## meta type to denote a statement (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 void* {.magic: "VoidType".} ## meta type to denote the absense of any type
auto* = expr auto* = expr
any* = distinct auto any* = distinct auto
@ -78,6 +78,17 @@ type
TNumber* = TInteger|TReal TNumber* = TInteger|TReal
## type class matching all number types ## 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.} proc defined*(x: expr): bool {.magic: "Defined", noSideEffect.}
## Special compile-time procedure that checks whether `x` is ## Special compile-time procedure that checks whether `x` is
## defined. `x` has to be an identifier or a qualified identifier. ## 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.} = proc seqToPtr[T](x: seq[T]): pointer {.noStackFrame, nosideeffect.} =
asm """return `x`""" 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 ## Generic equals operator for sequences: relies on a equals operator for
## the element type `T`. ## the element type `T`.
if seqToPtr(x) == seqToPtr(y): if seqToPtr(x) == seqToPtr(y):
@ -1499,7 +1510,7 @@ when not defined(NimrodVM):
if x[i] != y[i]: return false if x[i] != y[i]: return false
result = true 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 ## Returns the first index of `item` in `a` or -1 if not found. This requires
## appropriate `items` and `==` operations to work. ## appropriate `items` and `==` operations to work.
for i in items(a): 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 ## to be free of side effects, so that it can be used for debugging routines
## marked as ``noSideEffect``. ## 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 ## creates an exception object of type ``exceptn`` and sets its ``msg`` field
## to `message`. Returns the new exception object. ## to `message`. Returns the new exception object.
var var
@ -1815,7 +1826,7 @@ when hostOS == "standalone":
include panicoverride include panicoverride
when not defined(sysFatal): when not defined(sysFatal):
template sysFatal(exceptn: typeDesc, message: string) = template sysFatal(exceptn: typedesc, message: string) =
when hostOS == "standalone": when hostOS == "standalone":
panic(message) panic(message)
else: else:
@ -1824,7 +1835,7 @@ when not defined(sysFatal):
e.msg = message e.msg = message
raise e raise e
template sysFatal(exceptn: typeDesc, message, arg: string) = template sysFatal(exceptn: typedesc, message, arg: string) =
when hostOS == "standalone": when hostOS == "standalone":
rawoutput(message) rawoutput(message)
panic(arg) panic(arg)

View file

@ -6,8 +6,7 @@ documentation.
Compiling the Nimrod compiler is quite straightforward. Because Compiling the Nimrod compiler is quite straightforward. Because
the Nimrod compiler itself is written in the Nimrod programming language 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 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 latest version. The C sources are available in a separate repo [here](http://github.com/nimrod-code/csources).
the build directory.
Pre-compiled snapshots of the compiler are also available on Pre-compiled snapshots of the compiler are also available on
[Nimbuild](http://build.nimrod-code.org/). Your platform however may not [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. on [Freenode](irc://irc.freenode.net/nimrod) in #nimrod.
## License ## 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 except for some modules where the documentation suggests otherwise. This means
that you can use any license for your own programs developed with Nimrod, that you can use any license for your own programs developed with Nimrod,
allowing you to create commercial applications. allowing you to create commercial applications.

View file

@ -6,8 +6,7 @@ documentation.
Compiling the Nimrod compiler is quite straightforward. Because Compiling the Nimrod compiler is quite straightforward. Because
the Nimrod compiler itself is written in the Nimrod programming language 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 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 latest version. The C sources are available in a separate repo [here](http://github.com/nimrod-code/csources).
the build directory.
Pre-compiled snapshots of the compiler are also available on Pre-compiled snapshots of the compiler are also available on
[Nimbuild](http://build.nimrod-code.org/). Your platform however may not [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. on [Freenode](irc://irc.freenode.net/nimrod) in #nimrod.
## License ## 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 except for some modules where the documentation suggests otherwise. This means
that you can use any license for your own programs developed with Nimrod, that you can use any license for your own programs developed with Nimrod,
allowing you to create commercial applications. allowing you to create commercial applications.

View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -1,17 +1,22 @@
discard """ discard """
file: "tstaticparams.nim" file: "tstaticparams.nim"
output: "abracadabra\ntest" output: "abracadabra\ntest\n3"
""" """
type type
TFoo[T; Val: expr[string]] = object TFoo[T; Val: expr[string]] = object
data: array[4, T] data: array[4, T]
TBar[T; I: expr[int]] = object
data: array[I, T]
proc takeFoo(x: TFoo) = proc takeFoo(x: TFoo) =
echo "abracadabra" echo "abracadabra"
echo TFoo.Val echo TFoo.Val
var x: TFoo[int, "test"] var x: TFoo[int, "test"]
takeFoo(x) takeFoo(x)
var y: TBar[float, 4]
echo high(y.data)