diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 index 81bfbad89..59b76e242 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ compiler/c2nim/nimcache compiler/pas2nim/nimcache misc doc/*.html +doc/*.pdf doc/*.idx /web/upload koch @@ -28,6 +29,8 @@ examples/cross_todo/nimrod_backend/backend examples/cross_todo/nimrod_backend/testbackend examples/cross_todo/nimrod_backend/todo.sqlite3 examples/cross_todo/nimrod_commandline/nimtodo +install.sh +deinstall.sh # iOS specific wildcards. *.mode1v3 diff --git a/build.bat b/build.bat old mode 100755 new mode 100644 diff --git a/compiler/ast.nim b/compiler/ast.nim old mode 100755 new mode 100644 index 65806ea1f..aee99c833 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -71,21 +71,25 @@ type nkDotCall, # used to temporarily flag a nkCall node; # this is used # for transforming ``s.len`` to ``len(s)`` + nkCommand, # a call like ``p 2, 4`` without parenthesis nkCall, # a call like p(x, y) or an operation like +(a, b) nkCallStrLit, # a call with a string literal # x"abc" has two sons: nkIdent, nkRStrLit # x"""abc""" has two sons: nkIdent, nkTripleStrLit + nkInfix, # a call like (a + b) + nkPrefix, # a call like !a + nkPostfix, # something like a! (also used for visibility) + nkHiddenCallConv, # an implicit type conversion via a type converter + nkExprEqExpr, # a named parameter with equals: ''expr = expr'' nkExprColonExpr, # a named parameter with colon: ''expr: expr'' nkIdentDefs, # a definition like `a, b: typeDesc = expr` # either typeDesc or expr may be nil; used in # formal parameters, var statements, etc. nkVarTuple, # a ``var (a, b) = expr`` construct - nkInfix, # a call like (a + b) - nkPrefix, # a call like !a - nkPostfix, # something like a! (also used for visibility) nkPar, # syntactic (); may be a tuple constructor + nkObjConstr, # object constructor: T(a: 1, b: 2) nkCurly, # syntactic {} nkCurlyExpr, # an expression like a{i} nkBracket, # syntactic [] @@ -109,7 +113,6 @@ type nkHiddenStdConv, # an implicit standard type conversion nkHiddenSubConv, # an implicit type conversion from a subtype # to a supertype - nkHiddenCallConv, # an implicit type conversion via a type converter nkConv, # a type conversion nkCast, # a type cast nkStaticExpr, # a static expr @@ -452,12 +455,12 @@ type mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, - mConTArr, mConTT, mSlice, + mConTArr, mConTT, mSlice, mFields, mFieldPairs, mOmpParFor, - mAppendStrCh, mAppendStrStr, mAppendSeqElem, - mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, - mIsPartOf, mAstToStr, mRand, - mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, + mAppendStrCh, mAppendStrStr, mAppendSeqElem, + mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, + mIsPartOf, mAstToStr, mRand, + mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, @@ -466,18 +469,18 @@ type mUInt, mUInt8, mUInt16, mUInt32, mUInt64, mFloat, mFloat32, mFloat64, mFloat128, mBool, mChar, mString, mCstring, - mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc, + mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc, mVoidType, mPNimrodNode, - mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor, - mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU, mAppType, - mNaN, mInf, mNegInf, + mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor, + mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU, mAppType, + mNaN, mInf, mNegInf, mCompileOption, mCompileOptionArg, - mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind, - mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, + mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind, + mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo, - mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr, - mNBindSym, mNCallSite, - mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError, + mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr, + mNBindSym, mLocals, mNCallSite, + mEqIdent, mEqNimrodNode, mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo # things that we can evaluate safely at compile time, even if not asked for it: @@ -520,7 +523,7 @@ type TNodeSeq* = seq[PNode] PType* = ref TType PSym* = ref TSym - TNode*{.final.} = object # on a 32bit machine, this takes 32 bytes + TNode*{.final, acyclic.} = object # on a 32bit machine, this takes 32 bytes typ*: PType comment*: string info*: TLineInfo @@ -591,6 +594,7 @@ type TLib* = object of lists.TListEntry # also misused for headers! kind*: TLibKind generated*: bool # needed for the backends: + isOverriden*: bool name*: PRope path*: PNode # can be a string literal! @@ -605,7 +609,7 @@ type PInstantiation* = ref TInstantiation PLib* = ref TLib - TSym* = object of TIdObj + TSym* {.acyclic.} = object of TIdObj # proc and type instantiations are cached in the generic symbol case kind*: TSymKind of skType: @@ -653,7 +657,8 @@ type constraint*: PNode # additional constraints like 'lit|result' TTypeSeq* = seq[PType] - TType* = object of TIdObj # types are identical iff they have the + TType* {.acyclic.} = object of TIdObj # \ + # types are identical iff they have the # same id; there may be multiple copies of a type # in memory! kind*: TTypeKind # kind of type @@ -759,7 +764,7 @@ const dispatcherPos* = 8 # caution: if method has no 'result' it can be position 5! nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix, - nkCommand, nkCallStrLit} + nkCommand, nkCallStrLit, nkHiddenCallConv} nkLambdaKinds* = {nkLambda, nkDo} nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice} diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim old mode 100755 new mode 100644 index 564f262d7..364e164bb --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -156,6 +156,10 @@ proc skipConv*(n: PNode): PNode = result = n.sons[1] else: result = n +proc skipConvTakeType*(n: PNode): PNode = + result = n.skipConv + result.typ = n.typ + proc SameValue*(a, b: PNode): bool = result = false case a.kind diff --git a/compiler/babelcmd.nim b/compiler/babelcmd.nim index 956c6a6ae..b67a26040 100644 --- a/compiler/babelcmd.nim +++ b/compiler/babelcmd.nim @@ -60,7 +60,7 @@ iterator chosen(packages: PStringTable): string = proc addBabelPath(p: string, info: TLineInfo) = if not contains(options.searchPaths, p): - Message(info, hintPath, p) + if gVerbosity >= 1: Message(info, hintPath, p) lists.PrependStr(options.lazyPaths, p) proc addPathWithNimFiles(p: string, info: TLineInfo) = @@ -83,7 +83,7 @@ proc addPathRec(dir: string, info: TLineInfo) = if k == pcDir and p[pos] != '.': addPackage(packages, p) for p in packages.chosen: - addPathWithNimFiles(p, info) + addBabelPath(p, info) proc babelPath*(path: string, info: TLineInfo) = addPathRec(path, info) diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim old mode 100755 new mode 100644 diff --git a/compiler/c2nim/c2nim.nim b/compiler/c2nim/c2nim.nim old mode 100755 new mode 100644 index 40fb209c7..029f9ecda --- a/compiler/c2nim/c2nim.nim +++ b/compiler/c2nim/c2nim.nim @@ -1,7 +1,7 @@ # # # c2nim - C to Nimrod source converter -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -15,7 +15,7 @@ const Version = NimrodVersion Usage = """ c2nim - C to Nimrod source converter - (c) 2012 Andreas Rumpf + (c) 2013 Andreas Rumpf Usage: c2nim [options] inputfile [options] Options: -o, --out:FILE set output filename diff --git a/compiler/c2nim/clex.nim b/compiler/c2nim/clex.nim old mode 100755 new mode 100644 diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim old mode 100755 new mode 100644 index 9f7a7bf36..b964ed976 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -955,15 +955,22 @@ proc enumSpecifier(p: var TParser): PNode = result = newNodeP(nkConstSection, p) getTok(p, result) var i = 0 + var hasUnknown = false while true: var name = skipIdentExport(p) var val: PNode if p.tok.xkind == pxAsgn: getTok(p, name) val = constantExpression(p) - if val.kind == nkIntLit: i = int(val.intVal)+1 - else: parMessage(p, errXExpected, "int literal") + if val.kind == nkIntLit: + i = int(val.intVal)+1 + hasUnknown = false + else: + hasUnknown = true else: + if hasUnknown: + parMessage(p, warnUser, "computed const value may be wrong: " & + name.renderTree) val = newIntNodeP(nkIntLit, i, p) inc(i) var c = createConst(name, ast.emptyNode, val, p) diff --git a/compiler/c2nim/cpp.nim b/compiler/c2nim/cpp.nim old mode 100755 new mode 100644 diff --git a/compiler/c2nim/nimrod.cfg b/compiler/c2nim/nimrod.cfg old mode 100755 new mode 100644 diff --git a/compiler/c2nim/tests/systest.c b/compiler/c2nim/tests/systest.c old mode 100755 new mode 100644 index 2a9dd6c28..b73eb5bee --- a/compiler/c2nim/tests/systest.c +++ b/compiler/c2nim/tests/systest.c @@ -9,6 +9,20 @@ extern "C" { # endif #endif +enum +{ +/* 8bit, color or not */ + CV_LOAD_IMAGE_UNCHANGED =-1, +/* 8bit, gray */ + CV_LOAD_IMAGE_GRAYSCALE =0, +/* ?, color */ + CV_LOAD_IMAGE_COLOR =1, +/* any depth, ? */ + CV_LOAD_IMAGE_ANYDEPTH =2, +/* ?, any color */ + CV_LOAD_IMAGE_ANYCOLOR =4 +}; + typedef void (*callback_t) (int rc); typedef const char* (*callback2)(int rc, long L, const char* buffer); diff --git a/compiler/c2nim/tests/systest2.c b/compiler/c2nim/tests/systest2.c old mode 100755 new mode 100644 diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 71e4fe39b..b8b7f4c44 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim old mode 100755 new mode 100644 index c6efaa7a0..6a2ffe752 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -195,6 +195,46 @@ proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = addrLoc(dest), rdLoc(src)) if needToKeepAlive in flags: keepAlive(p, dest) +proc asgnComplexity(n: PNode): int = + if n != nil: + case n.kind + of nkSym: result = 1 + of nkRecCase: + # 'case objects' are too difficult to inline their assignment operation: + result = 100 + of nkRecList: + for t in items(n): + result += asgnComplexity(t) + else: nil + +proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) + +proc optAsgnLoc(a: TLoc, t: PType, field: PRope): TLoc = + result.k = locField + result.s = a.s + result.t = t + result.r = rdLoc(a).con(".").con(field) + result.heapRoot = a.heapRoot + +proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = + for i in 0 .. Sup, sizeof(x)); # seq->data[seq->len-1] = x; let seqAppendPattern = if gCmd != cmdCompileToCpp: - "$1 = ($2) #incrSeq(&($1)->Sup, sizeof($3));$n" - else: - "$1 = ($2) #incrSeq($1, sizeof($3));$n" - + "$1 = ($2) #incrSeq(&($1)->Sup, sizeof($3));$n" + else: + "$1 = ($2) #incrSeq($1, sizeof($3));$n" var a, b, dest: TLoc InitLocExpr(p, e.sons[1], a) InitLocExpr(p, e.sons[2], b) @@ -935,20 +996,12 @@ proc genReset(p: BProc, n: PNode) = linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", addrLoc(a), genTypeInfo(p.module, skipTypes(a.t, abstractVarRange))) -proc genNew(p: BProc, e: PNode) = - var - a, b: TLoc - reftype, bt: PType - sizeExpr: PRope - refType = skipTypes(e.sons[1].typ, abstractVarRange) - InitLocExpr(p, e.sons[1], a) +proc rawGenNew(p: BProc, a: TLoc, sizeExpr: PRope) = + var sizeExpr = sizeExpr + let refType = skipTypes(a.t, abstractVarRange) + var b: TLoc initLoc(b, locExpr, a.t, OnHeap) - # 'genNew' also handles 'unsafeNew': - if e.len == 3: - var se: TLoc - InitLocExpr(p, e.sons[2], se) - sizeExpr = se.rdLoc - else: + if sizeExpr.isNil: sizeExpr = ropef("sizeof($1)", getTypeDesc(p.module, skipTypes(reftype.sons[0], abstractRange))) let args = [getTypeDesc(p.module, reftype), @@ -965,9 +1018,20 @@ proc genNew(p: BProc, e: PNode) = else: b.r = ropecg(p.module, "($1) #newObj($2, $3)", args) genAssignment(p, a, b, {needToKeepAlive}) # set the object type: - bt = skipTypes(refType.sons[0], abstractRange) + let bt = skipTypes(refType.sons[0], abstractRange) genObjectInit(p, cpsStmts, bt, a, false) +proc genNew(p: BProc, e: PNode) = + var a: TLoc + InitLocExpr(p, e.sons[1], a) + # 'genNew' also handles 'unsafeNew': + if e.len == 3: + var se: TLoc + InitLocExpr(p, e.sons[2], se) + rawGenNew(p, a, se.rdLoc) + else: + rawGenNew(p, a, nil) + proc genNewSeqAux(p: BProc, dest: TLoc, length: PRope) = let seqtype = skipTypes(dest.t, abstractVarRange) let args = [getTypeDesc(p.module, seqtype), @@ -975,7 +1039,10 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: PRope) = var call: TLoc initLoc(call, locExpr, dest.t, OnHeap) if dest.s == OnHeap and usesNativeGC(): - linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", dest.rdLoc) + if canFormAcycle(dest.t): + linefmt(p, cpsStmts, "if ($1) #nimGCunref($1);$n", dest.rdLoc) + else: + linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", dest.rdLoc) call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", args) linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc) else: @@ -988,6 +1055,44 @@ proc genNewSeq(p: BProc, e: PNode) = InitLocExpr(p, e.sons[2], b) genNewSeqAux(p, a, b.rdLoc) +proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = + var tmp: TLoc + var t = e.typ.skipTypes(abstractInst) + getTemp(p, t, tmp) + let isRef = t.kind == tyRef + var r = rdLoc(tmp) + if isRef: + rawGenNew(p, tmp, nil) + t = t.sons[0].skipTypes(abstractInst) + r = ropef("(*$1)", r) + # XXX object initialization? but not necessary for temps, is it? + discard getTypeDesc(p.module, t) + for i in 1 .. data[$2]", rdLoc(d), intLiteral(i)) arr.s = OnHeap # we know that sequences are on the heap expr(p, t.sons[i], arr) @@ -1226,8 +1331,14 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = var a, b, x, y: TLoc if (e.sons[1].Kind == nkCurly) and fewCmps(e.sons[1]): # a set constructor but not a constant set: - # do not emit the set, but generate a bunch of comparisons - initLocExpr(p, e.sons[2], a) + # do not emit the set, but generate a bunch of comparisons; and if we do + # so, we skip the unnecessary range check: This is a semantical extension + # that code now relies on. :-/ XXX + let ea = if e.sons[2].kind in {nkChckRange, nkChckRange64}: + e.sons[2].sons[0] + else: + e.sons[2] + initLocExpr(p, ea, a) initLoc(b, locExpr, e.typ, OnUnknown) b.r = toRope("(") var length = sonsLen(e.sons[1]) @@ -1467,8 +1578,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mNewFinalize: genNewFinalize(p, e) of mNewSeq: genNewSeq(p, e) of mSizeOf: + let t = e.sons[1].typ.skipTypes({tyTypeDesc}) putIntoDest(p, d, e.typ, ropef("((NI)sizeof($1))", - [getTypeDesc(p.module, e.sons[1].typ)])) + [getTypeDesc(p.module, t)])) of mChr: genCast(p, e, d) of mOrd: genOrd(p, e, d) of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray: @@ -1755,6 +1867,7 @@ proc expr(p: BProc, e: PNode, d: var TLoc) = exprComplexConst(p, e, d) else: genTupleConstr(p, e, d) + of nkObjConstr: genObjConstr(p, e, d) of nkCast: genCast(p, e, d) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, e, d) of nkHiddenAddr, nkAddr: genAddr(p, e, d) diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index baf4f5586..c6c294b97 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -11,7 +11,7 @@ ## is needed for incremental compilation. import - ast, astalgo, ropes, options, strutils, lexbase, msgs, cgendata, rodutils, + ast, astalgo, ropes, options, strutils, nimlexbase, msgs, cgendata, rodutils, intsets, platform, llstream # Careful! Section marks need to contain a tabulator so that they cannot @@ -45,11 +45,8 @@ const ] NimMergeEndMark = "/*\tNIM_merge_END:*/" -template mergeSectionsEnabled: expr = - {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {} - proc genSectionStart*(fs: TCFileSection): PRope = - if mergeSectionsEnabled: + if compilationCachePresent: result = toRope(tnl) app(result, "/*\t") app(result, CFileSectionNames[fs]) @@ -57,11 +54,11 @@ proc genSectionStart*(fs: TCFileSection): PRope = app(result, tnl) proc genSectionEnd*(fs: TCFileSection): PRope = - if mergeSectionsEnabled: + if compilationCachePresent: result = toRope(NimMergeEndMark & tnl) proc genSectionStart*(ps: TCProcSection): PRope = - if mergeSectionsEnabled: + if compilationCachePresent: result = toRope(tnl) app(result, "/*\t") app(result, CProcSectionNames[ps]) @@ -69,7 +66,7 @@ proc genSectionStart*(ps: TCProcSection): PRope = app(result, tnl) proc genSectionEnd*(ps: TCProcSection): PRope = - if mergeSectionsEnabled: + if compilationCachePresent: result = toRope(NimMergeEndMark & tnl) proc writeTypeCache(a: TIdTable, s: var string) = @@ -122,8 +119,8 @@ proc skipWhite(L: var TBaseLexer) = var pos = L.bufpos while true: case ^pos - of CR: pos = lexbase.HandleCR(L, pos) - of LF: pos = lexbase.HandleLF(L, pos) + of CR: pos = nimlexbase.HandleCR(L, pos) + of LF: pos = nimlexbase.HandleLF(L, pos) of ' ': inc pos else: break L.bufpos = pos @@ -132,8 +129,8 @@ proc skipUntilCmd(L: var TBaseLexer) = var pos = L.bufpos while true: case ^pos - of CR: pos = lexbase.HandleCR(L, pos) - of LF: pos = lexbase.HandleLF(L, pos) + of CR: pos = nimlexbase.HandleCR(L, pos) + of LF: pos = nimlexbase.HandleLF(L, pos) of '\0': break of '/': if ^(pos+1) == '*' and ^(pos+2) == '\t': @@ -156,11 +153,11 @@ when false: while true: case buf[pos] of CR: - pos = lexbase.HandleCR(L, pos) + pos = nimlexbase.HandleCR(L, pos) buf = L.buf result.data.add(tnl) of LF: - pos = lexbase.HandleLF(L, pos) + pos = nimlexbase.HandleLF(L, pos) buf = L.buf result.data.add(tnl) of '\0': @@ -182,11 +179,11 @@ proc readVerbatimSection(L: var TBaseLexer): PRope = while true: case buf[pos] of CR: - pos = lexbase.HandleCR(L, pos) + pos = nimlexbase.HandleCR(L, pos) buf = L.buf r.add(tnl) of LF: - pos = lexbase.HandleLF(L, pos) + pos = nimlexbase.HandleLF(L, pos) buf = L.buf r.add(tnl) of '\0': diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim old mode 100755 new mode 100644 index 473d10e7b..e11678861 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -16,7 +16,8 @@ const # above X strings a hash-switch for strings is generated proc registerGcRoot(p: BProc, v: PSym) = - if gSelectedGc == gcMarkAndSweep and containsGarbageCollectedRef(v.loc.t): + if gSelectedGc in {gcMarkAndSweep, gcGenerational} and + containsGarbageCollectedRef(v.loc.t): # we register a specialized marked proc here; this has the advantage # that it works out of the box for thread local storage then :-) let prc = genTraverseProcForGlobal(p.module, v) @@ -44,7 +45,7 @@ proc genVarTuple(p: BProc, n: PNode) = if t.kind == tyTuple: field.r = ropef("$1.Field$2", [rdLoc(tup), toRope(i)]) else: - if (t.n.sons[i].kind != nkSym): InternalError(n.info, "genVarTuple") + if t.n.sons[i].kind != nkSym: InternalError(n.info, "genVarTuple") field.r = ropef("$1.$2", [rdLoc(tup), mangleRecFieldName(t.n.sons[i].sym, t)]) putLocIntoDest(p, v.loc, field) @@ -63,19 +64,23 @@ proc startBlock(p: BProc, start: TFormatStr = "{$n", result = len(p.blocks) setlen(p.blocks, result + 1) p.blocks[result].id = p.labels - p.blocks[result].nestedTryStmts = p.nestedTryStmts.len + p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 proc assignLabel(b: var TBlock): PRope {.inline.} = b.label = con("LA", b.id.toRope) result = b.label -proc blockBody(b: var TBlock): PRope {.inline.} = - return b.sections[cpsLocals].con(b.sections[cpsInit]).con(b.sections[cpsStmts]) +proc blockBody(b: var TBlock): PRope = + result = b.sections[cpsLocals] + if b.frameLen > 0: + result.appf("F.len+=$1;$n", b.frameLen.toRope) + result.app(b.sections[cpsInit]) + result.app(b.sections[cpsStmts]) proc endBlock(p: BProc, blockEnd: PRope) = - let topBlock = p.blocks.len - 1 + let topBlock = p.blocks.len-1 # the block is merged into the parent block - app(p.blocks[topBlock - 1].sections[cpsStmts], p.blocks[topBlock].blockBody) + app(p.blocks[topBlock-1].sections[cpsStmts], p.blocks[topBlock].blockBody) setlen(p.blocks, topBlock) # this is done after the block is popped so $n is # properly indented when pretty printing is enabled @@ -83,10 +88,13 @@ proc endBlock(p: BProc, blockEnd: PRope) = proc endBlock(p: BProc) = let topBlock = p.blocks.len - 1 - let blockEnd = if p.blocks[topBlock].label != nil: + var blockEnd = if p.blocks[topBlock].label != nil: rfmt(nil, "} $1: ;$n", p.blocks[topBlock].label) else: ~"}$n" + let frameLen = p.blocks[topBlock].frameLen + if frameLen > 0: + blockEnd.appf("F.len-=$1;$n", frameLen.toRope) endBlock(p, blockEnd) proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} = @@ -773,7 +781,7 @@ var proc genBreakPoint(p: BProc, t: PNode) = var name: string - if optEndb in p.Options: + if optEndb in p.Options: if t.kind == nkExprColonExpr: assert(t.sons[1].kind in {nkStrLit..nkTripleStrLit}) name = normalize(t.sons[1].strVal) @@ -783,7 +791,7 @@ proc genBreakPoint(p: BProc, t: PNode) = genLineDir(p, t) # BUGFIX appcg(p.module, gBreakpoints, "#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [ - toRope(toLinenumber(t.info)), makeCString(toFilename(t.info)), + toRope(toLinenumber(t.info)), makeCString(toFilename(t.info)), makeCString(name)]) proc genWatchpoint(p: BProc, n: PNode) = @@ -795,16 +803,13 @@ proc genWatchpoint(p: BProc, n: PNode) = [a.addrLoc, makeCString(renderTree(n.sons[1])), genTypeInfo(p.module, typ)]) -proc genPragma(p: BProc, n: PNode) = - for i in countup(0, sonsLen(n) - 1): +proc genPragma(p: BProc, n: PNode) = + for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] case whichPragma(it) - of wEmit: - genEmit(p, it) - of wBreakpoint: - genBreakPoint(p, it) - of wWatchpoint: - genWatchpoint(p, it) + of wEmit: genEmit(p, it) + of wBreakpoint: genBreakPoint(p, it) + of wWatchpoint: genWatchpoint(p, it) else: nil proc FieldDiscriminantCheckNeeded(p: BProc, asgn: PNode): bool = diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim old mode 100755 new mode 100644 diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 7a0b564a2..aa8b85600 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -59,7 +59,7 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) = if typ == nil: return var p = c.p case typ.kind - of tyGenericInst, tyGenericBody: + of tyGenericInst, tyGenericBody, tyTypeDesc: genTraverseProc(c, accessor, lastSon(typ)) of tyArrayConstr, tyArray: let arraySize = lengthOrd(typ.sons[0]) @@ -111,10 +111,11 @@ proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): PRope = lineF(p, cpsInit, "a = ($1)p;$n", t) c.p = p + assert typ.kind != tyTypedesc if typ.kind == tySequence: genTraverseProcSeq(c, "a".toRope, typ) else: - if skipTypes(typ.sons[0], abstractInst).kind in {tyArrayConstr, tyArray}: + if skipTypes(typ.sons[0], typedescInst).kind in {tyArrayConstr, tyArray}: # C's arrays are broken beyond repair: genTraverseProc(c, "a".toRope, typ.sons[0]) else: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim old mode 100755 new mode 100644 index c65d9072d..d37e79d6b --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -193,8 +193,8 @@ proc mapType(typ: PType): TCTypeKind = of 8: result = ctInt64 else: internalError("mapType") of tyRange: result = mapType(typ.sons[0]) - of tyPtr, tyVar, tyRef: - var base = skipTypes(typ.sons[0], abstractInst) + of tyPtr, tyVar, tyRef: + var base = skipTypes(typ.sons[0], typedescInst) case base.kind of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctArray else: result = ctPtr @@ -208,7 +208,7 @@ proc mapType(typ: PType): TCTypeKind = else: InternalError("mapType") proc mapReturnType(typ: PType): TCTypeKind = - if skipTypes(typ, abstractInst).kind == tyArray: result = ctPtr + if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr else: result = mapType(typ) proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope @@ -224,10 +224,10 @@ proc isInvalidReturnType(rettype: PType): bool = else: case mapType(rettype) of ctArray: - result = not (skipTypes(rettype, abstractInst).kind in + result = not (skipTypes(rettype, typedescInst).kind in {tyVar, tyRef, tyPtr}) of ctStruct: - result = needsComplexAssignment(skipTypes(rettype, abstractInst)) + result = needsComplexAssignment(skipTypes(rettype, typedescInst)) else: result = false const @@ -253,7 +253,7 @@ proc getGlobalTempName(): PRope = result = rfmt(nil, "TMP$1", toRope(backendId())) proc ccgIntroducedPtr(s: PSym): bool = - var pt = skipTypes(s.typ, abstractInst) + var pt = skipTypes(s.typ, typedescInst) assert skResult != s.kind if tfByRef in pt.flags: return true elif tfByCopy in pt.flags: return false @@ -280,13 +280,13 @@ proc fillResult(param: PSym) = proc getParamTypeDesc(m: BModule, t: PType, check: var TIntSet): PRope = when false: if t.Kind in {tyRef, tyPtr, tyVar}: - var b = skipTypes(t.sons[0], abstractInst) + var b = skipTypes(t.sons[0], typedescInst) if b.kind == tySet and mapSetType(b) == ctArray: return getTypeDescAux(m, b, check) result = getTypeDescAux(m, t, check) proc paramStorageLoc(param: PSym): TStorageLoc = - if param.typ.skipTypes({tyVar}).kind notin {tyArray, tyOpenArray}: + if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin {tyArray, tyOpenArray}: result = OnStack else: result = OnUnknown @@ -572,7 +572,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope = assert(CacheGetType(m.typeCache, t) == nil) IdTablePut(m.typeCache, t, con(result, "*")) if not isImportedType(t): - if skipTypes(t.sons[0], abstractInst).kind != tyEmpty: + if skipTypes(t.sons[0], typedescInst).kind != tyEmpty: const cppSeq = "struct $2 : #TGenericSeq {$n" cSeq = "struct $2 {$n" & @@ -744,7 +744,7 @@ proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): PRope = if objType.sym == nil: InternalError(d.info, "anonymous obj with discriminator") result = ropef("NimDT_$1_$2", [ - toRope(objType.sym.name.s), toRope(d.name.s)]) + toRope(objType.sym.name.s.mangle), toRope(d.name.s.mangle)]) proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): PRope = discard cgsym(m, "TNimNode") @@ -915,7 +915,7 @@ include ccgtrav proc genTypeInfo(m: BModule, typ: PType): PRope = var t = getUniqueType(typ) result = ropef("NTI$1", [toRope(t.id)]) - let owner = typ.skipTypes(abstractPtrs).owner.getModule + let owner = typ.skipTypes(typedescPtrs).owner.getModule if owner != m.module: # make sure the type info is created in the owner module discard genTypeInfo(owner.bmod, typ) diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim old mode 100755 new mode 100644 index 90696825b..3dcec7d42 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -80,13 +80,13 @@ proc GetUniqueType*(key: PType): PType = # no canonicalization for integral types, so that e.g. ``pid_t`` is # produced instead of ``NI``. result = key - of tyEmpty, tyNil, tyExpr, tyStmt, tyTypeDesc, tyPointer, tyString, + of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString, tyCString, tyNone, tyBigNum: result = gCanonicalTypes[k] if result == nil: gCanonicalTypes[k] = key result = key - of tyGenericParam, tyTypeClass: + of tyGenericParam, tyTypeClass, tyTypeDesc: InternalError("GetUniqueType") of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter: result = GetUniqueType(lastSon(key)) diff --git a/compiler/cgen.nim b/compiler/cgen.nim old mode 100755 new mode 100644 index 399785c82..753576aa0 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -269,18 +269,18 @@ proc genCLineDir(r: var PRope, filename: string, line: int) = proc genCLineDir(r: var PRope, info: TLineInfo) = genCLineDir(r, info.toFullPath, info.safeLineNm) -proc genLineDir(p: BProc, t: PNode) = +proc genLineDir(p: BProc, t: PNode) = var line = t.info.safeLineNm if optEmbedOrigSrc in gGlobalOptions: app(p.s(cpsStmts), con(~"//", t.info.sourceLine, rnl)) genCLineDir(p.s(cpsStmts), t.info.toFullPath, line) if ({optStackTrace, optEndb} * p.Options == {optStackTrace, optEndb}) and - (p.prc == nil or sfPure notin p.prc.flags): - linefmt(p, cpsStmts, "#endb($1);$n", toRope(line)) + (p.prc == nil or sfPure notin p.prc.flags): + linefmt(p, cpsStmts, "#endb($1, $2);$n", + line.toRope, makeCString(toFilename(t.info))) elif ({optLineTrace, optStackTrace} * p.Options == {optLineTrace, optStackTrace}) and (p.prc == nil or sfPure notin p.prc.flags): - linefmt(p, cpsStmts, "nimln($1, $2);$n", line.toRope, t.info.quotedFilename) @@ -470,12 +470,13 @@ proc localDebugInfo(p: BProc, s: PSym) = # XXX work around a bug: No type information for open arrays possible: if skipTypes(s.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: return var a = con("&", s.loc.r) - if (s.kind == skParam) and ccgIntroducedPtr(s): a = s.loc.r + if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r lineF(p, cpsInit, "F.s[$1].address = (void*)$3; F.s[$1].typ = $4; F.s[$1].name = $2;$n", - [toRope(p.frameLen), makeCString(normalize(s.name.s)), a, + [p.maxFrameLen.toRope, makeCString(normalize(s.name.s)), a, genTypeInfo(p.module, s.loc.t)]) - inc(p.frameLen) + inc(p.maxFrameLen) + inc p.blocks[p.blocks.len-1].frameLen proc assignLocalVar(p: BProc, s: PSym) = #assert(s.loc.k == locNone) // not yet assigned @@ -488,7 +489,7 @@ proc assignLocalVar(p: BProc, s: PSym) = if sfRegister in s.flags: app(decl, " register") #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: # app(decl, " GC_GUARD") - if (sfVolatile in s.flags) or (p.nestedTryStmts.len > 0): + if sfVolatile in s.flags or p.nestedTryStmts.len > 0: app(decl, " volatile") appf(decl, " $1;$n", [s.loc.r]) line(p, cpsLocals, decl) @@ -574,6 +575,8 @@ proc loadDynamicLib(m: BModule, lib: PLib) = if lib.path.kind in {nkStrLit..nkTripleStrLit}: var s: TStringSeq = @[] libCandidates(lib.path.strVal, s) + if gVerbosity >= 2: + MsgWriteln("Dependency: " & lib.path.strVal) var loadlib: PRope = nil for i in countup(0, high(s)): inc(m.labels) @@ -693,10 +696,11 @@ proc retIsNotVoid(s: PSym): bool = proc initFrame(p: BProc, procname, filename: PRope): PRope = discard cgsym(p.module, "pushFrame") - if p.frameLen > 0: + if p.maxFrameLen > 0: discard cgsym(p.module, "TVarSlot") - result = rfmt(nil, "\tnimfrs($1, $2, $3)$N", - procname, filename, p.frameLen.toRope) + result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N", + procname, filename, p.maxFrameLen.toRope, + p.blocks[0].frameLen.toRope) else: result = rfmt(nil, "\tnimfr($1, $2)$N", procname, filename) @@ -890,6 +894,12 @@ proc getFileHeader(cfilenoext: string): PRope = result = getCopyright(cfilenoext) addIntTypes(result) +proc genFilenames(m: BModule): PRope = + discard cgsym(m, "dbgRegisterFilename") + result = nil + for i in 0.. 0 if we are within a loop gcFrameId*: natural # for the GC stack marking diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim old mode 100755 new mode 100644 index e7bd54ef0..33bb94b38 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -108,8 +108,8 @@ proc methodDef*(s: PSym, fromCache: bool) = proc relevantCol(methods: TSymSeq, col: int): bool = # returns true iff the position is relevant - var t = methods[0].typ.sons[col] - if skipTypes(t, skipPtrs).kind == tyObject: + var t = methods[0].typ.sons[col].skipTypes(skipPtrs) + if t.kind == tyObject: for i in countup(1, high(methods)): let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs) if not SameType(t2, t): diff --git a/compiler/charsets.nim b/compiler/charsets.nim old mode 100755 new mode 100644 diff --git a/compiler/commands.nim b/compiler/commands.nim old mode 100755 new mode 100644 index a2154c907..27da03bbe --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -65,8 +65,13 @@ proc writeCommandLineUsage() = MsgWriteln(getCommandLineDesc()) helpWritten = true +proc addPrefix(switch: string): string = + if len(switch) == 1: result = "-" & switch + else: result = "--" & switch + proc InvalidCmdLineOption(pass: TCmdLinePass, switch: string, info: TLineInfo) = - LocalError(info, errInvalidCmdLineOption, switch) + if switch == " ": LocalError(info, errInvalidCmdLineOption, "-") + else: LocalError(info, errInvalidCmdLineOption, addPrefix(switch)) proc splitSwitch(switch: string, cmd, arg: var string, pass: TCmdLinePass, info: TLineInfo) = @@ -98,10 +103,10 @@ proc ProcessOnOffSwitchG(op: TGlobalOptions, arg: string, pass: TCmdlinePass, else: LocalError(info, errOnOrOffExpectedButXFound, arg) proc ExpectArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = - if arg == "": LocalError(info, errCmdLineArgExpected, switch) + if arg == "": LocalError(info, errCmdLineArgExpected, addPrefix(switch)) proc ExpectNoArg(switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = - if arg != "": LocalError(info, errCmdLineNoArgExpected, switch) + if arg != "": LocalError(info, errCmdLineNoArgExpected, addPrefix(switch)) proc ProcessSpecificNote(arg: string, state: TSpecialWord, pass: TCmdlinePass, info: TLineInfo) = @@ -143,6 +148,7 @@ proc testCompileOptionArg*(switch, arg: string, info: TLineInfo): bool = of "refc": result = gSelectedGC == gcRefc of "v2": result = gSelectedGC == gcV2 of "markandsweep": result = gSelectedGC == gcMarkAndSweep + of "generational": result = gSelectedGC == gcGenerational of "none": result = gSelectedGC == gcNone else: LocalError(info, errNoneBoehmRefcExpectedButXFound, arg) of "opt": @@ -190,8 +196,13 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool = of "patterns": result = contains(gOptions, optPatterns) else: InvalidCmdLineOption(passCmd1, switch, info) -proc processPath(path: string): string = - result = UnixToNativePath(path % ["nimrod", getPrefixDir(), "lib", libpath, +proc processPath(path: string, notRelativeToProj = false): string = + let p = if notRelativeToProj or os.isAbsolute(path) or + '$' in path or path[0] == '.': + path + else: + options.gProjectPath / path + result = UnixToNativePath(p % ["nimrod", getPrefixDir(), "lib", libpath, "home", removeTrailingDirSep(os.getHomeDir()), "projectname", options.gProjectName, "projectpath", options.gProjectPath]) @@ -206,6 +217,11 @@ proc track(arg: string, info: TLineInfo) = LocalError(info, errInvalidNumber, a[2]) msgs.addCheckpoint(newLineInfo(a[0], line, column)) +proc dynlibOverride(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = + if pass in {passCmd2, passPP}: + expectArg(switch, arg, pass, info) + options.inclDynlibOverride(arg) + proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = var theOS: TSystemOS @@ -218,7 +234,7 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = of "babelpath": if pass in {passCmd2, passPP}: expectArg(switch, arg, pass, info) - let path = processPath(arg) + let path = processPath(arg, notRelativeToProj=true) babelpath(path, info) of "excludepath": expectArg(switch, arg, pass, info) @@ -280,6 +296,9 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = of "markandsweep": gSelectedGC = gcMarkAndSweep defineSymbol("gcmarkandsweep") + of "generational": + gSelectedGC = gcGenerational + defineSymbol("gcgenerational") of "none": gSelectedGC = gcNone defineSymbol("nogc") @@ -437,9 +456,9 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = of "genscript": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optGenScript) - of "lib": + of "lib": expectArg(switch, arg, pass, info) - libpath = processPath(arg) + libpath = processPath(arg, notRelativeToProj=true) of "putenv": expectArg(switch, arg, pass, info) splitSwitch(arg, key, val, pass, info) @@ -471,6 +490,8 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = of "listfullpaths": expectNoArg(switch, arg, pass, info) gListFullPaths = true + of "dynliboverride": + dynlibOverride(switch, arg, pass, info) else: if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) else: InvalidCmdLineOption(pass, switch, info) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim old mode 100755 new mode 100644 index 17366f6e9..be6cb9875 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -10,7 +10,7 @@ # This module handles the conditional symbols. import - ast, astalgo, msgs, hashes, platform, strutils, idents + ast, astalgo, hashes, platform, strutils, idents var gSymbols*: TStrTable @@ -35,14 +35,12 @@ proc isDefined*(symbol: PIdent): bool = proc isDefined*(symbol: string): bool = result = isDefined(getIdent(symbol)) -proc ListSymbols*() = +iterator definedSymbolNames*: string = var it: TTabIter var s = InitTabIter(it, gSymbols) - OutWriteln("-- List of currently defined symbols --") - while s != nil: - if s.position == 1: OutWriteln(s.name.s) + while s != nil: + if s.position == 1: yield s.name.s s = nextIter(it, gSymbols) - OutWriteln("-- End of list --") proc countDefinedSymbols*(): int = var it: TTabIter diff --git a/compiler/crc.nim b/compiler/crc.nim old mode 100755 new mode 100644 diff --git a/compiler/depends.nim b/compiler/depends.nim old mode 100755 new mode 100644 diff --git a/compiler/docgen.nim b/compiler/docgen.nim old mode 100755 new mode 100644 diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index ba6e7ee8f..21a131996 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -142,10 +142,10 @@ proc getField(n: PNode; position: int): PSym = else: nil proc packObject(x: PNode, typ: PType, res: pointer) = - InternalAssert x.kind == nkPar + InternalAssert x.kind in {nkObjConstr, nkPar} # compute the field's offsets: discard typ.getSize - for i in countup(0, sonsLen(x) - 1): + for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1): var it = x.sons[i] if it.kind == nkExprColonExpr: internalAssert it.sons[0].kind == nkSym @@ -257,11 +257,11 @@ proc unpackObject(x: pointer, typ: PType, n: PNode): PNode = unpackObjectAdd(x, typ.n, result) else: result = n - if result.kind != nkPar: + if result.kind notin {nkObjConstr, nkPar}: GlobalError(n.info, "cannot map value from FFI") if typ.n.isNil: GlobalError(n.info, "cannot unpack unnamed tuple") - for i in countup(0, sonsLen(n) - 1): + for i in countup(ord(n.kind == nkObjConstr), sonsLen(n) - 1): var it = n.sons[i] if it.kind == nkExprColonExpr: internalAssert it.sons[0].kind == nkSym diff --git a/compiler/evals.nim b/compiler/evals.nim old mode 100755 new mode 100644 index 4b83cb703..3f5b7887a --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -249,7 +249,7 @@ proc getNullValueAux(obj: PNode, result: PNode) = else: InternalError(result.info, "getNullValueAux") proc getNullValue(typ: PType, info: TLineInfo): PNode = - var t = skipTypes(typ, abstractRange) + var t = skipTypes(typ, abstractRange-{tyTypeDesc}) result = emptyNode case t.kind of tyBool, tyEnum, tyChar, tyInt..tyInt64: @@ -273,9 +273,10 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result = newNodeIT(nkBracket, info, t) for i in countup(0, int(lengthOrd(t)) - 1): addSon(result, getNullValue(elemType(t), info)) - of tyTuple: + of tyTuple: + # XXX nkExprColonExpr is out of fashion ... result = newNodeIT(nkPar, info, t) - for i in countup(0, sonsLen(t) - 1): + for i in countup(0, sonsLen(t) - 1): var p = newNodeIT(nkExprColonExpr, info, t.sons[i]) var field = if t.n != nil: t.n.sons[i].sym else: newSym( skField, getIdent(":tmp" & $i), t.owner, info) @@ -293,10 +294,12 @@ proc evalVar(c: PEvalContext, n: PNode): PNode = if a.kind != nkIdentDefs: return raiseCannotEval(c, n.info) # XXX var (x, y) = z support? #assert(a.sons[0].kind == nkSym) can happen for transformed vars - if a.sons[2].kind != nkEmpty: + if a.sons[2].kind != nkEmpty: result = evalAux(c, a.sons[2], {}) - if isSpecial(result): return - else: + if result.kind in {nkType..nkNilLit}: + result = result.copyNode + if isSpecial(result): return + else: result = getNullValue(a.sons[0].typ, a.sons[0].info) if a.sons[0].kind == nkSym: var v = a.sons[0].sym @@ -444,29 +447,35 @@ proc evalArrayAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = stackTrace(c, n, errIndexOutOfBounds) else: stackTrace(c, n, errNilAccess) -proc evalFieldAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = +proc evalFieldAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = # a real field access; proc calls have already been transformed # XXX: field checks! result = evalAux(c, n.sons[0], flags) - if isSpecial(result): return + if isSpecial(result): return var x = result if x.kind != nkPar: return raiseCannotEval(c, n.info) + # this is performance critical: var field = n.sons[1].sym - for i in countup(0, sonsLen(x) - 1): - var it = x.sons[i] - if it.kind != nkExprColonExpr: - # lookup per index: - result = x.sons[field.position] - if result.kind == nkExprColonExpr: result = result.sons[1] - if not aliasNeeded(result, flags): result = copyTree(result) - return - #InternalError(it.info, "evalFieldAccess") - if it.sons[0].sym.name.id == field.name.id: - result = x.sons[i].sons[1] - if not aliasNeeded(result, flags): result = copyTree(result) - return - stackTrace(c, n, errFieldXNotFound, field.name.s) - result = emptyNode + result = x.sons[field.position] + if result.kind == nkExprColonExpr: result = result.sons[1] + if not aliasNeeded(result, flags): result = copyTree(result) + when false: + var field = n.sons[1].sym + for i in countup(0, sonsLen(x) - 1): + var it = x.sons[i] + if it.kind != nkExprColonExpr: + # lookup per index: + result = x.sons[field.position] + if result.kind == nkExprColonExpr: result = result.sons[1] + if not aliasNeeded(result, flags): result = copyTree(result) + return + #InternalError(it.info, "evalFieldAccess") + if it.sons[0].sym.name.id == field.name.id: + result = x.sons[i].sons[1] + if not aliasNeeded(result, flags): result = copyTree(result) + return + stackTrace(c, n, errFieldXNotFound, field.name.s) + result = emptyNode proc evalAsgn(c: PEvalContext, n: PNode): PNode = var a = n.sons[0] @@ -896,13 +905,13 @@ proc evalParseExpr(c: PEvalContext, n: PNode): PNode = if sonsLen(ast) != 1: GlobalError(code.info, errExprExpected, "multiple statements") result = ast.sons[0] - result.typ = newType(tyExpr, c.module) + #result.typ = newType(tyExpr, c.module) proc evalParseStmt(c: PEvalContext, n: PNode): PNode = var code = evalAux(c, n.sons[1], {}) result = parseString(code.getStrValue, code.info.toFilename, code.stringStartingLine) - result.typ = newType(tyStmt, c.module) + #result.typ = newType(tyStmt, c.module) proc evalTypeTrait*(n: PNode, context: PSym): PNode = ## XXX: This should be pretty much guaranteed to be true @@ -993,10 +1002,10 @@ proc evalExpandToAst(c: PEvalContext, original: PNode): PNode = case expandedSym.kind of skTemplate: - let genSymOwner = if c.tos != nil and c.tos.prc != nil: - c.tos.prc - else: - c.module + let genSymOwner = if c.tos != nil and c.tos.prc != nil: + c.tos.prc + else: + c.module result = evalTemplate(macroCall, expandedSym, genSymOwner) of skMacro: # At this point macroCall.sons[0] is nkSym node. @@ -1090,7 +1099,7 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[2], {efLValue}) if isSpecial(result): return addSon(a, result) - result = emptyNode + result = a of mNAddMultiple: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return @@ -1098,7 +1107,7 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[2], {efLValue}) if isSpecial(result): return for i in countup(0, sonsLen(result) - 1): addSon(a, result.sons[i]) - result = emptyNode + result = a of mNDel: result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return @@ -1336,19 +1345,27 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = cc = result if isEmpty(a) or isEmpty(b) or isEmpty(cc): result = emptyNode else: result = evalOp(m, n, a, b, cc) - + proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = result = emptyNode dec(gNestedEvals) if gNestedEvals <= 0: stackTrace(c, n, errTooManyIterations) - case n.kind # atoms: - of nkEmpty: result = n + case n.kind of nkSym: result = evalSym(c, n, flags) - of nkType..nkNilLit: result = copyNode(n) # end of atoms - of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit, nkInfix, - nkPrefix, nkPostfix: + of nkType..nkNilLit: + # nkStrLit is VERY common in the traces, so we should avoid + # the 'copyNode' here. + result = n #.copyNode + of nkAsgn, nkFastAsgn: result = evalAsgn(c, n) + of nkCommand..nkHiddenCallConv: result = evalMagicOrCall(c, n) - of nkCurly, nkBracket, nkRange: + of nkDotExpr: result = evalFieldAccess(c, n, flags) + of nkBracketExpr: + result = evalArrayAccess(c, n, flags) + of nkDerefExpr, nkHiddenDeref: result = evalDeref(c, n, flags) + of nkAddr, nkHiddenAddr: result = evalAddr(c, n, flags) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: result = evalConv(c, n) + of nkCurly, nkBracket, nkRange: # flags need to be passed here for mNAddMultiple :-( # XXX this is not correct in every case! var a = copyNode(n) @@ -1370,12 +1387,23 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = if isSpecial(result): return a.sons[i] = result result = a - of nkBracketExpr: result = evalArrayAccess(c, n, flags) - of nkDotExpr: result = evalFieldAccess(c, n, flags) - of nkDerefExpr, nkHiddenDeref: result = evalDeref(c, n, flags) - of nkAddr, nkHiddenAddr: result = evalAddr(c, n, flags) - of nkHiddenStdConv, nkHiddenSubConv, nkConv: result = evalConv(c, n) - of nkAsgn, nkFastAsgn: result = evalAsgn(c, n) + of nkObjConstr: + let t = skipTypes(n.typ, abstractInst) + var a: PNode + if t.kind == tyRef: + result = newNodeIT(nkRefTy, n.info, t) + a = getNullValue(t.sons[0], n.info) + addSon(result, a) + else: + a = getNullValue(t, n.info) + result = a + for i in countup(1, sonsLen(n) - 1): + let it = n.sons[i] + if it.kind == nkExprColonExpr: + let value = evalAux(c, it.sons[1], flags) + if isSpecial(value): return value + a.sons[it.sons[0].sym.position] = value + else: return raiseCannotEval(c, n.info) of nkWhenStmt, nkIfStmt, nkIfExpr: result = evalIf(c, n) of nkWhileStmt: result = evalWhile(c, n) of nkCaseStmt: result = evalCase(c, n) @@ -1414,6 +1442,9 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = result = raiseCannotEval(c, n.info) of nkRefTy: result = evalAux(c, n.sons[0], flags) + of nkEmpty: + # nkEmpty occurs once in each trace that I looked at + result = n else: InternalError(n.info, "evalAux: " & $n.kind) if result == nil: InternalError(n.info, "evalAux: returned nil " & $n.kind) @@ -1464,11 +1495,13 @@ proc evalMacroCall(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode = c.callsite = nOrig var s = newStackFrame() s.call = n - setlen(s.params, n.len) + var L = n.safeLen + if L == 0: L = 1 + setlen(s.params, L) # return value: s.params[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0]) # setup parameters: - for i in 1 .. < n.len: s.params[i] = setupMacroParam(n.sons[i]) + for i in 1 .. < L: s.params[i] = setupMacroParam(n.sons[i]) pushStackFrame(c, s) discard eval(c, sym.getBody) result = s.params[0] @@ -1483,7 +1516,13 @@ proc myOpen(module: PSym): PPassContext = pushStackFrame(c, newStackFrame()) result = c -proc myProcess(c: PPassContext, n: PNode): PNode = +var oldErrorCount: int + +proc myProcess(c: PPassContext, n: PNode): PNode = + # don't eval errornous code: + if oldErrorCount != msgs.gErrorCounter: + oldErrorCount = msgs.gErrorCounter + return n result = eval(PEvalContext(c), n) const evalPass* = makePass(myOpen, nil, myProcess, myProcess) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim old mode 100755 new mode 100644 index 11e89fee0..bb2f09151 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -391,35 +391,8 @@ proc resetCompilationLists* = initLinkedList(externalToCompile) initLinkedList(toLink) -proc footprint(filename: string): TCrc32 = - result = crcFromFile(filename) >< - platform.OS[targetOS].name >< - platform.CPU[targetCPU].name >< - extccomp.CC[extccomp.ccompiler].name - -proc externalFileChanged(filename: string): bool = - var crcFile = toGeneratedFile(filename, "crc") - var currentCrc = int(footprint(filename)) - var f: TFile - if open(f, crcFile, fmRead): - var line = newStringOfCap(40) - if not f.readLine(line): line = "0" - close(f) - var oldCrc = parseInt(line) - result = oldCrc != currentCrc - else: - result = true - if result: - if open(f, crcFile, fmWrite): - f.writeln($currentCrc) - close(f) - -proc addExternalFileToCompile*(filename: string) = - if optForceFullMake in gGlobalOptions or externalFileChanged(filename): - appendStr(externalToCompile, filename) - proc addFileToLink*(filename: string) = - prependStr(toLink, filename) + prependStr(toLink, filename) # BUGFIX: was ``appendStr`` proc execExternalProgram*(cmd: string) = @@ -536,6 +509,34 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = "nimrod", quoteIfContainsWhite(getPrefixDir()), "lib", quoteIfContainsWhite(libpath)]) +proc footprint(filename: string): TCrc32 = + result = crcFromFile(filename) >< + platform.OS[targetOS].name >< + platform.CPU[targetCPU].name >< + extccomp.CC[extccomp.ccompiler].name >< + getCompileCFileCmd(filename, true) + +proc externalFileChanged(filename: string): bool = + var crcFile = toGeneratedFile(filename, "crc") + var currentCrc = int(footprint(filename)) + var f: TFile + if open(f, crcFile, fmRead): + var line = newStringOfCap(40) + if not f.readLine(line): line = "0" + close(f) + var oldCrc = parseInt(line) + result = oldCrc != currentCrc + else: + result = true + if result: + if open(f, crcFile, fmWrite): + f.writeln($currentCrc) + close(f) + +proc addExternalFileToCompile*(filename: string) = + if optForceFullMake in gGlobalOptions or externalFileChanged(filename): + appendStr(externalToCompile, filename) + proc CompileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq, isExternal: bool) = var it = PStrEntry(list.head) @@ -572,7 +573,12 @@ proc CallCCompiler*(projectfile: string) = else: res = execProcesses(cmds, {poUseShell, poParentStreams}, gNumberOfProcessors) - if res != 0: rawMessage(errExecutionOfProgramFailed, []) + if res != 0: + if gNumberOfProcessors <= 1: + rawMessage(errExecutionOfProgramFailed, []) + else: + rawMessage(errGenerated, " execution of an external program failed; " & + "rerun with --parallelBuild:1 to see the error message") if optNoLinking notin gGlobalOptions: # call the linker: var it = PStrEntry(toLink.head) diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim old mode 100755 new mode 100644 index fd7ab60dc..d16639d08 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -98,7 +98,13 @@ proc parseLine(p: var TTmplParser) = of wElif, wOf, wElse, wExcept, wFinally: LLStreamWrite(p.outp, repeatChar(p.indent - 2)) LLStreamWrite(p.outp, substr(p.x, d)) - else: + of wLet, wVar, wConst, wType: + LLStreamWrite(p.outp, repeatChar(p.indent)) + LLStreamWrite(p.outp, substr(p.x, d)) + if not p.x.contains({':', '='}): + # no inline element --> treat as block: + inc(p.indent, 2) + else: LLStreamWrite(p.outp, repeatChar(p.indent)) LLStreamWrite(p.outp, substr(p.x, d)) p.state = psDirective diff --git a/compiler/filters.nim b/compiler/filters.nim old mode 100755 new mode 100644 diff --git a/compiler/idents.nim b/compiler/idents.nim old mode 100755 new mode 100644 diff --git a/compiler/importer.nim b/compiler/importer.nim old mode 100755 new mode 100644 diff --git a/compiler/ecmasgen.nim b/compiler/jsgen.nim old mode 100755 new mode 100644 similarity index 96% rename from compiler/ecmasgen.nim rename to compiler/jsgen.nim index a8c62ae09..f2e317f47 --- a/compiler/ecmasgen.nim +++ b/compiler/jsgen.nim @@ -20,22 +20,22 @@ import # implementation type - TEcmasGen = object of TPassContext + TJSGen = object of TPassContext module: PSym - BModule = ref TEcmasGen - TEcmasTypeKind = enum # necessary JS "types" + BModule = ref TJSGen + TJSTypeKind = enum # necessary JS "types" etyNone, # no type etyNull, # null type etyProc, # proc type etyBool, # bool type - etyInt, # Ecmascript's int - etyFloat, # Ecmascript's float - etyString, # Ecmascript's string - etyObject, # Ecmascript's reference to an object + etyInt, # JavaScript's int + etyFloat, # JavaScript's float + etyString, # JavaScript's string + etyObject, # JavaScript's reference to an object etyBaseIndex # base + index needed TCompRes{.final.} = object - kind: TEcmasTypeKind + kind: TJSTypeKind com: PRope # computation part # address if this is a (address, index)-tuple res: PRope # result part; index if this is an @@ -44,7 +44,6 @@ type TBlock{.final.} = object id: int # the ID of the label; positive means that it # has been used (i.e. the label should be emitted) - nestedTryStmts: int # how many try statements is it nested into isLoop: bool # whether it's a 'block' or 'while' TGlobals{.final.} = object @@ -57,12 +56,11 @@ type TProc{.final.} = object procDef: PNode prc: PSym - data: PRope + locals: PRope options: TOptions module: BModule g: PGlobals BeforeRetNeeded: bool - nestedTryStmts: int unique: int blocks: seq[TBlock] @@ -91,7 +89,7 @@ const MappedToObject = {tyObject, tyArray, tyArrayConstr, tyTuple, tyOpenArray, tySet, tyVar, tyRef, tyPtr, tyBigNum, tyVarargs} -proc mapType(typ: PType): TEcmasTypeKind = +proc mapType(typ: PType): TJSTypeKind = var t = skipTypes(typ, abstractInst) case t.kind of tyVar, tyRef, tyPtr: @@ -485,10 +483,6 @@ proc genLineDir(p: var TProc, n: PNode, r: var TCompRes) = ((p.prc == nil) or not (sfPure in p.prc.flags)): appf(r.com, "F.line = $1;$n", [toRope(line)]) -proc finishTryStmt(p: var TProc, r: var TCompRes, howMany: int) = - for i in countup(1, howMany): - app(r.com, "excHandler = excHandler.prev;" & tnl) - proc genWhileStmt(p: var TProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes @@ -498,7 +492,6 @@ proc genWhileStmt(p: var TProc, n: PNode, r: var TCompRes) = length = len(p.blocks) setlen(p.blocks, length + 1) p.blocks[length].id = - p.unique - p.blocks[length].nestedTryStmts = p.nestedTryStmts p.blocks[length].isLoop = true labl = p.unique gen(p, n.sons[0], cond) @@ -543,7 +536,6 @@ proc genTryStmt(p: var TProc, n: PNode, r: var TCompRes) = if optStackTrace in p.Options: app(r.com, "framePtr = F;" & tnl) app(r.com, "try {" & tnl) length = sonsLen(n) - inc(p.nestedTryStmts) genStmt(p, n.sons[0], a) app(r.com, mergeStmt(a)) i = 1 @@ -571,8 +563,6 @@ proc genTryStmt(p: var TProc, n: PNode, r: var TCompRes) = appf(epart, "$1}$n", [mergeStmt(a)]) inc(i) if epart != nil: appf(r.com, "} catch (EXC) {$n$1", [epart]) - finishTryStmt(p, r, p.nestedTryStmts) - dec(p.nestedTryStmts) app(r.com, "} finally {" & tnl & "excHandler = excHandler.prev;" & tnl) if (i < length) and (n.sons[i].kind == nkFinally): genStmt(p, n.sons[i].sons[0], a) @@ -605,8 +595,8 @@ proc genCaseStmt(p: var TProc, n: PNode, r: var TCompRes) = if cond.com != nil: appf(r.com, "$1;$n", [cond.com]) stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString if stringSwitch: - useMagic(p, "toEcmaStr") - appf(r.com, "switch (toEcmaStr($1)) {$n", [cond.res]) + useMagic(p, "toJSStr") + appf(r.com, "switch (toJSStr($1)) {$n", [cond.res]) else: appf(r.com, "switch ($1) {$n", [cond.res]) for i in countup(1, sonsLen(n) - 1): @@ -619,17 +609,17 @@ proc genCaseStmt(p: var TProc, n: PNode, r: var TCompRes) = v = copyNode(e.sons[0]) while (v.intVal <= e.sons[1].intVal): gen(p, v, cond) - if cond.com != nil: internalError(v.info, "ecmasgen.genCaseStmt") + if cond.com != nil: internalError(v.info, "jsgen.genCaseStmt") appf(r.com, "case $1: ", [cond.res]) Inc(v.intVal) else: gen(p, e, cond) - if cond.com != nil: internalError(e.info, "ecmasgen.genCaseStmt") + if cond.com != nil: internalError(e.info, "jsgen.genCaseStmt") if stringSwitch: case e.kind of nkStrLit..nkTripleStrLit: appf(r.com, "case $1: ", [makeJSString(e.strVal)]) - else: InternalError(e.info, "ecmasgen.genCaseStmt: 2") + else: InternalError(e.info, "jsgen.genCaseStmt: 2") else: appf(r.com, "case $1: ", [cond.res]) genStmt(p, lastSon(it), stmt) @@ -637,7 +627,7 @@ proc genCaseStmt(p: var TProc, n: PNode, r: var TCompRes) = of nkElse: genStmt(p, it.sons[0], stmt) appf(r.com, "default: $n$1break;$n", [mergeStmt(stmt)]) - else: internalError(it.info, "ecmasgen.genCaseStmt") + else: internalError(it.info, "jsgen.genCaseStmt") appf(r.com, "}$n", []) proc genStmtListExpr(p: var TProc, n: PNode, r: var TCompRes) @@ -655,7 +645,6 @@ proc genBlock(p: var TProc, n: PNode, r: var TCompRes) = sym.loc.a = idx setlen(p.blocks, idx + 1) p.blocks[idx].id = - p.unique # negative because it isn't used yet - p.blocks[idx].nestedTryStmts = p.nestedTryStmts labl = p.unique if n.kind == nkBlockExpr: genStmtListExpr(p, n.sons[1], r) else: genStmt(p, n.sons[1], r) @@ -682,7 +671,6 @@ proc genBreakStmt(p: var TProc, n: PNode, r: var TCompRes) = if idx < 0 or not p.blocks[idx].isLoop: InternalError(n.info, "no loop to break") p.blocks[idx].id = abs(p.blocks[idx].id) # label is used - finishTryStmt(p, r, p.nestedTryStmts - p.blocks[idx].nestedTryStmts) appf(r.com, "break L$1;$n", [toRope(p.blocks[idx].id)]) proc genAsmStmt(p: var TProc, n: PNode, r: var TCompRes) = @@ -692,7 +680,7 @@ proc genAsmStmt(p: var TProc, n: PNode, r: var TCompRes) = case n.sons[i].Kind of nkStrLit..nkTripleStrLit: app(r.com, n.sons[i].strVal) of nkSym: app(r.com, mangleName(n.sons[i].sym)) - else: InternalError(n.sons[i].info, "ecmasgen: genAsmStmt()") + else: InternalError(n.sons[i].info, "jsgen: genAsmStmt()") proc genIfStmt(p: var TProc, n: PNode, r: var TCompRes) = var @@ -758,7 +746,7 @@ proc generateHeader(p: var TProc, typ: PType): PRope = const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, - nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkStringToCString, + nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkObjConstr, nkStringToCString, nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix, nkCommand, nkHiddenCallConv, nkCallStrLit} @@ -967,7 +955,8 @@ proc genSym(p: var TProc, n: PNode, r: var TCompRes) = elif not p.g.generatedSyms.containsOrIncl(s.id): var r2: TCompRes genProc(p, s, r2) - app(p.g.code, mergeStmt(r2)) + app(p.locals, mergeStmt(r2)) + #app(r.com, mergeStmt(r2)) else: if s.loc.r == nil: InternalError(n.info, "symbol has no generated name: " & s.name.s) @@ -1152,6 +1141,8 @@ proc genConstant(p: var TProc, c: PSym, r: var TCompRes) = if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id): genLineDir(p, c.ast, r) genVarInit(p, c, c.ast, r) + app(p.g.code, r.com) + r.com = nil when false: proc genConstStmt(p: var TProc, n: PNode, r: var TCompRes) = @@ -1276,7 +1267,7 @@ proc genMagic(p: var TProc, n: PNode, r: var TCompRes) = binaryStmt(p, n, r, "", "$1 += $2") else: binaryStmt(p, n, r, "", "$1 = ($1.slice(0,-1)).concat($2)") - # XXX: make a copy of $2, because of ECMAScript's sucking semantics + # XXX: make a copy of $2, because of Javascript's sucking semantics of mAppendSeqElem: binaryStmt(p, n, r, "", "$1.push($2)") of mConStrStr: genConStrStr(p, n, r) of mEqStr: binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)") @@ -1367,6 +1358,21 @@ proc genTupleConstr(p: var TProc, n: PNode, r: var TCompRes) = appf(r.res, "Field$1: $2", [i.toRope, a.res]) r.res.app("}") +proc genObjConstr(p: var TProc, n: PNode, r: var TCompRes) = + # XXX inheritance? + var a: TCompRes + r.res = toRope("{") + for i in countup(0, sonsLen(n) - 1): + if i > 0: app(r.res, ", ") + var it = n.sons[i] + InternalAssert it.kind == nkExprColonExpr + gen(p, it.sons[1], a) + r.com = mergeExpr(r.com, a.com) + var f = it.sons[0].sym + if f.loc.r == nil: f.loc.r = mangleName(f) + appf(r.res, "$1: $2", [f.loc.r, a.res]) + r.res.app("}") + proc genConv(p: var TProc, n: PNode, r: var TCompRes) = var dest = skipTypes(n.typ, abstractVarRange) var src = skipTypes(n.sons[1].typ, abstractVarRange) @@ -1395,8 +1401,8 @@ proc convStrToCStr(p: var TProc, n: PNode, r: var TCompRes) = else: gen(p, n.sons[0], r) if r.res == nil: InternalError(n.info, "convStrToCStr") - useMagic(p, "toEcmaStr") - r.res = ropef("toEcmaStr($1)", [r.res]) + useMagic(p, "toJSStr") + r.res = ropef("toJSStr($1)", [r.res]) proc convCStrToStr(p: var TProc, n: PNode, r: var TCompRes) = # we do an optimization here as this is likely to slow down @@ -1418,7 +1424,6 @@ proc genReturnStmt(p: var TProc, n: PNode, r: var TCompRes) = if a.com != nil: appf(r.com, "$1;$n", mergeStmt(a)) else: genLineDir(p, n, r) - finishTryStmt(p, r, p.nestedTryStmts) app(r.com, "break BeforeRet;" & tnl) proc genProcBody(p: var TProc, prc: PSym, r: TCompRes): PRope = @@ -1451,16 +1456,17 @@ proc genProc(oldProc: var TProc, prc: PSym, r: var TCompRes) = resultAsgn = nil name = mangleName(prc) header = generateHeader(p, prc.typ) - if (prc.typ.sons[0] != nil) and sfPure notin prc.flags: + if prc.typ.sons[0] != nil and sfPure notin prc.flags: resultSym = prc.ast.sons[resultPos].sym - resultAsgn = ropef("var $1 = $2;$n", [mangleName(resultSym), + resultAsgn = ropef("var $# = $#;$n", [mangleName(resultSym), createVar(p, resultSym.typ, isIndirect(resultSym))]) gen(p, prc.ast.sons[resultPos], a) if a.com != nil: appf(returnStmt, "$1;$n", [a.com]) - returnStmt = ropef("return $1;$n", [a.res]) + returnStmt = ropef("return $#;$n", [a.res]) genStmt(p, prc.getBody, r) - r.com = ropef("function $1($2) {$n$3$4$5}$n", - [name, header, resultAsgn, genProcBody(p, prc, r), returnStmt]) + r.com = ropef("function $#($#) {$n$#$#$#$#}$n", + [name, header, p.locals, resultAsgn, + genProcBody(p, prc, r), returnStmt]) r.res = nil #if gVerbosity >= 3: # echo "END generated code for: " & prc.name.s @@ -1512,9 +1518,10 @@ proc genStmt(p: var TProc, n: PNode, r: var TCompRes) = nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: nil of nkProcDef, nkMethodDef, nkConverterDef: var s = n.sons[namePos].sym - if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: - var r2: TCompRes - genSym(p, n.sons[namePos], r2) + if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: + #var r2: TCompRes + genSym(p, n.sons[namePos], r) + r.res = nil of nkGotoState, nkState: internalError(n.info, "first class iterators not implemented") else: @@ -1566,6 +1573,7 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) = of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) of nkPar: genTupleConstr(p, n, r) + of nkObjConstr: genObjConstr(p, n, r) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r) of nkAddr, nkHiddenAddr: genAddr(p, n, r) of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r) @@ -1626,7 +1634,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode = if m.module == nil: InternalError(n.info, "myProcess") initProc(p, globals, m, nil, m.module.options) genModule(p, n, r) - app(p.g.code, p.data) + app(p.g.code, p.locals) app(p.g.code, mergeStmt(r)) proc myClose(b: PPassContext, n: PNode): PNode = @@ -1660,10 +1668,10 @@ proc myClose(b: PPassContext, n: PNode): PNode = discard writeRopeIfNotEqual(con(genHeader(), code), outfile) proc myOpenCached(s: PSym, rd: PRodReader): PPassContext = - InternalError("symbol files are not possible with the Ecmas code generator") + InternalError("symbol files are not possible with the JS code generator") result = nil proc myOpen(s: PSym): PPassContext = result = newModule(s) -const ecmasgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) +const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 0c3eea3be..ebcd67046 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -322,7 +322,7 @@ proc generateThunk(prc: PNode, dest: PType): PNode = # we cannot generate a proper thunk here for GC-safety reasons (see internal # documentation): - if gCmd == cmdCompileToEcmaScript: return prc + if gCmd == cmdCompileToJS: return prc result = newNodeIT(nkClosure, prc.info, dest) var conv = newNodeIT(nkHiddenStdConv, prc.info, dest) conv.add(emptyNode) @@ -391,7 +391,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) = gatherVars(o, inner, body) # dummy closure param needed? if inner.closureParam == nil and n.sym.typ.callConv == ccClosure: - assert tfCapturesEnv notin n.sym.typ.flags + #assert tfCapturesEnv notin n.sym.typ.flags dummyClosureParam(o, inner) # only transform if it really needs a closure: if inner.closureParam != nil: @@ -443,10 +443,15 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) = for i in countup(0, sonsLen(n) - 1): searchForInnerProcs(o, n.sons[i]) -proc newAsgnStmt(le, ri: PNode): PNode = - result = newNodeI(nkFastAsgn, ri.info) - result.add(le) - result.add(ri) +proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = + # Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would + # mean to be able to capture string literals which have no GC header. + # However this can only happen if the capture happens through a parameter, + # which is however the only case when we generate an assignment in the first + # place. + result = newNodeI(nkAsgn, info, 2) + result.sons[0] = le + result.sons[1] = ri proc addVar*(father, v: PNode) = var vpart = newNodeI(nkIdentDefs, v.info) @@ -481,13 +486,13 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode = if local.kind == skParam: # maybe later: (sfByCopy in local.flags) # add ``env.param = param`` - result.add(newAsgnStmt(fieldAccess, newSymNode(local))) + result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info)) IdNodeTablePut(o.localsToAccess, local, fieldAccess) # add support for 'up' references: for e, field in items(scope.deps): # add ``env.up = env2`` result.add(newAsgnStmt(indirectAccess(env, field, env.info), - newSymNode(getClosureVar(o, e)))) + newSymNode(getClosureVar(o, e)), env.info)) proc transformOuterProc(o: POuterContext, n: PNode): PNode = if n == nil: return nil @@ -543,7 +548,7 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode = if x != nil: n.sons[i] = x proc liftLambdas*(fn: PSym, body: PNode): PNode = - if body.kind == nkEmpty or gCmd == cmdCompileToEcmaScript: + if body.kind == nkEmpty or gCmd == cmdCompileToJS: # ignore forward declaration: result = body else: @@ -566,7 +571,7 @@ proc liftLambdas*(fn: PSym, body: PNode): PNode = result = ex proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode = - if body.kind == nkEmpty or gCmd == cmdCompileToEcmaScript: + if body.kind == nkEmpty or gCmd == cmdCompileToJS: result = body else: var o = newOuterContext(module) diff --git a/compiler/lexer.nim b/compiler/lexer.nim old mode 100755 new mode 100644 index 02f63aee8..bf9bf5343 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -16,7 +16,7 @@ # DOS or Macintosh text files, even when it is not the native format. import - hashes, options, msgs, strutils, platform, idents, lexbase, llstream, + hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream, wordrecg const @@ -530,10 +530,10 @@ proc HandleCRLF(L: var TLexer, pos: int): int = case L.buf[pos] of CR: registerLine() - result = lexbase.HandleCR(L, pos) + result = nimlexbase.HandleCR(L, pos) of LF: registerLine() - result = lexbase.HandleLF(L, pos) + result = nimlexbase.HandleLF(L, pos) else: result = pos proc getString(L: var TLexer, tok: var TToken, rawMode: bool) = @@ -559,7 +559,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) = pos = HandleCRLF(L, pos) buf = L.buf add(tok.literal, tnl) - of lexbase.EndOfFile: + of nimlexbase.EndOfFile: var line2 = L.linenumber L.LineNumber = line lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart) @@ -581,7 +581,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) = else: inc(pos) # skip '"' break - elif c in {CR, LF, lexbase.EndOfFile}: + elif c in {CR, LF, nimlexbase.EndOfFile}: lexMessage(L, errClosingQuoteExpected) break elif (c == '\\') and not rawMode: @@ -675,10 +675,12 @@ proc scanComment(L: var TLexer, tok: var TToken) = # a comment ends if the next line does not start with the # on the same # column after only whitespace tok.tokType = tkComment + # iNumber contains the number of '\n' in the token + tok.iNumber = 0 var col = getColNumber(L, pos) while true: var lastBackslash = -1 - while buf[pos] notin {CR, LF, lexbase.EndOfFile}: + while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: if buf[pos] == '\\': lastBackslash = pos+1 add(tok.literal, buf[pos]) inc(pos) @@ -686,7 +688,7 @@ proc scanComment(L: var TLexer, tok: var TToken) = # a backslash is a continuation character if only followed by spaces # plus a newline: while buf[lastBackslash] == ' ': inc(lastBackslash) - if buf[lastBackslash] notin {CR, LF, lexbase.EndOfFile}: + if buf[lastBackslash] notin {CR, LF, nimlexbase.EndOfFile}: # false positive: lastBackslash = -1 @@ -699,6 +701,7 @@ proc scanComment(L: var TLexer, tok: var TToken) = if buf[pos] == '#' and (col == indent or lastBackslash > 0): tok.literal.add "\n" col = indent + inc tok.iNumber else: if buf[pos] > ' ': L.indentAhead = indent @@ -838,7 +841,7 @@ proc rawGetTok(L: var TLexer, tok: var TToken) = else: if c in OpChars: getOperator(L, tok) - elif c == lexbase.EndOfFile: + elif c == nimlexbase.EndOfFile: tok.toktype = tkEof else: tok.literal = c & "" diff --git a/compiler/lists.nim b/compiler/lists.nim old mode 100755 new mode 100644 diff --git a/compiler/llstream.nim b/compiler/llstream.nim old mode 100755 new mode 100644 diff --git a/compiler/lookups.nim b/compiler/lookups.nim old mode 100755 new mode 100644 diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim old mode 100755 new mode 100644 index 6ff714dc7..005ee3ae3 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -38,7 +38,10 @@ proc newSysType(kind: TTypeKind, size: int): PType = proc getSysSym(name: string): PSym = result = StrTableGet(systemModule.tab, getIdent(name)) - if result == nil: rawMessage(errSystemNeeds, name) + if result == nil: + rawMessage(errSystemNeeds, name) + result = newSym(skError, getIdent(name), systemModule, systemModule.info) + result.typ = newType(tyError, systemModule) if result.kind == skStub: loadStub(result) proc sysTypeFromName*(name: string): PType = diff --git a/compiler/main.nim b/compiler/main.nim old mode 100755 new mode 100644 index 41073a591..46ef65e81 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -14,7 +14,7 @@ import llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs, os, lists, condsyms, rodread, rodwrite, ropes, trees, times, wordrecg, sem, semdata, idents, passes, docgen, extccomp, - cgen, ecmasgen, cgendata, + cgen, jsgen, cgendata, json, nversion, platform, nimconf, importer, passaux, depends, evals, types, idgen, tables, docgen2, service, magicsys, parser, crc, ccgutils @@ -155,7 +155,8 @@ proc compileModule(fileIdx: int32, flags: TSymFlags): PSym = growCache gMemCacheData, fileIdx gMemCacheData[fileIdx].needsRecompile = Probing result = newModule(fileIdx) - var rd = handleSymbolFile(result) + #var rd = handleSymbolFile(result) + var rd: PRodReader result.flags = result.flags + flags if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}: rd = handleSymbolFile(result) @@ -251,8 +252,8 @@ proc CommandCompileToC = compileProject() - if optCaasEnabled in gGlobalOptions: - cgenCaasUpdate() + if compilationCachePresent: + updateCachedModules() if gCmd != cmdRun: extccomp.CallCCompiler(changeFileExt(gProjectFull, "")) @@ -301,18 +302,19 @@ when has_LLVM_Backend: #registerPass(cleanupPass()) compileProject() -proc CommandCompileToEcmaScript = +proc CommandCompileToJS = #incl(gGlobalOptions, optSafeCode) - setTarget(osEcmaScript, cpuEcmaScript) + setTarget(osJS, cpuJS) #initDefines() DefineSymbol("nimrod") # 'nimrod' is always defined - DefineSymbol("ecmascript") + DefineSymbol("ecmascript") # For backward compatibility + DefineSymbol("js") semanticPasses() - registerPass(ecmasgenPass) + registerPass(jsgenPass) compileProject() proc InteractivePasses = - incl(gGlobalOptions, optSafeCode) + #incl(gGlobalOptions, optSafeCode) #setTarget(osNimrodVM, cpuNimrodVM) initDefines() DefineSymbol("nimrodvm") @@ -390,6 +392,15 @@ proc wantMainModule = gProjectMainIdx = addFileExt(gProjectFull, nimExt).fileInfoIdx +proc requireMainModuleOption = + if optMainModule.len == 0: + Fatal(gCmdLineInfo, errMainModuleMustBeSpecified) + else: + gProjectName = optMainModule + gProjectFull = gProjectPath / gProjectName + + gProjectMainIdx = addFileExt(gProjectFull, nimExt).fileInfoIdx + proc resetMemory = resetCompilationLists() ccgutils.resetCaches() @@ -461,9 +472,10 @@ proc MainCommand = gCmd = cmdCompileToC wantMainModule() CommandCompileToC() - of "cpp", "compiletocpp": + of "cpp", "compiletocpp": extccomp.cExt = ".cpp" gCmd = cmdCompileToCpp + if cCompiler == ccGcc: setCC("gpp") wantMainModule() DefineSymbol("cpp") CommandCompileToC() @@ -481,10 +493,10 @@ proc MainCommand = CommandCompileToC() else: rawMessage(errInvalidCommandX, command) - of "js", "compiletoecmascript": - gCmd = cmdCompileToEcmaScript + of "js", "compiletojs": + gCmd = cmdCompileToJS wantMainModule() - CommandCompileToEcmaScript() + CommandCompileToJS() of "compiletollvm": gCmd = cmdCompileToLLVM wantMainModule() @@ -526,9 +538,30 @@ proc MainCommand = wantMainModule() CommandGenDepend() of "dump": - gCmd = cmdDump - condsyms.ListSymbols() - for it in iterSearchPath(searchPaths): MsgWriteln(it) + gcmd = cmdDump + if getconfigvar("dump.format") == "json": + requireMainModuleOption() + + var definedSymbols = newJArray() + for s in definedSymbolNames(): definedSymbols.elems.add(%s) + + var libpaths = newJArray() + for dir in itersearchpath(searchpaths): libpaths.elems.add(%dir) + + var dumpdata = % [ + (key: "version", val: %VersionAsString), + (key: "project_path", val: %gProjectFull), + (key: "defined_symbols", val: definedSymbols), + (key: "lib_paths", val: libpaths) + ] + + outWriteLn($dumpdata) + else: + outWriteLn("-- list of currently defined symbols --") + for s in definedSymbolNames(): outWriteLn(s) + outWriteLn("-- end of list --") + + for it in iterSearchPath(searchpaths): msgWriteLn(it) of "check": gCmd = cmdCheck wantMainModule() @@ -558,13 +591,14 @@ proc MainCommand = wantMainModule() CommandSuggest() of "serve": + isServing = true gGlobalOptions.incl(optCaasEnabled) msgs.gErrorMax = high(int) # do not stop after first error serve(MainCommand) else: rawMessage(errInvalidCommandX, command) - if msgs.gErrorCounter == 0 and gCmd notin {cmdInterpret, cmdRun}: + if msgs.gErrorCounter == 0 and gCmd notin {cmdInterpret, cmdRun, cmdDump}: rawMessage(hintSuccessX, [$gLinesCompiled, formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3), formatSize(getTotalMem())]) diff --git a/compiler/msgs.nim b/compiler/msgs.nim old mode 100755 new mode 100644 index 0f2affc36..6062ebd7f --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -83,7 +83,9 @@ type errInvalidCommandX, errXOnlyAtModuleScope, errXNeedsParamObjectType, errTemplateInstantiationTooNested, errInstantiationFrom, - errInvalidIndexValueForTuple, errCommandExpectsFilename, errXExpected, + errInvalidIndexValueForTuple, errCommandExpectsFilename, + errMainModuleMustBeSpecified, + errXExpected, errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, @@ -301,6 +303,7 @@ const errInstantiationFrom: "instantiation from here", errInvalidIndexValueForTuple: "invalid index value for tuple subscript", errCommandExpectsFilename: "command expects a filename argument", + errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", errXExpected: "\'$1\' expected", errInvalidSectionStart: "invalid section start", errGridTableNotImplemented: "grid table is not implemented", @@ -506,6 +509,8 @@ var gCodegenLineInfo* = newLineInfo(int32(1), 1, 1) proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = raise newException(ERecoverableError, msg) +proc sourceLine*(i: TLineInfo): PRope + var gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent} gErrorCounter*: int = 0 # counts the number of errors @@ -523,8 +528,8 @@ proc SuggestWriteln*(s: string) = stdoutSocket.send(s & "\c\L") proc SuggestQuit*() = - if isNil(stdoutSocket): quit(0) - else: + if not isServing: quit(0) + elif not isNil(stdoutSocket): stdoutSocket.send("\c\L") raise newException(ESuggestDone, "suggest done") @@ -707,6 +712,11 @@ proc rawMessage*(msg: TMsgKind, arg: string) = var lastError = UnknownLineInfo() +proc writeSurroundingSrc(info: TLineInfo) = + const indent = " " + MsgWriteln(indent & info.sourceLine.data) + MsgWriteln(indent & repeatChar(info.col, ' ') & '^') + proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, eh: TErrorHandling) = var frmt: string @@ -732,6 +742,8 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, coordToStr(info.col), getMessageStr(msg, arg)] if not ignoreMsg: MsgWriteln(s) + if optPrintSurroundingSrc and msg in errMin..errMax: + info.writeSurroundingSrc handleError(msg, eh, s) proc Fatal*(info: TLineInfo, msg: TMsgKind, arg = "") = @@ -771,6 +783,12 @@ proc addSourceLine*(fileIdx: int32, line: string) = proc sourceLine*(i: TLineInfo): PRope = if i.fileIndex < 0: return nil + + if not optPreserveOrigSource and + fileInfos[i.fileIndex].lines.len == 0: + for line in lines(i.toFullPath): + addSourceLine i.fileIndex, line.string + InternalAssert i.fileIndex < fileInfos.len and i.line <= fileInfos[i.fileIndex].lines.len diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim old mode 100755 new mode 100644 diff --git a/compiler/lexbase.nim b/compiler/nimlexbase.nim old mode 100755 new mode 100644 similarity index 100% rename from compiler/lexbase.nim rename to compiler/nimlexbase.nim diff --git a/compiler/nimrod.cfg b/compiler/nimrod.cfg old mode 100755 new mode 100644 index aa49729b9..a0e59aa09 --- a/compiler/nimrod.cfg +++ b/compiler/nimrod.cfg @@ -6,7 +6,7 @@ hint[XDeclaredButNotUsed]:off path:"llvm" path:"$projectPath/.." -path:"$nimrod/packages/docutils" +path:"$lib/packages/docutils" define:booting diff --git a/compiler/nimrod.dot b/compiler/nimrod.dot old mode 100755 new mode 100644 index 36429844f..e9663d7c5 --- a/compiler/nimrod.dot +++ b/compiler/nimrod.dot @@ -472,32 +472,32 @@ cgen -> wordrecg; cgen -> rnimsyn; cgen -> treetab; cgen -> cgmeth; -ecmasgen -> ast; -ecmasgen -> astalgo; -ecmasgen -> strutils; -ecmasgen -> nhashes; -ecmasgen -> trees; -ecmasgen -> platform; -ecmasgen -> magicsys; -ecmasgen -> extccomp; -ecmasgen -> options; -ecmasgen -> nversion; -ecmasgen -> nimsets; -ecmasgen -> msgs; -ecmasgen -> crc; -ecmasgen -> bitsets; -ecmasgen -> idents; -ecmasgen -> lists; -ecmasgen -> types; -ecmasgen -> os; -ecmasgen -> times; -ecmasgen -> ropes; -ecmasgen -> math; -ecmasgen -> passes; -ecmasgen -> ccgutils; -ecmasgen -> wordrecg; -ecmasgen -> rnimsyn; -ecmasgen -> rodread; +jsgen -> ast; +jsgen -> astalgo; +jsgen -> strutils; +jsgen -> nhashes; +jsgen -> trees; +jsgen -> platform; +jsgen -> magicsys; +jsgen -> extccomp; +jsgen -> options; +jsgen -> nversion; +jsgen -> nimsets; +jsgen -> msgs; +jsgen -> crc; +jsgen -> bitsets; +jsgen -> idents; +jsgen -> lists; +jsgen -> types; +jsgen -> os; +jsgen -> times; +jsgen -> ropes; +jsgen -> math; +jsgen -> passes; +jsgen -> ccgutils; +jsgen -> wordrecg; +jsgen -> rnimsyn; +jsgen -> rodread; interact -> llstream; interact -> strutils; interact -> ropes; @@ -561,7 +561,7 @@ main -> passes; main -> docgen; main -> extccomp; main -> cgen; -main -> ecmasgen; +main -> jsgen; main -> platform; main -> interact; main -> nimconf; diff --git a/compiler/nimrod.ini b/compiler/nimrod.ini old mode 100755 new mode 100644 index ea98c743b..9febe24aa --- a/compiler/nimrod.ini +++ b/compiler/nimrod.ini @@ -57,8 +57,6 @@ Files: "compiler/*.nim" Files: "build/empty.txt" Files: "bin/empty.txt" -Files: "packages/docutils/*.nim" - [Lib] Files: "lib/nimbase.h" @@ -84,7 +82,8 @@ Files: "lib/wrappers/zip/libzip_all.c" Files: "lib/windows/*.nim" Files: "lib/posix/*.nim" -Files: "lib/ecmas/*.nim" +Files: "lib/js/*.nim" +Files: "lib/packages/docutils/*.nim" [Other] diff --git a/compiler/nimrod.nim b/compiler/nimrod.nim old mode 100755 new mode 100644 index 3666046ea..3fa80cebb --- a/compiler/nimrod.nim +++ b/compiler/nimrod.nim @@ -60,7 +60,7 @@ proc HandleCmdLine() = if gCmd == cmdRun: tccgen.run() if optRun in gGlobalOptions: - if gCmd == cmdCompileToEcmaScript: + if gCmd == cmdCompileToJS: var ex = quoteIfContainsWhite( completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir)) execExternalProgram("node " & ex & ' ' & service.arguments) diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim old mode 100755 new mode 100644 index 14e9ae726..93bccae24 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -44,9 +44,9 @@ proc inSet(s: PNode, elem: PNode): bool = return true result = false -proc overlap(a, b: PNode): bool = - if a.kind == nkRange: - if b.kind == nkRange: +proc overlap(a, b: PNode): bool = + if a.kind == nkRange: + if b.kind == nkRange: # X..Y and C..D overlap iff (X <= D and C <= Y) result = leValue(a.sons[0], b.sons[1]) and leValue(b.sons[0], a.sons[1]) @@ -98,13 +98,13 @@ proc ToTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode = result.typ = settype result.info = info e = 0 - while e < high(s) * elemSize: + while e < len(s) * elemSize: if bitSetIn(s, e): a = e b = e while true: Inc(b) - if (b > high(s) * elemSize) or not bitSetIn(s, b): break + if (b >= len(s) * elemSize) or not bitSetIn(s, b): break Dec(b) if a == b: addSon(result, newIntTypeNode(nkIntLit, a + first, elemType)) diff --git a/compiler/nversion.nim b/compiler/nversion.nim old mode 100755 new mode 100644 index d60a7a445..39fcd6c18 --- a/compiler/nversion.nim +++ b/compiler/nversion.nim @@ -18,5 +18,5 @@ const VersionPatch* = 1 VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch - RodFileVersion* = "1210" # modify this if the rod-format changes! + RodFileVersion* = "1212" # modify this if the rod-format changes! diff --git a/compiler/options.nim b/compiler/options.nim old mode 100755 new mode 100644 index d74bc7304..a7d513dc5 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -68,7 +68,7 @@ type # please make sure we have under 32 options TCommands* = enum # Nimrod's commands # **keep binary compatible** cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, - cmdCompileToEcmaScript, cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, + cmdCompileToJS, cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, cmdGenDepend, cmdDump, cmdCheck, # semantic checking for whole project cmdParse, # parse a single file (for debugging) @@ -81,7 +81,7 @@ type # please make sure we have under 32 options cmdRun # run the project via TCC backend TStringSeq* = seq[string] TGCMode* = enum # the selected GC - gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2 + gcNone, gcBoehm, gcMarkAndSweep, gcRefc, gcV2, gcGenerational const ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, @@ -99,16 +99,26 @@ var searchPaths*, lazyPaths*: TLinkedList outFile*: string = "" headerFile*: string = "" - gVerbosity*: int # how verbose the compiler is + gVerbosity* = 1 # how verbose the compiler is gNumberOfProcessors*: int # number of processors - gWholeProject*: bool # for 'doc2': output any dependency + gWholeProject*: bool # for 'doc2': output any dependency gEvalExpr* = "" # expression for idetools --eval gLastCmdTime*: float # when caas is enabled, we measure each command gListFullPaths*: bool - + isServing*: bool = false + proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc +template compilationCachePresent*: expr = + {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {} + +template optPreserveOrigSource*: expr = + optEmbedOrigSrc in gGlobalOptions + +template optPrintSurroundingSrc*: expr = + gVerbosity >= 2 + const genSubDir* = "nimcache" NimExt* = "nim" @@ -123,6 +133,7 @@ const # additional configuration variables: var gConfigVars* = newStringTable(modeStyleInsensitive) + gDllOverrides = newStringtable(modeCaseInsensitive) libpath* = "" gProjectName* = "" # holds a name like 'nimrod' gProjectPath* = "" # holds a path like /home/alice/projects/nimrod/compiler/ @@ -255,6 +266,20 @@ proc libCandidates*(s: string, dest: var seq[string]) = else: add(dest, s) +proc canonDynlibName(s: string): string = + let start = if s.startsWith("lib"): 3 else: 0 + let ende = strutils.find(s, {'(', ')', '.'}) + if ende >= 0: + result = s.substr(start, ende-1) + else: + result = s.substr(start) + +proc inclDynlibOverride*(lib: string) = + gDllOverrides[lib.canonDynlibName] = "true" + +proc isDynlibOverride*(lib: string): bool = + result = gDllOverrides.hasKey(lib.canonDynlibName) + proc binaryStrSearch*(x: openarray[string], y: string): int = var a = 0 var b = len(x) - 1 diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index 21c7faf19..de7bcaeee 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -187,21 +187,24 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = else: result = arLValue of nkDotExpr: - if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}: + if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in + {tyVar, tyPtr, tyRef}: result = arLValue - else: + else: result = isAssignable(owner, n.sons[0]) if result != arNone and sfDiscriminant in n.sons[1].sym.flags: result = arDiscriminant of nkBracketExpr: - if skipTypes(n.sons[0].typ, abstractInst).kind in {tyVar, tyPtr, tyRef}: + if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in + {tyVar, tyPtr, tyRef}: result = arLValue else: result = isAssignable(owner, n.sons[0]) of nkHiddenStdConv, nkHiddenSubConv, nkConv: # Object and tuple conversions are still addressable, so we skip them # XXX why is 'tyOpenArray' allowed here? - if skipTypes(n.typ, abstractPtrs).kind in {tyOpenArray, tyTuple, tyObject}: + if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in + {tyOpenArray, tyTuple, tyObject}: result = isAssignable(owner, n.sons[1]) elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct): # types that are equal modulo distinction preserve l-value: diff --git a/compiler/parsecfg.nim b/compiler/parsecfg.nim old mode 100755 new mode 100644 index 965160bd5..e0d1afff1 --- a/compiler/parsecfg.nim +++ b/compiler/parsecfg.nim @@ -12,7 +12,7 @@ # standard library. import - llstream, nhashes, strutils, lexbase + llstream, nhashes, strutils, nimlexbase type TCfgEventKind* = enum diff --git a/compiler/parser.nim b/compiler/parser.nim old mode 100755 new mode 100644 index a2c7f71d2..769aa7a3e --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -254,15 +254,21 @@ proc indexExprList(p: var TParser, first: PNode, k: TNodeKind, optPar(p) eat(p, endToken) -proc exprColonEqExpr(p: var TParser, kind: TNodeKind, tok: TTokType): PNode = +proc exprColonEqExpr(p: var TParser): PNode = var a = parseExpr(p) - if p.tok.tokType == tok: - result = newNodeP(kind, p) + if p.tok.tokType == tkColon: + result = newNodeP(nkExprColonExpr, p) getTok(p) #optInd(p, result) addSon(result, a) addSon(result, parseExpr(p)) - else: + elif p.tok.tokType == tkEquals: + result = newNodeP(nkExprEqExpr, p) + getTok(p) + #optInd(p, result) + addSon(result, a) + addSon(result, parseExpr(p)) + else: result = a proc exprList(p: var TParser, endTok: TTokType, result: PNode) = @@ -309,14 +315,13 @@ proc qualifiedIdentListAux(p: var TParser, endTok: TTokType, result: PNode) = optInd(p, a) eat(p, endTok) -proc exprColonEqExprListAux(p: var TParser, elemKind: TNodeKind, - endTok, sepTok: TTokType, result: PNode) = +proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi}) getTok(p) optInd(p, result) while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof) and (p.tok.tokType != tkSad) and (p.tok.tokType != tkInd): - var a = exprColonEqExpr(p, elemKind, sepTok) + var a = exprColonEqExpr(p) addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) @@ -324,10 +329,10 @@ proc exprColonEqExprListAux(p: var TParser, elemKind: TNodeKind, optPar(p) eat(p, endTok) -proc exprColonEqExprList(p: var TParser, kind, elemKind: TNodeKind, - endTok, sepTok: TTokType): PNode = +proc exprColonEqExprList(p: var TParser, kind: TNodeKind, + endTok: TTokType): PNode = result = newNodeP(kind, p) - exprColonEqExprListAux(p, elemKind, endTok, sepTok, result) + exprColonEqExprListAux(p, endTok, result) proc setOrTableConstr(p: var TParser): PNode = result = newNodeP(nkCurly, p) @@ -338,7 +343,7 @@ proc setOrTableConstr(p: var TParser): PNode = result.kind = nkTableConstr else: while p.tok.tokType notin {tkCurlyRi, tkEof, tkSad, tkInd}: - var a = exprColonEqExpr(p, nkExprColonExpr, tkColon) + var a = exprColonEqExpr(p) if a.kind == nkExprColonExpr: result.kind = nkTableConstr addSon(result, a) if p.tok.tokType != tkComma: break @@ -471,16 +476,15 @@ proc identOrLiteral(p: var TParser): PNode = of tkNil: result = newNodeP(nkNilLit, p) getTok(p) - of tkParLe: + of tkParLe: # () constructor - result = exprColonEqExprList(p, nkPar, nkExprColonExpr, tkParRi, tkColon) - of tkCurlyLe: + result = exprColonEqExprList(p, nkPar, tkParRi) + of tkCurlyLe: # {} constructor result = setOrTableConstr(p) - of tkBracketLe: + of tkBracketLe: # [] constructor - result = exprColonEqExprList(p, nkBracket, nkExprColonExpr, tkBracketRi, - tkColon) + result = exprColonEqExprList(p, nkBracket, tkBracketRi) of tkCast: result = parseCast(p) else: @@ -496,8 +500,11 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = var a = result result = newNodeP(nkCall, p) addSon(result, a) - exprColonEqExprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result) - parseDoBlocks(p, result) + exprColonEqExprListAux(p, tkParRi, result) + if result.len > 1 and result.sons[1].kind == nkExprColonExpr: + result.kind = nkObjConstr + else: + parseDoBlocks(p, result) of tkDo: var a = result result = newNodeP(nkCall, p) @@ -564,7 +571,7 @@ proc parsePragma(p: var TParser): PNode = optInd(p, result) while (p.tok.tokType != tkCurlyDotRi) and (p.tok.tokType != tkCurlyRi) and (p.tok.tokType != tkEof) and (p.tok.tokType != tkSad): - var a = exprColonEqExpr(p, nkExprColonExpr, tkColon) + var a = exprColonEqExpr(p) addSon(result, a) if p.tok.tokType == tkComma: getTok(p) @@ -739,7 +746,7 @@ proc parseProcExpr(p: var TParser, isExpr: bool): PNode = proc isExprStart(p: TParser): bool = case p.tok.tokType of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, - tkProc, tkIterator, tkBind, + tkProc, tkIterator, tkBind, tkAddr, tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, tkTuple, tkObject, tkType, tkWhen, tkCase, tkShared: result = true @@ -1200,9 +1207,9 @@ proc parseRoutine(p: var TParser, kind: TNodeKind): PNode = addSon(result, ast.emptyNode) indAndComment(p, result) # XXX: document this in the grammar! -proc newCommentStmt(p: var TParser): PNode = +proc newCommentStmt(p: var TParser): PNode = result = newNodeP(nkCommentStmt, p) - result.info.line = result.info.line - int16(1) + result.info.line = result.info.line - int16(1) - int16(p.tok.iNumber) type TDefParser = proc (p: var TParser): PNode {.nimcall.} diff --git a/compiler/pas2nim/nimrod.cfg b/compiler/pas2nim/nimrod.cfg old mode 100755 new mode 100644 diff --git a/compiler/pas2nim/pas2nim.nim b/compiler/pas2nim/pas2nim.nim old mode 100755 new mode 100644 diff --git a/compiler/pas2nim/paslex.nim b/compiler/pas2nim/paslex.nim old mode 100755 new mode 100644 diff --git a/compiler/pas2nim/pasparse.nim b/compiler/pas2nim/pasparse.nim old mode 100755 new mode 100644 diff --git a/compiler/passaux.nim b/compiler/passaux.nim old mode 100755 new mode 100644 diff --git a/compiler/passes.nim b/compiler/passes.nim old mode 100755 new mode 100644 diff --git a/compiler/pbraces.nim b/compiler/pbraces.nim old mode 100755 new mode 100644 diff --git a/compiler/pendx.nim b/compiler/pendx.nim old mode 100755 new mode 100644 diff --git a/compiler/platform.nim b/compiler/platform.nim old mode 100755 new mode 100644 index 1e73faa5d..d63fe6636 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -21,7 +21,7 @@ type # conditionals to condsyms (end of module). osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris, osIrix, osNetbsd, osFreebsd, osOpenbsd, osAix, osPalmos, osQnx, osAmiga, - osAtari, osNetware, osMacos, osMacosx, osHaiku, osEcmaScript, osNimrodVM, + osAtari, osNetware, osMacos, osMacosx, osHaiku, osJS, osNimrodVM, osStandalone type @@ -136,7 +136,7 @@ const objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {ospNeedsPIC, ospPosix, ospLacksThreadVars}), - (name: "EcmaScript", parDir: "..", + (name: "JS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", @@ -155,7 +155,7 @@ type # alias conditionals to condsyms (end of module). cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuArm, - cpuEcmaScript, cpuNimrodVM, cpuAVR + cpuJS, cpuNimrodVM, cpuAVR type TEndian* = enum @@ -177,7 +177,7 @@ const (name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), - (name: "ecmascript", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32), + (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32), (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)] diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim old mode 100755 new mode 100644 index 8f06beecd..ba761739a --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -202,6 +202,8 @@ proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib = result = newLib(kind) result.path = path Append(c.libs, result) + if path.kind in {nkStrLit..nkTripleStrLit}: + result.isOverriden = options.isDynLibOverride(path.strVal) proc expectDynlibNode(c: PContext, n: PNode): PNode = if n.kind != nkExprColonExpr: @@ -225,8 +227,9 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) = else: if n.kind == nkExprColonExpr: var lib = getLib(c, libDynamic, expectDynlibNode(c, n)) - addToLib(lib, sym) - incl(sym.loc.flags, lfDynamicLib) + if not lib.isOverriden: + addToLib(lib, sym) + incl(sym.loc.flags, lfDynamicLib) else: incl(sym.loc.flags, lfExportLib) # since we'll be loading the dynlib symbols dynamically, we must use diff --git a/compiler/procfind.nim b/compiler/procfind.nim old mode 100755 new mode 100644 index 4a1fb5ac8..2db6247e1 --- a/compiler/procfind.nim +++ b/compiler/procfind.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -10,28 +10,24 @@ # This module implements the searching for procs and iterators. # This is needed for proper handling of forward declarations. -import +import ast, astalgo, msgs, semdata, types, trees -proc equalGenericParams(procA, procB: PNode): bool = - var a, b: PSym - result = procA == procB - if result: return - if (procA == nil) or (procB == nil): return - if sonsLen(procA) != sonsLen(procB): return - for i in countup(0, sonsLen(procA) - 1): - if procA.sons[i].kind != nkSym: +proc equalGenericParams(procA, procB: PNode): bool = + if sonsLen(procA) != sonsLen(procB): return + for i in countup(0, sonsLen(procA) - 1): + if procA.sons[i].kind != nkSym: InternalError(procA.info, "equalGenericParams") return - if procB.sons[i].kind != nkSym: + if procB.sons[i].kind != nkSym: InternalError(procB.info, "equalGenericParams") return - a = procA.sons[i].sym - b = procB.sons[i].sym - if (a.name.id != b.name.id) or + let a = procA.sons[i].sym + let b = procB.sons[i].sym + if a.name.id != b.name.id or not sameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}): return - if (a.ast != nil) and (b.ast != nil): - if not ExprStructuralEquivalent(a.ast, b.ast): return + if a.ast != nil and b.ast != nil: + if not ExprStructuralEquivalent(a.ast, b.ast): return result = true proc SearchForProc*(c: PContext, fn: PSym, tos: int): PSym = @@ -39,19 +35,32 @@ proc SearchForProc*(c: PContext, fn: PSym, tos: int): PSym = # the same the sym in the symbol table is returned, else nil. var it: TIdentIter result = initIdentIter(it, c.tab.stack[tos], fn.Name) - while result != nil: - if (result.Kind == fn.kind): - if equalGenericParams(result.ast.sons[genericParamsPos], - fn.ast.sons[genericParamsPos]): + if isGenericRoutine(fn): + # we simply check the AST; this is imprecise but nearly the best what + # can be done; this doesn't work either though as type constraints are + # not kept in the AST .. + while result != nil: + if result.Kind == fn.kind and isGenericRoutine(result): + let genR = result.ast.sons[genericParamsPos] + let genF = fn.ast.sons[genericParamsPos] + if ExprStructuralEquivalent(genR, genF) and + ExprStructuralEquivalent(result.ast.sons[paramsPos], + fn.ast.sons[paramsPos]) and + equalGenericParams(genR, genF): + return + result = NextIdentIter(it, c.tab.stack[tos]) + else: + while result != nil: + if result.Kind == fn.kind and not isGenericRoutine(result): case equalParams(result.typ.n, fn.typ.n) of paramsEqual: - return - of paramsIncompatible: + return + of paramsIncompatible: LocalError(fn.info, errNotOverloadable, fn.name.s) - return - of paramsNotEqual: + return + of paramsNotEqual: nil - result = NextIdentIter(it, c.tab.stack[tos]) + result = NextIdentIter(it, c.tab.stack[tos]) when false: proc paramsFitBorrow(child, parent: PNode): bool = diff --git a/compiler/readme.txt b/compiler/readme.txt old mode 100755 new mode 100644 diff --git a/compiler/renderer.nim b/compiler/renderer.nim old mode 100755 new mode 100644 index 48a190ec1..b4ef52100 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -367,7 +367,7 @@ proc lsub(n: PNode): int = else: result = len(atom(n)) of succ(nkEmpty)..pred(nkTripleStrLit), succ(nkTripleStrLit)..nkNilLit: result = len(atom(n)) - of nkCall, nkBracketExpr, nkCurlyExpr, nkConv, nkPattern: + of nkCall, nkBracketExpr, nkCurlyExpr, nkConv, nkPattern, nkObjConstr: result = lsub(n.sons[0]) + lcomma(n, 1) + 2 of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: result = lsub(n[1]) of nkCast: result = lsub(n.sons[0]) + lsub(n.sons[1]) + len("cast[]()") @@ -779,7 +779,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkRStrLit: put(g, tkRStrLit, atom(n)) of nkCharLit: put(g, tkCharLit, atom(n)) of nkNilLit: put(g, tkNil, atom(n)) # complex expressions - of nkCall, nkConv, nkDotCall, nkPattern: + of nkCall, nkConv, nkDotCall, nkPattern, nkObjConstr: if sonsLen(n) >= 1: gsub(g, n.sons[0]) put(g, tkParLe, "(") gcomma(g, n, 1) diff --git a/compiler/rodread.nim b/compiler/rodread.nim old mode 100755 new mode 100644 index a4287f8e8..3e04a755d --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -127,7 +127,7 @@ type options: TOptions reason: TReasonForRecompile modDeps: seq[int32] - files: TStringSeq + files: seq[int32] dataIdx: int # offset of start of data section convertersIdx: int # offset of start of converters section initIdx, interfIdx, compilerProcsIdx, methodsIdx: int @@ -140,6 +140,7 @@ type memfile: TMemFile # unfortunately there is no point in time where we # can close this! XXX methods*: TSymSeq + inViewMode: bool PRodReader* = ref TRodReader @@ -367,12 +368,12 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym = inc(r.pos) id = decodeVInt(r.s, r.pos) setId(id) - else: + else: InternalError(info, "decodeSym: no id") if r.s[r.pos] == '&': inc(r.pos) ident = getIdent(decodeStr(r.s, r.pos)) - else: + else: InternalError(info, "decodeSym: no ident") #echo "decoding: {", ident.s result = PSym(IdTableGet(r.syms, id)) @@ -381,8 +382,14 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym = result.id = id IdTablePut(r.syms, result, result) if debugIds: registerID(result) - elif (result.id != id): + elif result.id != id: InternalError(info, "decodeSym: wrong id") + elif result.kind != skStub and not r.inViewMode: + # we already loaded the symbol + return + else: + reset(result[]) + result.id = id result.kind = k result.name = ident # read the rest of the symbol description: if r.s[r.pos] == '^': @@ -495,7 +502,7 @@ proc processCompilerProcs(r: PRodReader, module: PSym) = IdTablePut(r.syms, s, s) StrTableAdd(rodCompilerProcs, s) -proc processIndex(r: PRodReader, idx: var TIndex) = +proc processIndex(r: PRodReader; idx: var TIndex; outf: TFile = nil) = var key, val, tmp: int inc(r.pos, 2) # skip "(\10" inc(r.line) @@ -505,10 +512,11 @@ proc processIndex(r: PRodReader, idx: var TIndex) = inc(r.pos) key = idx.lastIdxKey + tmp val = decodeVInt(r.s, r.pos) + idx.lastIdxVal - else: + else: key = idx.lastIdxKey + 1 val = tmp + idx.lastIdxVal IITablePut(idx.tab, key, val) + if not outf.isNil: outf.write(key, " ", val, "\n") idx.lastIdxKey = key idx.lastIdxVal = val setID(key) # ensure that this id will not be used @@ -523,7 +531,7 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool = # new command forces us to consider it here :-) case old of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, - cmdCompileToEcmaScript, cmdCompileToLLVM: + cmdCompileToJS, cmdCompileToLLVM: if new in {cmdDoc, cmdCheck, cmdIdeTools, cmdPretty, cmdDef, cmdInteractive}: return false @@ -576,9 +584,10 @@ proc processRodFile(r: PRodReader, crc: TCrc32) = inc(r.pos, 2) # skip "(\10" inc(r.line) while r.s[r.pos] != ')': - var relativePath = decodeStr(r.s, r.pos) - var resolvedPath = relativePath.findModule - r.files.add(if resolvedPath.len > 0: resolvedPath else: relativePath) + let relativePath = decodeStr(r.s, r.pos) + let resolvedPath = relativePath.findModule + let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath + r.files.add(finalPath.fileInfoIdx) inc(r.pos) # skip #10 inc(r.line) if r.s[r.pos] == ')': inc(r.pos) @@ -586,7 +595,7 @@ proc processRodFile(r: PRodReader, crc: TCrc32) = inc(r.pos, 2) # skip "(\10" inc(r.line) while r.s[r.pos] != ')': - w = r.files[decodeVInt(r.s, r.pos)] + w = r.files[decodeVInt(r.s, r.pos)].toFullPath inc(r.pos) # skip ' ' inclCrc = decodeVInt(r.s, r.pos) if r.reason == rrNone: @@ -598,8 +607,8 @@ proc processRodFile(r: PRodReader, crc: TCrc32) = if r.s[r.pos] == ')': inc(r.pos) of "DEPS": inc(r.pos) # skip ':' - while r.s[r.pos] > '\x0A': - r.modDeps.add int32(decodeVInt(r.s, r.pos)) + while r.s[r.pos] > '\x0A': + r.modDeps.add(r.files[int32(decodeVInt(r.s, r.pos))]) if r.s[r.pos] == ' ': inc(r.pos) of "INTERF": r.interfIdx = r.pos + 2 @@ -670,11 +679,9 @@ proc newRodReader(modfilename: string, crc: TCrc32, add(version, r.s[r.pos]) inc(r.pos) if r.s[r.pos] == '\x0A': inc(r.pos) - if version == RodFileVersion: + if version != RodFileVersion: # since ROD files are only for caching, no backwards compatibility is # needed - processRodFile(r, crc) - else: result = nil else: result = nil @@ -719,8 +726,14 @@ proc findSomeWhere(id: int) = echo "found id ", id, " in ", gMods[i].filename proc getReader(moduleId: int): PRodReader = - InternalAssert moduleId >= 0 and moduleId < gMods.len - result = gMods[moduleId].rd + # we can't index 'gMods' here as it's indexed by a *file index* which is not + # the module ID! We could introduce a mapping ID->PRodReader but I'll leave + # this for later versions if benchmarking shows the linear search causes + # problems: + for i in 0 .. 0: rawMessage(hintProcessing, reasonToFrmt[result] % filename) @@ -859,7 +873,9 @@ proc rawLoadStub(s: PSym) = var d = IITableGet(rd.index.tab, s.id) if d == invalidKey: InternalError("loadStub: invalid key") var rs = decodeSymSafePos(rd, d, UnknownLineInfo()) - if rs != s: + if rs != s: + #echo "rs: ", toHex(cast[int](rs.position), int.sizeof * 2), + # "\ns: ", toHex(cast[int](s.position), int.sizeof * 2) InternalError(rs.info, "loadStub: wrong symbol") elif rs.id != theId: InternalError(rs.info, "loadStub: wrong ID") @@ -894,3 +910,264 @@ proc getBody*(s: PSym): PNode = InitIdTable(gTypeTable) InitStrTable(rodCompilerProcs) + +# viewer: +proc writeNode(f: TFile; n: PNode) = + f.write("(") + if n != nil: + f.write($n.kind) + if n.typ != nil: + f.write('^') + f.write(n.typ.id) + case n.kind + of nkCharLit..nkInt64Lit: + if n.intVal != 0: + f.write('!') + f.write(n.intVal) + of nkFloatLit..nkFloat64Lit: + if n.floatVal != 0.0: + f.write('!') + f.write($n.floatVal) + of nkStrLit..nkTripleStrLit: + if n.strVal != "": + f.write('!') + f.write(n.strVal.escape) + of nkIdent: + f.write('!') + f.write(n.ident.s) + of nkSym: + f.write('!') + f.write(n.sym.id) + else: + for i in countup(0, sonsLen(n) - 1): + writeNode(f, n.sons[i]) + f.write(")") + +proc writeSym(f: TFile; s: PSym) = + if s == nil: + f.write("{}\n") + return + f.write("{") + f.write($s.kind) + f.write('+') + f.write(s.id) + f.write('&') + f.write(s.name.s) + if s.typ != nil: + f.write('^') + f.write(s.typ.id) + if s.owner != nil: + f.write('*') + f.write(s.owner.id) + if s.flags != {}: + f.write('$') + f.write($s.flags) + if s.magic != mNone: + f.write('@') + f.write($s.magic) + if s.options != gOptions: + f.write('!') + f.write($s.options) + if s.position != 0: + f.write('%') + f.write($s.position) + if s.offset != -1: + f.write('`') + f.write($s.offset) + if s.constraint != nil: + f.write('#') + f.writeNode(s.constraint) + if s.ast != nil: + f.writeNode(s.ast) + f.write("}\n") + +proc writeType(f: TFile; t: PType) = + if t == nil: + f.write("[]\n") + return + f.write('[') + f.write($t.kind) + f.write('+') + f.write($t.id) + if t.n != nil: + f.writeNode(t.n) + if t.flags != {}: + f.write('$') + f.write($t.flags) + if t.callConv != low(t.callConv): + f.write('?') + f.write($t.callConv) + if t.owner != nil: + f.write('*') + f.write($t.owner.id) + if t.sym != nil: + f.write('&') + f.write(t.sym.id) + if t.size != -1: + f.write('/') + f.write($t.size) + if t.align != 2: + f.write('=') + f.write($t.align) + if t.containerID != 0: + f.write('@') + f.write($t.containerID) + for i in countup(0, sonsLen(t) - 1): + if t.sons[i] == nil: + f.write("^()") + else: + f.write('^') + f.write($t.sons[i].id) + f.write("]\n") + +proc viewFile(rodfile: string) = + var r = newRodReader(rodfile, 0, 0) + if r == nil: + rawMessage(errGenerated, "cannot open file (or maybe wrong version):" & + rodfile) + return + r.inViewMode = true + var outf = system.open(rodfile.changeFileExt(".rod.txt"), fmWrite) + while r.s[r.pos] != '\0': + let section = rdWord(r) + case section + of "CRC": + inc(r.pos) # skip ':' + outf.writeln("CRC:", $decodeVInt(r.s, r.pos)) + of "ID": + inc(r.pos) # skip ':' + r.moduleID = decodeVInt(r.s, r.pos) + setID(r.moduleID) + outf.writeln("ID:", $r.moduleID) + of "OPTIONS": + inc(r.pos) # skip ':' + r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos))) + outf.writeln("OPTIONS:", $r.options) + of "GOPTIONS": + inc(r.pos) # skip ':' + let dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos))) + outf.writeln("GOPTIONS:", $dep) + of "CMD": + inc(r.pos) # skip ':' + let dep = cast[TCommands](int32(decodeVInt(r.s, r.pos))) + outf.writeln("CMD:", $dep) + of "DEFINES": + inc(r.pos) # skip ':' + var d = 0 + outf.write("DEFINES:") + while r.s[r.pos] > '\x0A': + let w = decodeStr(r.s, r.pos) + inc(d) + outf.write(" ", w) + if r.s[r.pos] == ' ': inc(r.pos) + outf.write("\n") + of "FILES": + inc(r.pos, 2) # skip "(\10" + inc(r.line) + outf.write("FILES(\n") + while r.s[r.pos] != ')': + let relativePath = decodeStr(r.s, r.pos) + let resolvedPath = relativePath.findModule + let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath + r.files.add(finalPath.fileInfoIdx) + inc(r.pos) # skip #10 + inc(r.line) + outf.writeln finalPath + if r.s[r.pos] == ')': inc(r.pos) + outf.write(")\n") + of "INCLUDES": + inc(r.pos, 2) # skip "(\10" + inc(r.line) + outf.write("INCLUDES(\n") + while r.s[r.pos] != ')': + let w = r.files[decodeVInt(r.s, r.pos)] + inc(r.pos) # skip ' ' + let inclCrc = decodeVInt(r.s, r.pos) + if r.s[r.pos] == '\x0A': + inc(r.pos) + inc(r.line) + outf.write(w, " ", inclCrc, "\n") + if r.s[r.pos] == ')': inc(r.pos) + outf.write(")\n") + of "DEPS": + inc(r.pos) # skip ':' + outf.write("DEPS:") + while r.s[r.pos] > '\x0A': + let v = int32(decodeVInt(r.s, r.pos)) + r.modDeps.add(r.files[v]) + if r.s[r.pos] == ' ': inc(r.pos) + outf.write(" ", r.files[v]) + outf.write("\n") + of "INTERF", "COMPILERPROCS": + inc r.pos, 2 + if section == "INTERF": r.interfIdx = r.pos + else: r.compilerProcsIdx = r.pos + outf.write(section, "(\n") + while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): + let w = decodeStr(r.s, r.pos) + inc(r.pos) + let key = decodeVInt(r.s, r.pos) + inc(r.pos) # #10 + outf.write(w, " ", key, "\n") + if r.s[r.pos] == ')': inc r.pos + outf.write(")\n") + of "INDEX": + outf.write(section, "(\n") + processIndex(r, r.index, outf) + outf.write(")\n") + of "IMPORTS": + outf.write(section, "(\n") + processIndex(r, r.imports, outf) + outf.write(")\n") + of "CONVERTERS", "METHODS": + inc r.pos + if section == "METHODS": r.methodsIdx = r.pos + else: r.convertersIdx = r.pos + outf.write(section, ":") + while r.s[r.pos] > '\x0A': + let d = decodeVInt(r.s, r.pos) + outf.write(" ", $d) + if r.s[r.pos] == ' ': inc(r.pos) + outf.write("\n") + of "DATA": + inc(r.pos, 2) + r.dataIdx = r.pos + outf.write("DATA(\n") + while r.s[r.pos] != ')': + if r.s[r.pos] == '(': + outf.writeNode decodeNode(r, UnknownLineInfo()) + outf.write("\n") + elif r.s[r.pos] == '[': + outf.writeType decodeType(r, UnknownLineInfo()) + else: + outf.writeSym decodeSym(r, UnknownLineInfo()) + if r.s[r.pos] == '\x0A': + inc(r.pos) + inc(r.line) + if r.s[r.pos] == ')': inc r.pos + outf.write(")\n") + of "INIT": + outf.write("INIT(\n") + inc r.pos, 2 + r.initIdx = r.pos + while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': + let d = decodeVInt(r.s, r.pos) + inc(r.pos) # #10 + #let p = r.pos + #r.pos = d + r.dataIdx + #outf.writeNode decodeNode(r, UnknownLineInfo()) + #outf.write("\n") + #r.pos = p + if r.s[r.pos] == ')': inc r.pos + outf.write(")\n") + else: + InternalError("invalid section: '" & section & + "' at " & $r.line & " in " & r.filename) + skipSection(r) + if r.s[r.pos] == '\x0A': + inc(r.pos) + inc(r.line) + outf.close + +when isMainModule: + viewFile(paramStr(1).addFileExt(rodExt)) diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim old mode 100755 new mode 100644 diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim old mode 100755 new mode 100644 index 9eab844d1..0221977bf --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -205,6 +205,9 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) = return # we need no surrounding [] here because the type is in a line of its own if t.kind == tyForward: InternalError("encodeType: tyForward") + # for the new rodfile viewer we use a preceeding [ so that the data section + # can easily be disambiguated: + add(result, '[') encodeVInt(ord(t.kind), result) add(result, '+') encodeVInt(t.id, result) diff --git a/compiler/ropes.nim b/compiler/ropes.nim old mode 100755 new mode 100644 diff --git a/compiler/sem.nim b/compiler/sem.nim old mode 100755 new mode 100644 index 555f5e7b7..805af9e31 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -19,19 +19,15 @@ import # implementation -type - TExprFlag = enum - efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType - TExprFlags = set[TExprFlag] - -proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode -proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode +proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.procvar.} +proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode {. + procvar.} proc semExprNoType(c: PContext, n: PNode): PNode proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semProcBody(c: PContext, n: PNode): PNode proc fitNode(c: PContext, formal: PType, arg: PNode): PNode -proc changeType(n: PNode, newType: PType) +proc changeType(n: PNode, newType: PType, check: bool) proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode proc semTypeNode(c: PContext, n: PNode, prev: PType): PType @@ -200,14 +196,12 @@ proc addCodeForGenerics(c: PContext, n: PNode) = addSon(n, prc.ast) c.lastGenericIdx = c.generics.len -proc semExprNoFlags(c: PContext, n: PNode): PNode {.procvar.} = - result = semExpr(c, n, {}) - proc myOpen(module: PSym): PPassContext = var c = newContext(module) if c.p != nil: InternalError(module.info, "sem.myOpen") c.semConstExpr = semConstExpr - c.semExpr = semExprNoFlags + c.semExpr = semExpr + c.semOperand = semOperand c.semConstBoolExpr = semConstBoolExpr c.semOverloadedCall = semOverloadedCall c.semTypeNode = semTypeNode diff --git a/compiler/semcall.nim b/compiler/semcall.nim old mode 100755 new mode 100644 index 67d157261..be41299ad --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -85,7 +85,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, #writeMatches(alt) if c.inCompilesContext > 0: # quick error message for performance of 'compiles' built-in: - GlobalError(n.Info, errAmbiguousCallXYZ, "") + GlobalError(n.Info, errGenerated, "ambiguous call") elif gErrorCounter == 0: # don't cascade errors var args = "(" @@ -174,7 +174,10 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = # common case; check the only candidate has the right # number of generic type parameters: if safeLen(s.ast.sons[genericParamsPos]) != n.len-1: - return explicitGenericInstError(n) + let expected = safeLen(s.ast.sons[genericParamsPos]) + LocalError(n.info, errGenerated, "cannot instantiate: " & renderTree(n) & + "; got " & $(n.len-1) & " type(s) but expected " & $expected) + return n result = explicitGenericSym(c, n, s) elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}: # choose the generic proc with the proper number of type parameters. @@ -204,7 +207,7 @@ proc SearchForBorrowProc(c: PContext, fn: PSym, tos: int): PSym = call.add(newIdentNode(fn.name, fn.info)) for i in 1.. = ss) or (skipTypes(dst, abstractInst).kind in IntegralTypes) or - (skipTypes(src, abstractInst).kind in IntegralTypes) + (skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes) proc isSymChoice(n: PNode): bool {.inline.} = result = n.kind in nkSymChoices @@ -199,7 +213,7 @@ proc semConv(c: PContext, n: PNode, s: PSym): PNode = addSon(result, copyTree(n.sons[0])) addSon(result, semExprWithType(c, n.sons[1])) var op = result.sons[1] - + if not isSymChoice(op): let status = checkConvertible(result.typ, op.typ) case status @@ -238,24 +252,24 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = LocalError(n.info, errXExpectsTypeOrValue, opToStr[m]) else: n.sons[1] = semExprWithType(c, n.sons[1]) - restoreOldStyleType(n.sons[1]) var typ = skipTypes(n.sons[1].typ, abstractVarRange) case typ.Kind of tySequence, tyString, tyOpenArray, tyVarargs: n.typ = getSysType(tyInt) of tyArrayConstr, tyArray: - n.typ = n.sons[1].typ.sons[0] # indextype + n.typ = typ.sons[0] # indextype of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32: - n.typ = n.sons[1].typ + # do not skip the range! + n.typ = n.sons[1].typ.skipTypes(abstractVar) else: LocalError(n.info, errInvalidArgForX, opToStr[m]) result = n -proc semSizeof(c: PContext, n: PNode): PNode = +proc semSizeof(c: PContext, n: PNode): PNode = if sonsLen(n) != 2: LocalError(n.info, errXExpectsTypeOrValue, "sizeof") - else: + else: n.sons[1] = semExprWithType(c, n.sons[1]) - restoreOldStyleType(n.sons[1]) + #restoreOldStyleType(n.sons[1]) n.typ = getSysType(tyInt) result = n @@ -265,10 +279,10 @@ proc semOf(c: PContext, n: PNode): PNode = n.sons[2] = semExprWithType(c, n.sons[2]) #restoreOldStyleType(n.sons[1]) #restoreOldStyleType(n.sons[2]) - let a = skipTypes(n.sons[1].typ, typedescPtrs) - let b = skipTypes(n.sons[2].typ, typedescPtrs) - let x = skipTypes(n.sons[1].typ, abstractPtrs) - let y = skipTypes(n.sons[2].typ, abstractPtrs) + let a = skipTypes(n.sons[1].typ, abstractPtrs) + let b = skipTypes(n.sons[2].typ, abstractPtrs) + let x = skipTypes(n.sons[1].typ, abstractPtrs-{tyTypeDesc}) + let y = skipTypes(n.sons[2].typ, abstractPtrs-{tyTypeDesc}) if x.kind == tyTypeDesc or y.kind != tyTypeDesc: LocalError(n.info, errXExpectsObjectTypes, "of") @@ -319,7 +333,7 @@ proc semIs(c: PContext, n: PNode): PNode = proc semOpAux(c: PContext, n: PNode) = const flags = {efDetermineType} - for i in countup(1, n.sonsLen- 1): + for i in countup(1, n.sonsLen-1): var a = n.sons[i] if a.kind == nkExprEqExpr and sonsLen(a) == 2: var info = a.sons[0].info @@ -328,7 +342,7 @@ proc semOpAux(c: PContext, n: PNode) = a.typ = a.sons[1].typ else: n.sons[i] = semExprWithType(c, a, flags) - + proc overloadedCallOpr(c: PContext, n: PNode): PNode = # quick check if there is *any* () operator overloaded: var par = getIdent("()") @@ -340,11 +354,11 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): addSon(result, n.sons[i]) result = semExpr(c, result) -proc changeType(n: PNode, newType: PType) = +proc changeType(n: PNode, newType: PType, check: bool) = case n.kind of nkCurly, nkBracket: for i in countup(0, sonsLen(n) - 1): - changeType(n.sons[i], elemType(newType)) + changeType(n.sons[i], elemType(newType), check) of nkPar: if newType.kind != tyTuple: InternalError(n.info, "changeType: no tuple type for constructor") @@ -359,15 +373,21 @@ proc changeType(n: PNode, newType: PType) = if f == nil: internalError(m.info, "changeType(): invalid identifier") return - changeType(n.sons[i].sons[1], f.typ) + changeType(n.sons[i].sons[1], f.typ, check) else: for i in countup(0, sonsLen(n) - 1): var m = n.sons[i] var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i]) addSon(a, newSymNode(newType.n.sons[i].sym)) addSon(a, m) - changeType(m, newType.sons[i]) + changeType(m, newType.sons[i], check) n.sons[i] = a + of nkCharLit..nkUInt64Lit: + if check: + let value = n.intVal + if value < firstOrd(newType) or value > lastOrd(newType): + LocalError(n.info, errGenerated, "cannot convert " & $value & + " to " & typeToString(newType)) else: nil n.typ = newType @@ -384,13 +404,13 @@ proc arrayConstrType(c: PContext, n: PNode): PType = typ.sons[0] = makeRangeType(c, 0, sonsLen(n) - 1, n.info) result = typ -proc semArrayConstr(c: PContext, n: PNode): PNode = +proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result = newNodeI(nkBracket, n.info) result.typ = newTypeS(tyArrayConstr, c) rawAddSon(result.typ, nil) # index type if sonsLen(n) == 0: rawAddSon(result.typ, newTypeS(tyEmpty, c)) # needs an empty basetype! - else: + else: var x = n.sons[0] var lastIndex: biggestInt = 0 var indexType = getSysType(tyInt) @@ -413,7 +433,7 @@ proc semArrayConstr(c: PContext, n: PNode): PNode = localError(x.info, errInvalidOrderInArrayConstructor) x = x.sons[1] - n.sons[i] = semExprWithType(c, x) + n.sons[i] = semExprWithType(c, x, flags*{efAllowDestructor}) addSon(result, fitNode(c, typ, n.sons[i])) inc(lastIndex) addSonSkipIntLit(result.typ, typ) @@ -447,11 +467,11 @@ proc fixAbstractType(c: PContext, n: PNode) = elif skipTypes(it.sons[1].typ, abstractVar).kind in {tyNil, tyArrayConstr, tyTuple, tySet}: var s = skipTypes(it.typ, abstractVar) - changeType(it.sons[1], s) + changeType(it.sons[1], s, check=true) n.sons[i] = it.sons[1] of nkBracket: # an implicitely constructed array (passed to an open array): - n.sons[i] = semArrayConstr(c, it) + n.sons[i] = semArrayConstr(c, it, {}) else: nil #if (it.typ == nil): @@ -483,9 +503,10 @@ proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = result = n case n.kind - of nkSym: + of nkSym: # n.sym.typ can be nil in 'check' mode ... - if n.sym.typ != nil and skipTypes(n.sym.typ, abstractInst).kind != tyVar: + if n.sym.typ != nil and + skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind != tyVar: incl(n.sym.flags, sfAddrTaken) result = newHiddenAddrTaken(c, n) of nkDotExpr: @@ -493,16 +514,16 @@ proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = if n.sons[1].kind != nkSym: internalError(n.info, "analyseIfAddressTaken") return - if skipTypes(n.sons[1].sym.typ, abstractInst).kind != tyVar: + if skipTypes(n.sons[1].sym.typ, abstractInst-{tyTypeDesc}).kind != tyVar: incl(n.sons[1].sym.flags, sfAddrTaken) result = newHiddenAddrTaken(c, n) of nkBracketExpr: checkMinSonsLen(n, 1) - if skipTypes(n.sons[0].typ, abstractInst).kind != tyVar: + if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind != tyVar: if n.sons[0].kind == nkSym: incl(n.sons[0].sym.flags, sfAddrTaken) result = newHiddenAddrTaken(c, n) else: - result = newHiddenAddrTaken(c, n) # BUGFIX! + result = newHiddenAddrTaken(c, n) proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = checkMinSonsLen(n, 1) @@ -517,16 +538,24 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = if n.sons[0].kind == nkSym and n.sons[0].sym.magic in FakeVarParams: # BUGFIX: check for L-Value still needs to be done for the arguments! + # note sometimes this is eval'ed twice so we check for nkHiddenAddr here: for i in countup(1, sonsLen(n) - 1): if i < sonsLen(t) and t.sons[i] != nil and - skipTypes(t.sons[i], abstractInst).kind == tyVar: + skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: if isAssignable(c, n.sons[i]) notin {arLValue, arLocalLValue}: - LocalError(n.sons[i].info, errVarForOutParamNeeded) + if n.sons[i].kind != nkHiddenAddr: + LocalError(n.sons[i].info, errVarForOutParamNeeded) return - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): + if n.sons[i].kind == nkHiddenCallConv: + # we need to recurse explicitly here as converters can create nested + # calls and then they wouldn't be analysed otherwise + analyseIfAddressTakenInCall(c, n.sons[i]) + semProcvarCheck(c, n.sons[i]) if i < sonsLen(t) and - skipTypes(t.sons[i], abstractInst).kind == tyVar: - n.sons[i] = analyseIfAddressTaken(c, n.sons[i]) + skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: + if n.sons[i].kind != nkHiddenAddr: + n.sons[i] = analyseIfAddressTaken(c, n.sons[i]) include semmagic @@ -541,11 +570,12 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = call.add(n.sons[0]) var allConst = true for i in 1 .. < n.len: - let a = getConstExpr(c.module, n.sons[i]) - if a != nil: call.add(a) - else: + var a = getConstExpr(c.module, n.sons[i]) + if a == nil: allConst = false - call.add(n.sons[i]) + a = n.sons[i] + if a.kind == nkHiddenStdConv: a = a.sons[1] + call.add(a) if allConst: result = semfold.getConstExpr(c.module, call) if result.isNil: result = n @@ -617,6 +647,7 @@ proc semDirectCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, flags: TExprFlags): PNode = result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) +proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = result = nil checkMinSonsLen(n, 1) @@ -635,8 +666,9 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = let nOrig = n.copyTree semOpAux(c, n) var t: PType = nil - if (n.sons[0].typ != nil): t = skipTypes(n.sons[0].typ, abstractInst) - if (t != nil) and (t.kind == tyProc): + if n.sons[0].typ != nil: + t = skipTypes(n.sons[0].typ, abstractInst-{tyTypedesc}) + if t != nil and t.kind == tyProc: # This is a proc variable, apply normal overload resolution var m: TCandidate initCandidate(m, t) @@ -668,10 +700,11 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # we assume that a procedure that calls something indirectly # has side-effects: 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) let destType = t.skipTypes({tyTypeDesc, tyGenericInst}) result = semConv(c, n, symFromType(destType, n.info)) - return + return else: result = overloadedCallOpr(c, n) # Now that nkSym does not imply an iteration over the proc/iterator space, @@ -694,7 +727,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # this seems to be a hotspot in the compiler! let nOrig = n.copyTree - semOpAux(c, n) + #semLazyOpAux(c, n) result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) if result == nil: result = overloadedCallOpr(c, n) @@ -706,6 +739,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = of skMacro: result = semMacroExpr(c, result, nOrig, callee) of skTemplate: result = semTemplateExpr(c, result, callee) else: + semFinishOperands(c, n) activate(c, n) fixAbstractType(c, result) analyseIfAddressTakenInCall(c, result) @@ -714,7 +748,8 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = result = evalAtCompileTime(c, result) proc buildStringify(c: PContext, arg: PNode): PNode = - if arg.typ != nil and skipTypes(arg.typ, abstractInst).kind == tyString: + if arg.typ != nil and + skipTypes(arg.typ, abstractInst-{tyTypeDesc}).kind == tyString: result = arg else: result = newNodeI(nkCall, arg.info) @@ -752,7 +787,7 @@ proc discardCheck(result: PNode) = # XXX too much work and fixing would break bootstrapping: #Message(n.info, warnNilStatement) result.typ = nil - elif not ImplicitelyDiscardable(result) and result.typ.kind != tyError and + elif not ImplicitlyDiscardable(result) and result.typ.kind != tyError and gCmd != cmdInteractive: localError(result.info, errDiscardValue) @@ -845,16 +880,17 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = return semSym(c, n, s, flags) n.sons[0] = semExprWithType(c, n.sons[0], flags) - restoreOldStyleType(n.sons[0]) + #restoreOldStyleType(n.sons[0]) var i = considerAcc(n.sons[1]) var ty = n.sons[0].typ var f: PSym = nil result = nil - if isTypeExpr(n.sons[0]): + if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.len == 1: + if ty.kind == tyTypeDesc: ty = ty.sons[0] case ty.kind - of tyEnum: + of tyEnum: # look up if the identifier belongs to the enum: - while ty != nil: + while ty != nil: f = getSymFromList(ty.n, i) if f != nil: break ty = ty.sons[0] # enum inheritance @@ -881,7 +917,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # XXX: This is probably not relevant any more # reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim": ty = n.sons[0].Typ - + ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef}) var check: PNode = nil if ty.kind == tyObject: @@ -971,7 +1007,8 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = checkSonsLen(n, 2) n.sons[0] = makeDeref(n.sons[0]) for i in countup(1, sonsLen(n) - 1): - n.sons[i] = semExprWithType(c, n.sons[i], flags) + n.sons[i] = semExprWithType(c, n.sons[i], + flags*{efInTypeof, efDetermineType}) var indexType = if arr.kind == tyArray: arr.sons[0] else: getSysType(tyInt) var arg = IndexTypesMatch(c, indexType, n.sons[1].typ, n.sons[1]) if arg != nil: @@ -1082,11 +1119,13 @@ proc semAsgn(c: PContext, n: PNode): PNode = localError(a.info, errXCannotBeAssignedTo, renderTree(a, {renderNoComments})) else: - var - rhs = semExprWithType(c, n.sons[1]) + let lhs = n.sons[0] - if lhs.kind == nkSym and lhs.sym.kind == skResult and - lhs.sym.typ.kind == tyGenericParam: + lhsIsResult = lhs.kind == nkSym and lhs.sym.kind == skResult + var + rhs = semExprWithType(c, n.sons[1], + if lhsIsResult: {efAllowDestructor} else: {}) + if lhsIsResult and lhs.sym.typ.kind == tyGenericParam: if matchTypeClass(lhs.typ, rhs.typ): InternalAssert c.p.resultSym != nil lhs.typ = rhs.typ @@ -1128,7 +1167,7 @@ proc semProcBody(c: PContext, n: PNode): PNode = # ``result``: if result.kind == nkSym and result.sym == c.p.resultSym: nil - elif result.kind == nkNilLit or ImplicitelyDiscardable(result): + elif result.kind == nkNilLit or ImplicitlyDiscardable(result): # intended semantic: if it's 'discardable' and the context allows for it, # discard it. This is bad for chaining but nicer for C wrappers. # ambiguous :-( @@ -1531,21 +1570,22 @@ proc checkPar(n: PNode): TParKind = LocalError(n.sons[i].info, errNamedExprNotAllowed) return paNone -proc semTupleFieldsConstr(c: PContext, n: PNode): PNode = +proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result = newNodeI(nkPar, n.info) var typ = newTypeS(tyTuple, c) typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs var ids = initIntSet() - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): if (n.sons[i].kind != nkExprColonExpr) or - not (n.sons[i].sons[0].kind in {nkSym, nkIdent}): + not (n.sons[i].sons[0].kind in {nkSym, nkIdent}): illFormedAst(n.sons[i]) var id: PIdent if n.sons[i].sons[0].kind == nkIdent: id = n.sons[i].sons[0].ident else: id = n.sons[i].sons[0].sym.name if ContainsOrIncl(ids, id.id): localError(n.sons[i].info, errFieldInitTwice, id.s) - n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1]) + n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1], + flags*{efAllowDestructor}) var f = newSymS(skField, n.sons[i].sons[0], c) f.typ = skipIntLit(n.sons[i].sons[1].typ) rawAddSon(typ, f.typ) @@ -1554,14 +1594,56 @@ proc semTupleFieldsConstr(c: PContext, n: PNode): PNode = addSon(result, n.sons[i]) result.typ = typ -proc semTuplePositionsConstr(c: PContext, n: PNode): PNode = +proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result = n # we don't modify n, but compute the type: var typ = newTypeS(tyTuple, c) # leave typ.n nil! for i in countup(0, sonsLen(n) - 1): - n.sons[i] = semExprWithType(c, n.sons[i]) + n.sons[i] = semExprWithType(c, n.sons[i], flags*{efAllowDestructor}) addSonSkipIntLit(typ, n.sons[i].typ) result.typ = typ +proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = + var t = semTypeNode(c, n.sons[0], nil) + result = n + result.typ = t + result.kind = nkObjConstr + t = skipTypes(t, abstractInst) + if t.kind == tyRef: t = skipTypes(t.sons[0], abstractInst) + if t.kind != tyObject: + localError(n.info, errGenerated, "object constructor needs an object type") + return + var ids = initIntSet() + for i in 1.. lastOrd(n.typ): + LocalError(n.info, errGenerated, "cannot convert " & $value & + " to " & typeToString(n.typ)) + +proc foldConv*(n, a: PNode; check = false): PNode = # XXX range checks? case skipTypes(n.typ, abstractRange).kind of tyInt..tyInt64: case skipTypes(a.typ, abstractRange).kind - of tyFloat..tyFloat64: result = newIntNodeT(system.toInt(getFloat(a)), n) + of tyFloat..tyFloat64: + result = newIntNodeT(system.toInt(getFloat(a)), n) of tyChar: result = newIntNodeT(getOrdValue(a), n) else: result = a result.typ = n.typ - of tyFloat..tyFloat64: + if check: rangeCheck(n, result.intVal) + of tyFloat..tyFloat64: case skipTypes(a.typ, abstractRange).kind of tyInt..tyInt64, tyEnum, tyBool, tyChar: result = newFloatNodeT(toFloat(int(getOrdValue(a))), n) - else: + else: result = a result.typ = n.typ of tyOpenArray, tyVarargs, tyProc: @@ -490,7 +497,7 @@ proc getArrayConstr(m: PSym, n: PNode): PNode = proc foldArrayAccess(m: PSym, n: PNode): PNode = var x = getConstExpr(m, n.sons[0]) - if x == nil: return + if x == nil or x.typ.skipTypes({tyGenericInst}).kind == tyTypeDesc: return var y = getConstExpr(m, n.sons[1]) if y == nil: return @@ -516,13 +523,13 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode = LocalError(n.info, errIndexOutOfBounds) else: nil -proc foldFieldAccess(m: PSym, n: PNode): PNode = +proc foldFieldAccess(m: PSym, n: PNode): PNode = # a real field access; proc calls have already been transformed var x = getConstExpr(m, n.sons[0]) - if x == nil or x.kind != nkPar: return + if x == nil or x.kind notin {nkObjConstr, nkPar}: return var field = n.sons[1].sym - for i in countup(0, sonsLen(x) - 1): + for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1): var it = x.sons[i] if it.kind != nkExprColonExpr: # lookup per index: @@ -541,7 +548,12 @@ proc foldConStrStr(m: PSym, n: PNode): PNode = let a = getConstExpr(m, n.sons[i]) if a == nil: return nil result.strVal.add(getStrOrChar(a)) - + +proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode = + result = newSymNode(s, info) + result.typ = newType(tyTypeDesc, s.owner) + result.typ.addSonSkipIntLit(s.typ) + proc getConstExpr(m: PSym, n: PNode): PNode = result = nil case n.kind @@ -569,6 +581,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode = if sfFakeConst notin s.flags: result = copyTree(s.ast) elif s.kind in {skProc, skMethod}: # BUGFIX result = n + elif s.kind in {skType, skGenericParam}: + result = newSymNodeTypeDesc(s, n.info) of nkCharLit..nkNilLit: result = copyNode(n) of nkIfExpr: @@ -587,16 +601,18 @@ proc getConstExpr(m: PSym, n: PNode): PNode = LocalError(a.info, errCannotEvalXBecauseIncompletelyDefined, "sizeof") result = nil - elif skipTypes(a.typ, abstractInst).kind in {tyArray,tyObject,tyTuple}: + elif skipTypes(a.typ, typedescInst).kind in + IntegralTypes+NilableTypes+{tySet}: + #{tyArray,tyObject,tyTuple}: + result = newIntNodeT(getSize(a.typ), n) + else: result = nil # XXX: size computation for complex types is still wrong - else: - result = newIntNodeT(getSize(a.typ), n) of mLow: result = newIntNodeT(firstOrd(n.sons[1].typ), n) of mHigh: if skipTypes(n.sons[1].typ, abstractVar).kind notin - {tyOpenArray, tyVarargs, tySequence, tyString}: + {tyOpenArray, tyVarargs, tySequence, tyString}: result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n) else: var a = getArrayConstr(m, n.sons[1]) @@ -647,7 +663,14 @@ proc getConstExpr(m: PSym, n: PNode): PNode = if a == nil: return nil result.sons[i] = a incl(result.flags, nfAllConst) - of nkPar: + of nkObjConstr: + result = copyTree(n) + for i in countup(1, sonsLen(n) - 1): + var a = getConstExpr(m, n.sons[i].sons[1]) + if a == nil: return nil + result.sons[i].sons[1] = a + incl(result.flags, nfAllConst) + of nkPar: # tuple constructor result = copyTree(n) if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr): @@ -678,8 +701,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode = result.typ = n.typ of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast: var a = getConstExpr(m, n.sons[1]) - if a == nil: return - result = foldConv(n, a) + if a == nil: return + result = foldConv(n, a, check=n.kind == nkHiddenStdConv) of nkBracketExpr: result = foldArrayAccess(m, n) of nkDotExpr: result = foldFieldAccess(m, n) else: diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim old mode 100755 new mode 100644 index 4eaa5f39b..8c3cef027 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -30,7 +30,7 @@ proc getIdentNode(n: PNode): PNode = else: illFormedAst(n) result = n - + proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var TIntSet): PNode proc semGenericStmtScope(c: PContext, n: PNode, @@ -63,12 +63,12 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode = else: result = symChoice(c, n, s, scOpen) of skGenericParam: - result = newSymNode(s, n.info) + result = newSymNodeTypeDesc(s, n.info) of skParam: result = n of skType: if (s.typ != nil) and (s.typ.kind != tyGenericParam): - result = newSymNode(s, n.info) + result = newSymNodeTypeDesc(s, n.info) else: result = n else: result = newSymNode(s, n.info) @@ -156,13 +156,13 @@ proc semGenericStmt(c: PContext, n: PNode, of skProc, skMethod, skIterator, skConverter: result.sons[0] = symChoice(c, n.sons[0], s, scOption) first = 1 - of skGenericParam: - result.sons[0] = newSymNode(s, n.sons[0].info) + of skGenericParam: + result.sons[0] = newSymNodeTypeDesc(s, n.sons[0].info) first = 1 of skType: # bad hack for generics: if (s.typ != nil) and (s.typ.kind != tyGenericParam): - result.sons[0] = newSymNode(s, n.sons[0].info) + result.sons[0] = newSymNodeTypeDesc(s, n.sons[0].info) first = 1 else: result.sons[0] = newSymNode(s, n.sons[0].info) diff --git a/compiler/seminst.nim b/compiler/seminst.nim old mode 100755 new mode 100644 diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 119e1ef19..058964fc8 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -23,9 +23,10 @@ proc expectIntLit(c: PContext, n: PNode): int = proc semInstantiationInfo(c: PContext, n: PNode): PNode = result = newNodeIT(nkPar, n.info, n.typ) let idx = expectIntLit(c, n.sons[1]) + let useFullPaths = expectIntLit(c, n.sons[2]) let info = getInfoContext(idx) var filename = newNodeIT(nkStrLit, n.info, getSysType(tyString)) - filename.strVal = ToFilename(info) + filename.strVal = if useFullPaths != 0: info.toFullPath else: info.ToFilename var line = newNodeIT(nkIntLit, n.info, getSysType(tyInt)) line.intVal = ToLinenumber(info) result.add(filename) @@ -73,6 +74,33 @@ proc semBindSym(c: PContext, n: PNode): PNode = else: LocalError(n.sons[1].info, errUndeclaredIdentifier, sl.strVal) +proc semLocals(c: PContext, n: PNode): PNode = + var counter = 0 + var tupleType = newTypeS(tyTuple, c) + result = newNodeIT(nkPar, n.info, tupleType) + tupleType.n = newNodeI(nkRecList, n.info) + # for now we skip openarrays ... + for i in countdown(c.tab.tos-1, ModuleTablePos+1): + for it in items(c.tab.stack[i]): + # XXX parameters' owners are wrong for generics; this caused some pain + # for closures too; we should finally fix it. + #if it.owner != c.p.owner: return result + if it.kind in skLocalVars and + it.typ.skipTypes({tyGenericInst, tyVar}).kind notin + {tyVarargs, tyOpenArray, tyTypeDesc, tyExpr, tyStmt, tyEmpty}: + + var field = newSym(skField, it.name, getCurrOwner(), n.info) + field.typ = it.typ.skipTypes({tyGenericInst, tyVar}) + field.position = counter + inc(counter) + + addSon(tupleType.n, newSymNode(field)) + addSonSkipIntLit(tupleType, field.typ) + + var a = newSymNode(it, result.info) + if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a) + result.add(a) + proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode proc magicsAfterOverloadResolution(c: PContext, n: PNode, flags: TExprFlags): PNode = @@ -86,5 +114,6 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mOrd: result = semOrd(c, n) of mShallowCopy: result = semShallowCopy(c, n, flags) of mNBindSym: result = semBindSym(c, n) + of mLocals: result = semLocals(c, n) else: result = n diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index b2fd0fb04..f43820fa7 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -14,9 +14,7 @@ import # Second semantic checking pass over the AST. Necessary because the old # way had some inherent problems. Performs: # -# * procvar checks # * effect+exception tracking -# * closure analysis # * checks for invalid usages of compiletime magics (not implemented) # * checks for invalid usages of PNimNode (not implemented) # * later: will do an escape analysis for closures at least diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim old mode 100755 new mode 100644 index 9867c20be..d27247158 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -119,11 +119,11 @@ proc semWhile(c: PContext, n: PNode): PNode = closeScope(c.tab) proc toCover(t: PType): biggestInt = - var t2 = skipTypes(t, abstractVarRange) + var t2 = skipTypes(t, abstractVarRange-{tyTypeDesc}) if t2.kind == tyEnum and enumHasHoles(t2): result = sonsLen(t2.n) else: - result = lengthOrd(skipTypes(t, abstractVar)) + result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc})) proc semCase(c: PContext, n: PNode): PNode = # check selector: @@ -133,7 +133,7 @@ proc semCase(c: PContext, n: PNode): PNode = n.sons[0] = semExprWithType(c, n.sons[0]) var chckCovered = false var covered: biggestint = 0 - case skipTypes(n.sons[0].Typ, abstractVarRange).Kind + case skipTypes(n.sons[0].Typ, abstractVarRange-{tyTypeDesc}).Kind of tyInt..tyInt64, tyChar, tyEnum: chckCovered = true of tyFloat..tyFloat128, tyString, tyError: @@ -166,10 +166,10 @@ proc semCase(c: PContext, n: PNode): PNode = proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode = result = fitNode(c, typ, n) if result.kind in {nkHiddenStdConv, nkHiddenSubConv}: - changeType(result.sons[1], typ) + changeType(result.sons[1], typ, check=true) result = result.sons[1] - elif not sameType(result.typ, typ): - changeType(result, typ) + elif not sameType(result.typ, typ): + changeType(result, typ, check=false) proc findShadowedVar(c: PContext, v: PSym): PSym = for i in countdown(c.tab.tos - 2, ModuleTablePos+1): @@ -203,11 +203,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var typ: PType if a.sons[length-2].kind != nkEmpty: typ = semTypeNode(c, a.sons[length-2], nil) - else: + else: typ = nil var def: PNode - if a.sons[length-1].kind != nkEmpty: - def = semExprWithType(c, a.sons[length-1]) + if a.sons[length-1].kind != nkEmpty: + def = semExprWithType(c, a.sons[length-1], {efAllowDestructor}) # BUGFIX: ``fitNode`` is needed here! # check type compability between def.typ and typ: if typ != nil: def = fitNode(c, typ, def) @@ -301,52 +301,109 @@ proc semConst(c: PContext, n: PNode): PNode = addSon(b, copyTree(def)) addSon(result, b) -proc transfFieldLoopBody(n: PNode, forLoop: PNode, - tupleType: PType, - tupleIndex, first: int): PNode = +type + TFieldInstCtx = object # either 'tup[i]' or 'field' is valid + tupleType: PType # if != nil we're traversing a tuple + tupleIndex: int + field: PSym + replaceByFieldName: bool + +proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = case n.kind of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n of nkIdent: result = n var L = sonsLen(forLoop) - # field name: - if first > 0: + if c.replaceByFieldName: if n.ident.id == forLoop[0].ident.id: - if tupleType.n == nil: - # ugh, there are no field names: - result = newStrNode(nkStrLit, "") - else: - result = newStrNode(nkStrLit, tupleType.n.sons[tupleIndex].sym.name.s) + let fieldName = if c.tupleType.isNil: c.field.name.s + elif c.tupleType.n.isNil: "Field" & $c.tupleIndex + else: c.tupleType.n.sons[c.tupleIndex].sym.name.s + result = newStrNode(nkStrLit, fieldName) return # other fields: - for i in first..L-3: + for i in ord(c.replaceByFieldName)..L-3: if n.ident.id == forLoop[i].ident.id: var call = forLoop.sons[L-2] - var tupl = call.sons[i+1-first] - result = newNodeI(nkBracketExpr, n.info) - result.add(tupl) - result.add(newIntNode(nkIntLit, tupleIndex)) + var tupl = call.sons[i+1-ord(c.replaceByFieldName)] + if c.field.isNil: + result = newNodeI(nkBracketExpr, n.info) + result.add(tupl) + result.add(newIntNode(nkIntLit, c.tupleIndex)) + else: + result = newNodeI(nkDotExpr, n.info) + result.add(tupl) + result.add(newSymNode(c.field, n.info)) break else: + if n.kind == nkContinueStmt: + localError(n.info, errGenerated, + "'continue' not supported in a 'fields' loop") result = copyNode(n) newSons(result, sonsLen(n)) for i in countup(0, sonsLen(n)-1): - result.sons[i] = transfFieldLoopBody(n.sons[i], forLoop, - tupleType, tupleIndex, first) + result.sons[i] = instFieldLoopBody(c, n.sons[i], forLoop) -proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = - # so that 'break' etc. work as expected, we produce +type + TFieldsCtx = object + c: PContext + m: TMagic + +proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) = + case typ.kind + of nkSym: + var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid + fc.field = typ.sym + fc.replaceByFieldName = c.m == mFieldPairs + openScope(c.c.tab) + inc c.c.InUnrolledContext + let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop) + father.add(SemStmt(c.c, body)) + dec c.c.InUnrolledContext + closeScope(c.c.tab) + of nkNilLit: nil + of nkRecCase: + let L = forLoop.len + let call = forLoop.sons[L-2] + if call.len > 2: + LocalError(forLoop.info, errGenerated, + "parallel 'fields' iterator does not work for 'case' objects") + return + # iterate over the selector: + semForObjectFields(c, typ[0], forLoop, father) + # we need to generate a case statement: + var caseStmt = newNodeI(nkCaseStmt, forLoop.info) + # generate selector: + var access = newNodeI(nkDotExpr, forLoop.info, 2) + access.sons[0] = call.sons[1] + access.sons[1] = newSymNode(typ.sons[0].sym, forLoop.info) + caseStmt.add(semExprWithType(c.c, access)) + # copy the branches over, but replace the fields with the for loop body: + for i in 1 .. 0: var lastStmt = lastSon(result) - if lastStmt.kind != nkNilLit and not ImplicitelyDiscardable(lastStmt): + if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt): result.typ = lastStmt.typ #localError(lastStmt.info, errGenerated, # "Last expression must be explicitly returned if it " & diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim old mode 100755 new mode 100644 diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim old mode 100755 new mode 100644 index 9fc6a54d9..6f24e1f6d --- a/compiler/semthreads.nim +++ b/compiler/semthreads.nim @@ -311,6 +311,12 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner = # container construction: result = toNil # nothing until later for i in 0..n.len-1: aggregateOwner(result, analyse(c, n[i])) + of nkObjConstr: + if n.typ != nil and containsGarbageCollectedRef(n.typ): + result = toMine + else: + result = toNil # nothing until later + for i in 1..n.len-1: aggregateOwner(result, analyse(c, n[i])) of nkAddr, nkHiddenAddr: var a = lvalueSym(n) if a.kind == nkSym: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim old mode 100755 new mode 100644 index db2ef1b3f..3abc994ef --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -45,7 +45,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = e = newSymS(skEnumField, n.sons[i].sons[0], c) var v = semConstExpr(c, n.sons[i].sons[1]) var strVal: PNode = nil - case skipTypes(v.typ, abstractInst).kind + case skipTypes(v.typ, abstractInst-{tyTypeDesc}).kind of tyTuple: if sonsLen(v) == 2: strVal = v.sons[1] # second tuple part is the string value @@ -222,10 +222,11 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = let bound = result.typ.sons[0].sym if bound != nil: return bound - else: - return result.typ.sym - else: - return result.typ.sym + return result + if result.typ.sym == nil: + LocalError(n.info, errTypeExpected) + return errorSym(c, n) + return result.typ.sym if result.kind != skType: # this implements the wanted ``var v: V, x: V`` feature ... var ov: TOverloadIter @@ -323,8 +324,8 @@ proc semBranchRange(c: PContext, t, a, b: PNode, covered: var biggestInt): PNode checkMinSonsLen(t, 1) let ac = semConstExpr(c, a) let bc = semConstExpr(c, b) - let at = fitNode(c, t.sons[0].typ, ac) - let bt = fitNode(c, t.sons[0].typ, bc) + let at = fitNode(c, t.sons[0].typ, ac).skipConvTakeType + let bt = fitNode(c, t.sons[0].typ, bc).skipConvTakeType result = newNodeI(nkRange, a.info) result.add(at) @@ -388,7 +389,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int, return incl(a.sons[0].sym.flags, sfDiscriminant) var covered: biggestInt = 0 - var typ = skipTypes(a.sons[0].Typ, abstractVar) + var typ = skipTypes(a.sons[0].Typ, abstractVar-{tyTypeDesc}) if not isOrdinalType(typ): LocalError(n.info, errSelectorMustBeOrdinal) elif firstOrd(typ) < 0: @@ -873,6 +874,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = if s.typ == nil: if s.kind != skError: LocalError(n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) + elif s.kind == skParam and s.typ.kind == tyTypeDesc: + assert s.typ.len > 0 + InternalAssert prev == nil + result = s.typ.sons[0] elif prev == nil: result = s.typ else: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim old mode 100755 new mode 100644 diff --git a/compiler/service.nim b/compiler/service.nim index defdbceb7..b3c7fbc83 100644 --- a/compiler/service.nim +++ b/compiler/service.nim @@ -73,6 +73,9 @@ proc serve*(action: proc (){.nimcall.}) = var line = stdin.readLine.string if line == "quit": quit() execute line + echo "" + FlushFile(stdout) + of "tcp", "": var server = Socket() let p = getConfigVar("server.port") @@ -83,11 +86,10 @@ proc serve*(action: proc (){.nimcall.}) = new(stdoutSocket) while true: accept(server, stdoutSocket) - discard stdoutSocket.recvLine(inp) + stdoutSocket.readLine(inp) execute inp.string stdoutSocket.send("\c\L") stdoutSocket.close() else: echo "Invalid server.type:", typ quit 1 - diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim old mode 100755 new mode 100644 index 72a5762a9..e1882e1fb --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -97,6 +97,45 @@ proc copyCandidate(a: var TCandidate, b: TCandidate) = a.baseTypeMatch = b.baseTypeMatch copyIdTable(a.bindings, b.bindings) +proc sumGeneric(t: PType): int = + var t = t + while true: + case t.kind + of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyArrayConstr, + tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody: + t = t.lastSon + inc result + of tyVar: + # but do not make 'var T' more specific than 'T'! + t = t.sons[0] + of tyGenericInvokation, tyTuple: + result = ord(t.kind == tyGenericInvokation) + for i in 0 .. b.sumGeneric + + if a.len > 1 and b.len > 1: + let aa = a.sons[1].sumGeneric + let bb = b.sons[1].sumGeneric + var a = a + var b = b + + if aa < bb: swap(a, b) + # all must be better + for i in 2 .. = f0 and a1 <= f1: + result = isConvertible + elif a0 <= f1 and f0 <= a1: + # X..Y and C..D overlap iff (X <= D and C <= Y) + result = isConvertible + else: + result = isNone + proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = # is a subtype of f? result = isNone @@ -338,6 +402,8 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = result = typeRel(c, base(f), base(a)) # bugfix: accept integer conversions here #if result < isGeneric: result = isNone + if result notin {isNone, isGeneric}: + result = typeRangeRel(f, a) elif skipTypes(f, {tyRange}).kind == a.kind: result = isIntConv elif isConvertibleToRange(skipTypes(f, {tyRange}), a): @@ -655,7 +721,8 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c) of isSubrange: inc(m.subtypeMatches) - result = copyTree(arg) + #result = copyTree(arg) + result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) of isGeneric: inc(m.genericMatches) if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}: @@ -669,7 +736,7 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, result = copyTree(arg) result.typ = getInstantiatedType(c, arg, m, f) # BUG: f may not be the right key! - if skipTypes(result.typ, abstractVar).kind in {tyTuple}: + if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}: result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) # BUGFIX: use ``result.typ`` and not `f` here of isFromIntLit: @@ -680,7 +747,7 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, of isEqual: inc(m.exactMatches) result = copyTree(arg) - if skipTypes(f, abstractVar).kind in {tyTuple}: + if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}: result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) of isNone: # do not do this in ``typeRel`` as it then can't infere T in ``ref T``: @@ -756,6 +823,28 @@ proc setSon(father: PNode, at: int, son: PNode) = if sonsLen(father) <= at: setlen(father.sons, at + 1) father.sons[at] = son +# we are allowed to modify the calling node in the 'prepare*' procs: +proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode = + if formal.kind == tyExpr and formal.len != 1: + # {tyTypeDesc, tyExpr, tyStmt, tyProxy}: + # a.typ == nil is valid + result = a + elif a.typ.isNil: + result = c.semOperand(c, a, {efDetermineType}) + else: + result = a + +proc prepareOperand(c: PContext; a: PNode): PNode = + if a.typ.isNil: + result = c.semOperand(c, a, {efDetermineType}) + else: + result = a + +proc prepareNamedParam(a: PNode) = + if a.sons[0].kind != nkIdent: + var info = a.sons[0].info + a.sons[0] = newIdentNode(considerAcc(a.sons[0]), info) + proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var TIntSet) = template checkConstraint(n: expr) {.immediate, dirty.} = @@ -780,6 +869,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, if n.sons[a].kind == nkExprEqExpr: # named param # check if m.callee has such a param: + prepareNamedParam(n.sons[a]) if n.sons[a].sons[0].kind != nkIdent: LocalError(n.sons[a].info, errNamedParamHasToBeIdent) m.state = csNoMatch @@ -795,9 +885,11 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.state = csNoMatch return m.baseTypeMatch = false + n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1]) + n.sons[a].typ = n.sons[a].sons[1].typ var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, n.sons[a].sons[1], nOrig.sons[a].sons[1]) - if arg == nil: + if arg == nil: m.state = csNoMatch return checkConstraint(n.sons[a].sons[1]) @@ -809,30 +901,33 @@ proc matchesAux(c: PContext, n, nOrig: PNode, if f != formalLen - 1: container = nil else: setSon(m.call, formal.position + 1, arg) - else: + else: # unnamed param - if f >= formalLen: + if f >= formalLen: # too many arguments? - if tfVarArgs in m.callee.flags: + if tfVarArgs in m.callee.flags: # is ok... but don't increment any counters... - if skipTypes(n.sons[a].typ, abstractVar).kind == tyString: - addSon(m.call, implicitConv(nkHiddenStdConv, getSysType(tyCString), + # we have no formal here to snoop at: + n.sons[a] = prepareOperand(c, n.sons[a]) + if skipTypes(n.sons[a].typ, abstractVar-{tyTypeDesc}).kind==tyString: + addSon(m.call, implicitConv(nkHiddenStdConv, getSysType(tyCString), copyTree(n.sons[a]), m, c)) - else: + else: addSon(m.call, copyTree(n.sons[a])) elif formal != nil: m.baseTypeMatch = false + n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, n.sons[a], nOrig.sons[a]) - if (arg != nil) and m.baseTypeMatch and (container != nil): + if (arg != nil) and m.baseTypeMatch and (container != nil): addSon(container, arg) - else: + else: m.state = csNoMatch - return - else: + return + else: m.state = csNoMatch - return - else: + return + else: if m.callee.n.sons[f].kind != nkSym: InternalError(n.sons[a].info, "matches") return @@ -843,6 +938,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.state = csNoMatch return m.baseTypeMatch = false + n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, n.sons[a], nOrig.sons[a]) if arg == nil: @@ -855,12 +951,18 @@ proc matchesAux(c: PContext, n, nOrig: PNode, setSon(m.call, formal.position + 1, implicitConv(nkHiddenStdConv, formal.typ, container, m, c)) if f != formalLen - 1: container = nil - else: + else: setSon(m.call, formal.position + 1, arg) checkConstraint(n.sons[a]) inc(a) inc(f) +proc semFinishOperands*(c: PContext, n: PNode) = + # this needs to be called to ensure that after overloading resolution every + # argument has been sem'checked: + for i in 1 .. float conversion? + # float -> int conversions. ugh. result = transformSons(c, n) - elif firstOrd(dest) <= firstOrd(source) and - lastOrd(source) <= lastOrd(dest): + elif firstOrd(n.typ) <= firstOrd(n.sons[1].typ) and + lastOrd(n.sons[1].typ) <= lastOrd(n.typ): # BUGFIX: simply leave n as it is; we need a nkConv node, # but no range check: result = transformSons(c, n) @@ -334,13 +334,14 @@ proc transformConv(c: PTransf, n: PNode): PTransNode = # generate a range check: if dest.kind == tyInt64 or source.kind == tyInt64: result = newTransNode(nkChckRange64, n, 3) - else: + else: result = newTransNode(nkChckRange, n, 3) dest = skipTypes(n.typ, abstractVar) result[0] = transform(c, n.sons[1]) result[1] = newIntTypeNode(nkIntLit, firstOrd(dest), source).PTransNode result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), source).PTransNode - of tyFloat..tyFloat128: + of tyFloat..tyFloat128: + # XXX int64 -> float conversion? if skipTypes(n.typ, abstractVar).kind == tyRange: result = newTransNode(nkChckRangeF, n, 3) dest = skipTypes(n.typ, abstractVar) diff --git a/compiler/trees.nim b/compiler/trees.nim old mode 100755 new mode 100644 index 08b89a76e..f371cb021 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -115,7 +115,7 @@ proc isDeepConstExpr*(n: PNode): bool = result = true of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv: result = isDeepConstExpr(n.sons[1]) - of nkCurly, nkBracket, nkPar, nkClosure: + of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure: for i in 0 .. 0: result = t.typeToString + else: result = typeToStr[t.kind].strip + proc constraintsToStr(t: PType): string = let sep = if tfAny in t.flags: " or " else: " and " result = "" - for i in countup(0, t.sons.len - 1): + for i in countup(0, t.len - 1): if i > 0: result.add(sep) - result.add(t.sons[i].typeToString) + result.add(t.sons[i].consToStr) proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = - const - typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty", - "Array Constructor [$1]", "nil", "expr", "stmt", "typeDesc", - "GenericInvokation", "GenericBody", "GenericInst", "GenericParam", - "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", - "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", - "pointer", "OpenArray[$1]", "string", "CString", "Forward", - "int", "int8", "int16", "int32", "int64", - "float", "float32", "float64", "float128", - "uint", "uint8", "uint16", "uint32", "uint64", - "bignum", "const ", - "!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass"] var t = typ result = "" if t == nil: return @@ -433,12 +439,15 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = add(result, typeToString(t.sons[i])) add(result, ']') of tyTypeDesc: - if t.sons == nil or t.sons.len == 0: result = "typedesc" + if t.len == 0: result = "typedesc" else: result = "typedesc[" & constraintsToStr(t) & "]" of tyTypeClass: - result = constraintsToStr(t) + case t.len + of 0: result = "typeclass[]" + of 1: result = "typeclass[" & consToStr(t.sons[0]) & "]" + else: result = constraintsToStr(t) of tyExpr: - if t.sons.len == 0: result = "expr" + if t.len == 0: result = "expr" else: result = "expr[" & constraintsToStr(t) & "]" of tyArray: if t.sons[0].kind == tyRange: @@ -476,8 +485,8 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = add(result, ']') of tyPtr, tyRef, tyVar, tyMutable, tyConst: result = typeToStr[t.kind] & typeToString(t.sons[0]) - of tyRange: - result = "range " & rangeToStr(t.n) + of tyRange: + result = "range " & rangeToStr(t.n) & "(" & typeToString(t.sons[0]) & ")" of tyProc: result = if tfIterator in t.flags: "iterator (" else: "proc (" for i in countup(1, sonsLen(t) - 1): @@ -534,7 +543,7 @@ proc firstOrd(t: PType): biggestInt = else: assert(t.n.sons[0].kind == nkSym) result = t.n.sons[0].sym.position - of tyGenericInst, tyDistinct, tyConst, tyMutable: + of tyGenericInst, tyDistinct, tyConst, tyMutable, tyTypeDesc: result = firstOrd(lastSon(t)) else: InternalError("invalid kind for first(" & $t.kind & ')') @@ -567,7 +576,7 @@ proc lastOrd(t: PType): biggestInt = of tyEnum: assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym) result = t.n.sons[sonsLen(t.n) - 1].sym.position - of tyGenericInst, tyDistinct, tyConst, tyMutable: + of tyGenericInst, tyDistinct, tyConst, tyMutable, tyTypeDesc: result = lastOrd(lastSon(t)) of tyProxy: result = 0 else: @@ -833,7 +842,8 @@ proc SameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = of tyGenericInst: result = sameTypeAux(lastSon(a), lastSon(b), c) of tyTypeDesc: - if TypeDescExactMatch in c.flags: + if c.cmp == dcEqIgnoreDistinct: result = false + elif TypeDescExactMatch in c.flags: CycleCheck() result = sameChildrenAux(x, y, c) and sameFlags(a, b) else: @@ -938,7 +948,8 @@ proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool = of tyTypeClass: match = matchTypeClass(bindings, req, t) else: nil - elif t.kind in {tyObject}: + elif t.kind in {tyObject} and req.len != 0: + # empty 'object' is fine as constraint in a type class match = sameType(t, req) if tfAny in typeClass.flags: @@ -962,11 +973,11 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool = result = true if typ == nil: return if ContainsOrIncl(marker, typ.id): return - var t = skipTypes(typ, abstractInst) + var t = skipTypes(typ, abstractInst-{tyTypeDesc}) case t.kind of tyVar: if kind == skConst: return false - var t2 = skipTypes(t.sons[0], abstractInst) + var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc}) case t2.kind of tyVar: result = false # ``var var`` is always an invalid type: @@ -980,8 +991,9 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool = if not result: break if result and t.sons[0] != nil: result = typeAllowedAux(marker, t.sons[0], skResult) - of tyExpr, tyStmt, tyTypeDesc: + of tyExpr, tyStmt, tyTypeDesc: result = true + # XXX er ... no? these should not be allowed! of tyGenericBody, tyGenericParam, tyForward, tyNone, tyGenericInvokation, tyTypeClass: result = false @@ -994,7 +1006,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool = of tyGenericInst, tyDistinct: result = typeAllowedAux(marker, lastSon(t), kind) of tyRange: - result = skipTypes(t.sons[0], abstractInst).kind in + result = skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind in {tyChar, tyEnum, tyInt..tyFloat128} of tyOpenArray, tyVarargs: result = (kind == skParam) and typeAllowedAux(marker, t.sons[0], skVar) @@ -1162,6 +1174,8 @@ proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt = result = align(result, a) of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter: result = computeSizeAux(lastSon(typ), a) + of tyTypeDesc: + result = (if typ.len == 1: computeSizeAux(typ.sons[0], a) else: -1) of tyProxy: result = 1 else: #internalError("computeSizeAux()") diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim old mode 100755 new mode 100644 diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg old mode 100755 new mode 100644 diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg old mode 100755 new mode 100644 diff --git a/config/nimrod.cfg b/config/nimrod.cfg old mode 100755 new mode 100644 index 9db13e836..d9075a20f --- a/config/nimrod.cfg +++ b/config/nimrod.cfg @@ -1,5 +1,5 @@ # Configuration file for the Nimrod Compiler. -# (c) 2012 Andreas Rumpf +# (c) 2013 Andreas Rumpf # Feel free to edit the default values as you need. @@ -29,7 +29,7 @@ path="$lib/wrappers/x11" path="$lib/wrappers/zip" path="$lib/windows" path="$lib/posix" -path="$lib/ecmas" +path="$lib/js" path="$lib/pure/unidecode" @if nimbabel: @@ -55,7 +55,6 @@ path="$lib/pure/unidecode" @end # additional options always passed to the compiler: ---verbosity: "1" --parallel_build: "0" # 0 to auto-detect number of processors hint[LineTooLong]=off @@ -91,6 +90,7 @@ hint[LineTooLong]=off @end @if macosx: + cc = clang tlsEmulation:on gcc.options.always = "-w -fasm-blocks" gpp.options.always = "-w -fasm-blocks" diff --git a/contributors.txt b/contributors.txt old mode 100755 new mode 100644 diff --git a/copying.txt b/copying.txt old mode 100755 new mode 100644 diff --git a/deinstall.sh b/deinstall.sh deleted file mode 100755 index 8b21d8adb..000000000 --- a/deinstall.sh +++ /dev/null @@ -1,59 +0,0 @@ -#! /bin/sh -# Generated by niminst - -if [ $# -eq 1 ] ; then - case $1 in - "--help"|"-h"|"help"|"h") - echo "Nimrod deinstallation script" - echo "Usage: [sudo] sh deinstall.sh DIR" - echo "Where DIR may be:" - echo " /usr/bin" - echo " /usr/local/bin" - echo " /opt" - echo " (treated like '/opt')" - exit 1 - ;; - "/usr/bin") - bindir=/usr/bin - configdir=/etc - libdir=/usr/lib/nimrod - docdir=/usr/share/nimrod/doc - datadir=/usr/share/nimrod/data - ;; - "/usr/local/bin") - bindir=/usr/local/bin - configdir=/etc - libdir=/usr/local/lib/nimrod - docdir=/usr/local/share/nimrod/doc - datadir=/usr/local/share/nimrod/data - ;; - *) - bindir="$1/nimrod/bin" - configdir="$1/nimrod/config" - libdir="$1/nimrod/lib" - docdir="$1/nimrod/doc" - datadir="$1/nimrod/data" - ;; - esac - echo "removing files..." - - rm -f $bindir/nimrod - rm -f $configdir/nimrod.cfg - rm -f $configdir/nimdoc.cfg - rm -f $configdir/nimdoc.tex.cfg - rm -rf $docdir - rm -rf $datadir - rm -rf $libdir - - echo "deinstallation successful" -else - echo "Nimrod deinstallation script" - echo "Usage: [sudo] sh deinstall.sh DIR" - echo "Where DIR may be:" - echo " /usr/bin" - echo " /usr/local/bin" - echo " /opt" - echo " (treated like '/opt')" - exit 1 -fi - diff --git a/doc/abstypes.txt b/doc/abstypes.txt old mode 100755 new mode 100644 diff --git a/doc/advopt.txt b/doc/advopt.txt old mode 100755 new mode 100644 index b8088bbcd..38461244d --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -70,8 +70,13 @@ Advanced options: --putenv:key=value set an environment variable --babelPath:PATH add a path for Babel support --excludePath:PATH exclude a path from the list of search paths + --dynlibOverride:SYMBOL marks SYMBOL so that dynlib:SYMBOL + has no effect and can be statically linked instead; + symbol matching is fuzzy so + that --dynlibOverride:lua matches + dynlib: "liblua.so.3" --listCmd list the commands used to execute external programs --parallelBuild=0|1|... perform a parallel build value = number of processors (0 for auto-detect) - --verbosity:0|1|2|3 set Nimrod's verbosity level (0 is default) + --verbosity:0|1|2|3 set Nimrod's verbosity level (1 is default) -v, --version show detailed version information diff --git a/doc/apis.txt b/doc/apis.txt old mode 100755 new mode 100644 diff --git a/doc/astspec.txt b/doc/astspec.txt old mode 100755 new mode 100644 diff --git a/doc/basicopt.txt b/doc/basicopt.txt old mode 100755 new mode 100644 diff --git a/doc/c2nim.txt b/doc/c2nim.txt old mode 100755 new mode 100644 diff --git a/doc/docs.txt b/doc/docs.txt old mode 100755 new mode 100644 diff --git a/doc/effects.txt b/doc/effects.txt old mode 100755 new mode 100644 diff --git a/doc/endb.txt b/doc/endb.txt old mode 100755 new mode 100644 index 900553f0e..d4437e5d9 --- a/doc/endb.txt +++ b/doc/endb.txt @@ -61,8 +61,8 @@ Executing Commands Breakpoint Commands =================== -``b``, ``setbreak`` [fromline [toline]] [file] - Set a new breakpoint named 'identifier' for the given file +``b``, ``setbreak`` [fromline [toline]] [file] + Set a new breakpoint for the given file and line numbers. If no file is given, the current execution point's filename is used. If the filename has no extension, ``.nim`` is appended for your convenience. @@ -71,16 +71,16 @@ Breakpoint Commands breakpoint contains a line number range. Some examples if it is still unclear: - * ``b br1 12 15 thallo`` creates a breakpoint named ``br1`` that + * ``b 12 15 thallo`` creates a breakpoint that will be triggered if the instruction pointer reaches one of the lines 12-15 in the file ``thallo.nim``. - * ``b br1 12 thallo`` creates a breakpoint named ``br1`` that + * ``b 12 thallo`` creates a breakpoint that will be triggered if the instruction pointer reaches the line 12 in the file ``thallo.nim``. - * ``b br1 12`` creates a breakpoint named ``br1`` that + * ``b 12`` creates a breakpoint that will be triggered if the instruction pointer reaches the line 12 in the current file. - * ``b br1`` creates a breakpoint named ``br1`` that + * ``b`` creates a breakpoint that will be triggered if the instruction pointer reaches the current line in the current file again. diff --git a/doc/filelist.txt b/doc/filelist.txt old mode 100755 new mode 100644 diff --git a/doc/filters.txt b/doc/filters.txt old mode 100755 new mode 100644 diff --git a/doc/gc.txt b/doc/gc.txt index dfcfa07f3..854f9ce2a 100644 --- a/doc/gc.txt +++ b/doc/gc.txt @@ -22,14 +22,25 @@ delta-subgraph of the heap that changed since its last run. The GC is only triggered in a memory allocation operation. It it not triggered by some timer and does not run in a background thread. +To force a full collection call ``GC_fullCollect``. Note that it is generally +better to let the GC do its work and not enforce a full collection. + + +Cycle collector +=============== + The cycle collector can be en-/disabled independently from the other parts of the GC with ``GC_enableMarkAndSweep`` and ``GC_disableMarkAndSweep``. The compiler analyses the types for their possibility to build cycles, but often it is necessary to help this analysis with the ``acyclic`` pragma (see -`acyclic `_ for further information). +`acyclic `_ for further information). -To force a full collection call ``GC_fullCollect``. Note that it is generally -better to let the GC do its work and not enforce a full collection. +You can also use the ``acyclic`` pragma for data that is cyclic in reality and +then break up the cycles explicitly with ``GC_addCycleRoot``. This can be a +very valuable optimization; the Nimrod compiler itself relies on this +optimization trick to improve performance. Note that ``GC_addCycleRoot`` is +a quick operation; the root is only registered for the next run of the +cycle collector. Realtime support diff --git a/doc/grammar.txt b/doc/grammar.txt old mode 100755 new mode 100644 diff --git a/doc/intern.txt b/doc/intern.txt old mode 100755 new mode 100644 diff --git a/doc/keywords.txt b/doc/keywords.txt old mode 100755 new mode 100644 diff --git a/doc/lib.txt b/doc/lib.txt old mode 100755 new mode 100644 index a429a8289..2f781f375 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -16,7 +16,10 @@ Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary while impure libraries do. A wrapper is an impure library that is a very low-level interface to a C library. -Read this `document `_ for a quick overview of the API design. +Read this `document `_ for a quick overview of the API design. If +you can't find here some functionality you are looking for you could try using +the 3rd party `package manager Babel `_ +and its list of packages. Pure libraries @@ -328,7 +331,7 @@ Modules for JS backend --------------------------- * `dom `_ - Declaration of the Document Object Model for the ECMAScript backend. + Declaration of the Document Object Model for the JS backend. Impure libraries diff --git a/doc/manual.txt b/doc/manual.txt old mode 100755 new mode 100644 index ad43d0379..8ca7b697e --- a/doc/manual.txt +++ b/doc/manual.txt @@ -984,7 +984,7 @@ in future versions of the compiler. .. code-block:: nimrod type - TPerson = tuple[name: string, age: int] # type representing a person + TPerson = tuple[name: string, age: int] # type representing a person: # a person consists of a name # and an age var @@ -1000,7 +1000,6 @@ For consistency with ``object`` declarations, tuples in a ``type`` section can also be defined with indentation instead of ``[]``: .. code-block:: nimrod - type TPerson = tuple # type representing a person name: string # a person consists of a name @@ -1011,7 +1010,6 @@ and information hiding. Objects have access to their type at runtime, so that the ``of`` operator can be used to determine the object's type. .. code-block:: nimrod - type TPerson {.inheritable.} = object name*: string # the * means that `name` is accessible from other modules @@ -1032,6 +1030,19 @@ and thus have no hidden type field. One can use the ``inheritable`` pragma to introduce new object roots apart from ``system.TObject``. +Object construction +------------------- + +Objects can also be created with an `object construction expression`:idx: that +has the syntax ``T(fieldA: valueA, fieldB: valueB, ...)`` where ``T`` is +an ``object`` type or a ``ref object`` type: + +.. code-block:: nimrod + var student = TStudent(name: "Anton", age: 5, id: 3) + +For a ``ref object`` type ``new`` is invoked implicitly. + + Object variants --------------- Often an object hierarchy is overkill in certain situations where simple @@ -1061,15 +1072,22 @@ An example: of nkIf: condition, thenPart, elsePart: PNode - var - n: PNode - new(n) # creates a new node - n.kind = nkFloat - n.floatVal = 0.0 # valid, because ``n.kind==nkFloat``, so that it fits + # create a new case object: + var n = PNode(kind: nkIf, condition: nil) + # accessing n.thenPart is valid because the ``nkIf`` branch is active: + n.thenPart = PNode(kind: nkFloat, floatVal: 2.0) # the following statement raises an `EInvalidField` exception, because - # n.kind's value does not fit: + # n.kind's value does not fit and the ``nkString`` branch is not active: n.strVal = "" + + # invalid: would change the active object branch: + n.kind = nkInt + + var x = PNode(kind: nkAdd, leftOp: PNode(kind: nkInt, intVal: 4), + rightOp: PNode(kind: nkInt, intVal: 2)) + # valid: does not change the active object branch: + x.kind = nkSub As can been seen from the example, an advantage to an object hierarchy is that no casting between different object types is needed. Yet, access to invalid @@ -1078,6 +1096,11 @@ object fields raises an exception. The syntax of ``case`` in an object declaration follows closely the syntax of the ``case`` statement: The branches in a ``case`` section may be indented too. +In the example the ``kind`` field is called the `discriminator`:idx:\: For +safety its address cannot be taken and assignments to it are restricted: The +new value must not lead to a change of the active object branch. For an object +branch switch ``system.reset`` has to be used. + Set type -------- @@ -2387,6 +2410,7 @@ the proc's name. Procs as expressions can appear both as nested procs and inside top level executable code. + Do notation ----------- @@ -2410,7 +2434,7 @@ Again, let's see the equivalent of the previous example: sort(cities) do (x,y: string) -> int: cmp(x.len, y.len) -Finally, more than one ``do`` blocks can appear in a single call: +Finally, more than one ``do`` block can appear in a single call: .. code-block:: nimrod proc performWithUndo(task: proc(), undo: proc()) = ... @@ -2426,6 +2450,7 @@ omitted if the supplied proc doesn't have any parameters and return value. The compatibility works in the other direction too as the ``do`` syntax can be used with macros and templates expecting ``stmt`` blocks. + Nonoverloadable builtins ------------------------ @@ -2987,8 +3012,7 @@ possibly raised exceptions; the algorithm operates on ``p``'s call graph: raise ``system.E_Base`` unless ``q`` has an explicit ``raises`` list. 3. Every call to a method ``m`` is assumed to raise ``system.E_Base`` unless ``m`` has an explicit ``raises`` list. -4. For every other call the analysis can determine an - exact ``raises`` list. +4. For every other call the analysis can determine an exact ``raises`` list. 5. For determining a ``raises`` list, the ``raise`` and ``try`` statements of ``p`` are taken into consideration. @@ -3259,7 +3283,7 @@ Symbol lookup in generics The symbol binding rules in generics are slightly subtle: There are "open" and "closed" symbols. A "closed" symbol cannot be re-bound in the instantiation -context, an "open" symbol can be. Per default overloaded symbols are open +context, an "open" symbol can. Per default overloaded symbols are open and every other symbol is closed. Open symbols are looked up in two different contexts: Both the context @@ -4106,8 +4130,8 @@ implemented with term rewriting: proc p(x, y: int; cond: bool): int = result = if cond: x + y else: x - y - template optP{p(x, y, true)}(x, y: expr): expr = x + y - template optP{p(x, y, false)}(x, y: expr): expr = x - y + template optP1{p(x, y, true)}(x, y: expr): expr = x + y + template optP2{p(x, y, false)}(x, y: expr): expr = x - y Example: hoisting @@ -4386,6 +4410,40 @@ generate a destructor for the record type. Nimrod will automatically insert calls to any base class destructors in both user-defined and generated destructors. +A destructor is attached to the type it destructs; expressions of this type +can then only be used in *destructible contexts*: + +.. code-block:: nimrod + type + TMyObj = object + x, y: int + p: pointer + + proc destruct(o: var TMyObj) {.destructor.} = + if o.p != nil: dealloc o.p + + proc open: TMyObj = + result = TMyObj(x: 1, y: 2, p: alloc(3)) + + proc main() = + # destructor automatically invoked at the end of the scope: + var x = open() + + # Error: usage of a type with a destructor in a non destructible context + echo open() + +A destructible context is currently only the following: + +1. The ``expr`` in ``var x = expr``. +2. The ``expr`` in ``let x = expr``. +3. The ``expr`` in ``return expr``. +4. The ``expr`` in ``result = expr`` where ``result`` is the special symbol + introduced by the compiler. + +These rules ensure that the construction is tied to a variable and can easily +be destructed at its scope exit. Later versions of the language will improve +the support of destructors. + procvar pragma -------------- @@ -4720,7 +4778,7 @@ Foreign function interface ========================== Nimrod's `FFI`:idx: (foreign function interface) is extensive and only the -parts that scale to other future backends (like the LLVM/EcmaScript backends) +parts that scale to other future backends (like the LLVM/JavaScript backends) are documented here. diff --git a/doc/mytest.cfg b/doc/mytest.cfg old mode 100755 new mode 100644 diff --git a/doc/nimdoc.css b/doc/nimdoc.css old mode 100755 new mode 100644 diff --git a/doc/niminst.txt b/doc/niminst.txt old mode 100755 new mode 100644 diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt old mode 100755 new mode 100644 index 06a0f4500..179de3b04 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -68,6 +68,25 @@ ShadowIdent A local variable shadows another local User Some user defined warning. ========================== ============================================ + +Verbosity levels +---------------- + +===== ============================================ +Level Description +===== ============================================ +0 Minimal output level for the compiler. +1 Displays compilation of all the compiled files, including those imported + by other modules or through the `compile pragma<#compile-pragma>`_. + This is the default level. +2 Displays compilation statistics, enumerates the dynamic + libraries that will be loaded by the final binary and dumps to + standard output the result of applying `a filter to the source code + `_ if any filter was used during compilation. +3 In addition to the previous levels dumps a debug stack trace + for compiler developers. +===== ============================================ + Configuration files ------------------- @@ -190,7 +209,7 @@ Define Effect ``useRealtimeGC`` Enables support of Nimrod's GC for *soft* realtime systems. See the documentation of the `gc `_ for further information. -``nodejs`` The EcmaScript target is actually ``node.js``. +``nodejs`` The JS target is actually ``node.js``. ``ssl`` Enables OpenSSL support for the sockets module. ``memProfiler`` Enables memory profiling for the native GC. ================== ========================================================= @@ -549,17 +568,17 @@ efficient: else: quit(errorStr(p, "unknown variable: " & k.key)) -The ECMAScript target +The JavaScript target ===================== -Nimrod can also generate `ECMAScript`:idx: (also known as `JavaScript`:idx:) -code. However, the ECMAScript code generator is experimental! +Nimrod can also generate `JavaScript`:idx: code. However, the +JavaScript code generator is experimental! -Nimrod targets ECMAScript 1.5 which is supported by any widely used browser. -Since ECMAScript does not have a portable means to include another module, +Nimrod targets JavaScript 1.5 which is supported by any widely used browser. +Since JavaScript does not have a portable means to include another module, Nimrod just generates a long ``.js`` file. -Features or modules that the ECMAScript platform does not support are not +Features or modules that the JavaScript platform does not support are not available. This includes: * manual memory management (``alloc``, etc.) @@ -571,7 +590,7 @@ available. This includes: However, the modules `strutils`:idx:, `math`:idx:, and `times`:idx: are available! To access the DOM, use the `dom`:idx: module that is only -available for the ECMAScript platform. +available for the JavaScript platform. To compile a Nimrod module into a ``.js`` file use the ``js`` command; the default is a ``.js`` file that is supposed to be referenced in an ``.html`` diff --git a/doc/overview.txt b/doc/overview.txt old mode 100755 new mode 100644 diff --git a/doc/pegdocs.txt b/doc/pegdocs.txt old mode 100755 new mode 100644 diff --git a/doc/readme.txt b/doc/readme.txt old mode 100755 new mode 100644 diff --git a/doc/regexprs.txt b/doc/regexprs.txt old mode 100755 new mode 100644 diff --git a/doc/rst.txt b/doc/rst.txt old mode 100755 new mode 100644 diff --git a/doc/tools.txt b/doc/tools.txt old mode 100755 new mode 100644 diff --git a/doc/tut1.txt b/doc/tut1.txt old mode 100755 new mode 100644 index 746d11f01..7c2a35f94 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -1311,6 +1311,24 @@ integer. echo(person[0]) # "Peter" echo(person[1]) # 30 + # You don't need to declare tuples in a separate type section. + var building: tuple[street: string, number: int] + building = ("Rue del Percebe", 13) + echo(building.street) + + # The following line does not compile, they are different tuples! + #person = building + # --> Error: type mismatch: got (tuple[street: string, number: int]) + # but expected 'TPerson' + + # The following works because the field names and types are the same. + var teacher: tuple[name: string, age: int] = ("Mark", 42) + person = teacher + +Even though you don't need to declare a type for a tuple to use it, tuples +created with different field names will be considered different objects despite +having the same field types. + Reference and pointer types --------------------------- diff --git a/doc/tut2.txt b/doc/tut2.txt old mode 100755 new mode 100644 index c080d1339..e1e36bfc4 --- a/doc/tut2.txt +++ b/doc/tut2.txt @@ -46,8 +46,7 @@ Objects Like tuples, objects are a means to pack different values together in a structured way. However, objects provide many features that tuples do not: They provide inheritance and information hiding. Because objects encapsulate -data, the ``()`` tuple constructor cannot be used to construct objects. So -the order of the object's fields is not as important as it is for tuples. The +data, the ``T()`` object constructor should only be used internally and the programmer should provide a proc to initialize the object (this is called a *constructor*). @@ -55,7 +54,6 @@ Objects have access to their type at runtime. There is an ``of`` operator that can be used to check the object's type: .. code-block:: nimrod - type TPerson = object of TObject name*: string # the * means that `name` is accessible from other modules @@ -68,6 +66,8 @@ Objects have access to their type at runtime. There is an student: TStudent person: TPerson assert(student of TStudent) # is true + # object construction: + student = TStudent(name: "Anton", age: 5, id: 2) Object fields that should be visible from outside the defining module have to be marked by ``*``. In contrast to tuples, different object types are @@ -161,12 +161,7 @@ An example: of nkIf: condition, thenPart, elsePart: PNode - var - n: PNode - new(n) # creates a new node - n.kind = nkFloat - n.floatVal = 0.0 # valid, because ``n.kind==nkFloat`` - + var n = PNode(kind: nkFloat, floatVal: 1.0) # the following statement raises an `EInvalidField` exception, because # n.kind's value does not fit: n.strVal = "" @@ -288,30 +283,22 @@ Procedures always use static dispatch. For dynamic dispatch replace the .. code-block:: nimrod type - TExpr = object of TObject ## abstract base class for an expression - TLiteral = object of TExpr + PExpr = ref object of TObject ## abstract base class for an expression + PLiteral = ref object of PExpr x: int - TPlusExpr = object of TExpr - a, b: ref TExpr - - method eval(e: ref TExpr): int = + PPlusExpr = ref object of PExpr + a, b: PExpr + + # watch out: 'eval' relies on dynamic binding + method eval(e: PExpr): int = # override this base method quit "to override!" - method eval(e: ref TLiteral): int = return e.x - - method eval(e: ref TPlusExpr): int = - # watch out: relies on dynamic binding - return eval(e.a) + eval(e.b) + method eval(e: PLiteral): int = e.x + method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b) - proc newLit(x: int): ref TLiteral = - new(result) - result.x = x - - proc newPlus(a, b: ref TExpr): ref TPlusExpr = - new(result) - result.a = a - result.b = b + proc newLit(x: int): PLiteral = PLiteral(x: x) + proc newPlus(a, b: PExpr): PPlusExpr = PPlusExpr(a: a, b: b) echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) diff --git a/examples/0mq/client.nim b/examples/0mq/client.nim old mode 100755 new mode 100644 diff --git a/examples/0mq/server.nim b/examples/0mq/server.nim old mode 100755 new mode 100644 diff --git a/examples/allany.nim b/examples/allany.nim old mode 100755 new mode 100644 diff --git a/examples/cairoex.nim b/examples/cairoex.nim old mode 100755 new mode 100644 diff --git a/examples/cgiex.nim b/examples/cgiex.nim old mode 100755 new mode 100644 diff --git a/examples/cross_calculator/lazarus/nimlaz.lpi b/examples/cross_calculator/lazarus/nimlaz.lpi old mode 100755 new mode 100644 diff --git a/examples/cross_calculator/lazarus/nimlaz.lpr b/examples/cross_calculator/lazarus/nimlaz.lpr old mode 100755 new mode 100644 diff --git a/examples/cross_calculator/lazarus/nimlaz.lrs b/examples/cross_calculator/lazarus/nimlaz.lrs old mode 100755 new mode 100644 diff --git a/examples/cross_calculator/lazarus/readme.txt b/examples/cross_calculator/lazarus/readme.txt old mode 100755 new mode 100644 diff --git a/examples/cross_calculator/lazarus/unit1.lfm b/examples/cross_calculator/lazarus/unit1.lfm old mode 100755 new mode 100644 diff --git a/examples/cross_calculator/lazarus/unit1.pas b/examples/cross_calculator/lazarus/unit1.pas old mode 100755 new mode 100644 diff --git a/examples/cross_calculator/nimrod_backend/backend.nim b/examples/cross_calculator/nimrod_backend/backend.nim old mode 100755 new mode 100644 diff --git a/examples/curlex.nim b/examples/curlex.nim old mode 100755 new mode 100644 diff --git a/examples/debugging.nim b/examples/debugging.nim new file mode 100644 index 000000000..89cdd3b2a --- /dev/null +++ b/examples/debugging.nim @@ -0,0 +1,17 @@ +# Simple program to test the debugger +# compile with --debugger:on + +proc someComp(x, y: int): int = + let a = x+y + if a > 7: + let b = a*90 + {.breakpoint.} + result = b + {.breakpoint.} + +proc pp() = + var aa = 45 + var bb = "abcdef" + echo someComp(23, 45) + +pp() diff --git a/examples/filterex.nim b/examples/filterex.nim old mode 100755 new mode 100644 diff --git a/examples/fizzbuzz.nim b/examples/fizzbuzz.nim old mode 100755 new mode 100644 diff --git a/examples/gtk/ex1.nim b/examples/gtk/ex1.nim old mode 100755 new mode 100644 diff --git a/examples/gtk/ex2.nim b/examples/gtk/ex2.nim old mode 100755 new mode 100644 diff --git a/examples/gtk/ex3.nim b/examples/gtk/ex3.nim old mode 100755 new mode 100644 diff --git a/examples/gtk/ex4.nim b/examples/gtk/ex4.nim old mode 100755 new mode 100644 diff --git a/examples/gtk/ex5.nim b/examples/gtk/ex5.nim old mode 100755 new mode 100644 diff --git a/examples/gtk/ex6.nim b/examples/gtk/ex6.nim old mode 100755 new mode 100644 diff --git a/examples/gtk/ex7.nim b/examples/gtk/ex7.nim old mode 100755 new mode 100644 diff --git a/examples/gtk/ex8.nim b/examples/gtk/ex8.nim old mode 100755 new mode 100644 diff --git a/examples/gtk/ex9.nim b/examples/gtk/ex9.nim old mode 100755 new mode 100644 diff --git a/examples/hallo.nim b/examples/hallo.nim old mode 100755 new mode 100644 diff --git a/examples/htmlrefs.nim b/examples/htmlrefs.nim old mode 100755 new mode 100644 diff --git a/examples/htmltitle.nim b/examples/htmltitle.nim old mode 100755 new mode 100644 diff --git a/examples/httpserver2.nim b/examples/httpserver2.nim old mode 100755 new mode 100644 diff --git a/examples/iupex1.nim b/examples/iupex1.nim old mode 100755 new mode 100644 diff --git a/examples/keyval.nim b/examples/keyval.nim old mode 100755 new mode 100644 diff --git a/examples/keyval2.nim b/examples/keyval2.nim old mode 100755 new mode 100644 diff --git a/examples/luaex.nim b/examples/luaex.nim old mode 100755 new mode 100644 diff --git a/examples/maximum.nim b/examples/maximum.nim old mode 100755 new mode 100644 diff --git a/examples/myfile.txt b/examples/myfile.txt old mode 100755 new mode 100644 diff --git a/examples/parsecfgex.nim b/examples/parsecfgex.nim old mode 100755 new mode 100644 diff --git a/examples/pythonex.nim b/examples/pythonex.nim old mode 100755 new mode 100644 diff --git a/examples/readme.txt b/examples/readme.txt old mode 100755 new mode 100644 diff --git a/examples/sdlex.nim b/examples/sdlex.nim old mode 100755 new mode 100644 diff --git a/examples/statcsv.nim b/examples/statcsv.nim old mode 100755 new mode 100644 diff --git a/examples/tclex.nim b/examples/tclex.nim old mode 100755 new mode 100644 diff --git a/examples/transff.nim b/examples/transff.nim old mode 100755 new mode 100644 diff --git a/examples/wingui.nim b/examples/wingui.nim old mode 100755 new mode 100644 diff --git a/examples/x11ex.nim b/examples/x11ex.nim old mode 100755 new mode 100644 diff --git a/gpl.html b/gpl.html old mode 100755 new mode 100644 diff --git a/icons/koch.ico b/icons/koch.ico old mode 100755 new mode 100644 diff --git a/icons/koch.rc b/icons/koch.rc old mode 100755 new mode 100644 diff --git a/icons/koch.res b/icons/koch.res old mode 100755 new mode 100644 diff --git a/icons/nimrod.ico b/icons/nimrod.ico old mode 100755 new mode 100644 diff --git a/icons/nimrod.rc b/icons/nimrod.rc old mode 100755 new mode 100644 diff --git a/icons/nimrod.res b/icons/nimrod.res old mode 100755 new mode 100644 diff --git a/install.sh b/install.sh deleted file mode 100755 index 24e95730d..000000000 --- a/install.sh +++ /dev/null @@ -1,1120 +0,0 @@ -#! /bin/sh -# Generated by niminst - -set -e - -if [ $# -eq 1 ] ; then - if test -f bin/nimrod - then - echo "Nimrod build detected" - else - echo "Please build Nimrod before installing it" - exit 1 - fi - case $1 in - "--help"|"-h"|"help"|"h") - echo "Nimrod installation script" - echo "Usage: [sudo] sh install.sh DIR" - echo "Where DIR may be:" - echo " /usr/bin" - echo " /usr/local/bin" - echo " /opt" - echo " (treated like '/opt')" - echo "To deinstall, use the command:" - echo "sh deinstall.sh DIR" - exit 1 - ;; - "/usr/bin") - bindir=/usr/bin - configdir=/etc - libdir=/usr/lib/nimrod - docdir=/usr/share/nimrod/doc - datadir=/usr/share/nimrod/data - ;; - "/usr/local/bin") - bindir=/usr/local/bin - configdir=/etc - libdir=/usr/local/lib/nimrod - docdir=/usr/local/share/nimrod/doc - datadir=/usr/local/share/nimrod/data - ;; - *) - bindir="$1/nimrod/bin" - configdir="$1/nimrod/config" - libdir="$1/nimrod/lib" - docdir="$1/nimrod/doc" - datadir="$1/nimrod/data" - - mkdir -p $1/nimrod - mkdir -p $bindir - mkdir -p $configdir - ;; - esac - mkdir -p $libdir - mkdir -p $docdir - echo "copying files..." - mkdir -p $libdir/system - mkdir -p $libdir/core - mkdir -p $libdir/pure - mkdir -p $libdir/pure/collections - mkdir -p $libdir/impure - mkdir -p $libdir/wrappers - mkdir -p $libdir/wrappers/cairo - mkdir -p $libdir/wrappers/gtk - mkdir -p $libdir/wrappers/lua - mkdir -p $libdir/wrappers/opengl - mkdir -p $libdir/wrappers/readline - mkdir -p $libdir/wrappers/sdl - mkdir -p $libdir/wrappers/x11 - mkdir -p $libdir/wrappers/zip - mkdir -p $libdir/windows - mkdir -p $libdir/posix - mkdir -p $libdir/ecmas - - cp bin/nimrod $bindir/nimrod - chmod 755 $bindir/nimrod - cp config/nimrod.cfg $configdir/nimrod.cfg - chmod 644 $configdir/nimrod.cfg - cp config/nimdoc.cfg $configdir/nimdoc.cfg - chmod 644 $configdir/nimdoc.cfg - cp config/nimdoc.tex.cfg $configdir/nimdoc.tex.cfg - chmod 644 $configdir/nimdoc.tex.cfg - if [ -f doc/abstypes.txt ]; then - cp doc/abstypes.txt $docdir/abstypes.txt - chmod 644 $docdir/abstypes.txt - fi - if [ -f doc/advopt.txt ]; then - cp doc/advopt.txt $docdir/advopt.txt - chmod 644 $docdir/advopt.txt - fi - if [ -f doc/apis.txt ]; then - cp doc/apis.txt $docdir/apis.txt - chmod 644 $docdir/apis.txt - fi - if [ -f doc/astspec.txt ]; then - cp doc/astspec.txt $docdir/astspec.txt - chmod 644 $docdir/astspec.txt - fi - if [ -f doc/basicopt.txt ]; then - cp doc/basicopt.txt $docdir/basicopt.txt - chmod 644 $docdir/basicopt.txt - fi - if [ -f doc/c2nim.txt ]; then - cp doc/c2nim.txt $docdir/c2nim.txt - chmod 644 $docdir/c2nim.txt - fi - if [ -f doc/docs.txt ]; then - cp doc/docs.txt $docdir/docs.txt - chmod 644 $docdir/docs.txt - fi - if [ -f doc/effects.txt ]; then - cp doc/effects.txt $docdir/effects.txt - chmod 644 $docdir/effects.txt - fi - if [ -f doc/endb.txt ]; then - cp doc/endb.txt $docdir/endb.txt - chmod 644 $docdir/endb.txt - fi - if [ -f doc/estp.txt ]; then - cp doc/estp.txt $docdir/estp.txt - chmod 644 $docdir/estp.txt - fi - if [ -f doc/filelist.txt ]; then - cp doc/filelist.txt $docdir/filelist.txt - chmod 644 $docdir/filelist.txt - fi - if [ -f doc/filters.txt ]; then - cp doc/filters.txt $docdir/filters.txt - chmod 644 $docdir/filters.txt - fi - if [ -f doc/gc.txt ]; then - cp doc/gc.txt $docdir/gc.txt - chmod 644 $docdir/gc.txt - fi - if [ -f doc/grammar.txt ]; then - cp doc/grammar.txt $docdir/grammar.txt - chmod 644 $docdir/grammar.txt - fi - if [ -f doc/intern.txt ]; then - cp doc/intern.txt $docdir/intern.txt - chmod 644 $docdir/intern.txt - fi - if [ -f doc/keywords.txt ]; then - cp doc/keywords.txt $docdir/keywords.txt - chmod 644 $docdir/keywords.txt - fi - if [ -f doc/lib.txt ]; then - cp doc/lib.txt $docdir/lib.txt - chmod 644 $docdir/lib.txt - fi - if [ -f doc/manual.txt ]; then - cp doc/manual.txt $docdir/manual.txt - chmod 644 $docdir/manual.txt - fi - if [ -f doc/nimgrep.txt ]; then - cp doc/nimgrep.txt $docdir/nimgrep.txt - chmod 644 $docdir/nimgrep.txt - fi - if [ -f doc/niminst.txt ]; then - cp doc/niminst.txt $docdir/niminst.txt - chmod 644 $docdir/niminst.txt - fi - if [ -f doc/nimrodc.txt ]; then - cp doc/nimrodc.txt $docdir/nimrodc.txt - chmod 644 $docdir/nimrodc.txt - fi - if [ -f doc/overview.txt ]; then - cp doc/overview.txt $docdir/overview.txt - chmod 644 $docdir/overview.txt - fi - if [ -f doc/pegdocs.txt ]; then - cp doc/pegdocs.txt $docdir/pegdocs.txt - chmod 644 $docdir/pegdocs.txt - fi - if [ -f doc/readme.txt ]; then - cp doc/readme.txt $docdir/readme.txt - chmod 644 $docdir/readme.txt - fi - if [ -f doc/regexprs.txt ]; then - cp doc/regexprs.txt $docdir/regexprs.txt - chmod 644 $docdir/regexprs.txt - fi - if [ -f doc/rst.txt ]; then - cp doc/rst.txt $docdir/rst.txt - chmod 644 $docdir/rst.txt - fi - if [ -f doc/subexes.txt ]; then - cp doc/subexes.txt $docdir/subexes.txt - chmod 644 $docdir/subexes.txt - fi - if [ -f doc/tools.txt ]; then - cp doc/tools.txt $docdir/tools.txt - chmod 644 $docdir/tools.txt - fi - if [ -f doc/trmacros.txt ]; then - cp doc/trmacros.txt $docdir/trmacros.txt - chmod 644 $docdir/trmacros.txt - fi - if [ -f doc/tut1.txt ]; then - cp doc/tut1.txt $docdir/tut1.txt - chmod 644 $docdir/tut1.txt - fi - if [ -f doc/tut2.txt ]; then - cp doc/tut2.txt $docdir/tut2.txt - chmod 644 $docdir/tut2.txt - fi - if [ -f doc/actors.html ]; then - cp doc/actors.html $docdir/actors.html - chmod 644 $docdir/actors.html - fi - if [ -f doc/algorithm.html ]; then - cp doc/algorithm.html $docdir/algorithm.html - chmod 644 $docdir/algorithm.html - fi - if [ -f doc/apis.html ]; then - cp doc/apis.html $docdir/apis.html - chmod 644 $docdir/apis.html - fi - if [ -f doc/asyncio.html ]; then - cp doc/asyncio.html $docdir/asyncio.html - chmod 644 $docdir/asyncio.html - fi - if [ -f doc/base64.html ]; then - cp doc/base64.html $docdir/base64.html - chmod 644 $docdir/base64.html - fi - if [ -f doc/browsers.html ]; then - cp doc/browsers.html $docdir/browsers.html - chmod 644 $docdir/browsers.html - fi - if [ -f doc/c2nim.html ]; then - cp doc/c2nim.html $docdir/c2nim.html - chmod 644 $docdir/c2nim.html - fi - if [ -f doc/cgi.html ]; then - cp doc/cgi.html $docdir/cgi.html - chmod 644 $docdir/cgi.html - fi - if [ -f doc/channels.html ]; then - cp doc/channels.html $docdir/channels.html - chmod 644 $docdir/channels.html - fi - if [ -f doc/colors.html ]; then - cp doc/colors.html $docdir/colors.html - chmod 644 $docdir/colors.html - fi - if [ -f doc/complex.html ]; then - cp doc/complex.html $docdir/complex.html - chmod 644 $docdir/complex.html - fi - if [ -f doc/cookies.html ]; then - cp doc/cookies.html $docdir/cookies.html - chmod 644 $docdir/cookies.html - fi - if [ -f doc/critbits.html ]; then - cp doc/critbits.html $docdir/critbits.html - chmod 644 $docdir/critbits.html - fi - if [ -f doc/db_mongo.html ]; then - cp doc/db_mongo.html $docdir/db_mongo.html - chmod 644 $docdir/db_mongo.html - fi - if [ -f doc/db_mysql.html ]; then - cp doc/db_mysql.html $docdir/db_mysql.html - chmod 644 $docdir/db_mysql.html - fi - if [ -f doc/db_postgres.html ]; then - cp doc/db_postgres.html $docdir/db_postgres.html - chmod 644 $docdir/db_postgres.html - fi - if [ -f doc/db_sqlite.html ]; then - cp doc/db_sqlite.html $docdir/db_sqlite.html - chmod 644 $docdir/db_sqlite.html - fi - if [ -f doc/dom.html ]; then - cp doc/dom.html $docdir/dom.html - chmod 644 $docdir/dom.html - fi - if [ -f doc/dynlib.html ]; then - cp doc/dynlib.html $docdir/dynlib.html - chmod 644 $docdir/dynlib.html - fi - if [ -f doc/encodings.html ]; then - cp doc/encodings.html $docdir/encodings.html - chmod 644 $docdir/encodings.html - fi - if [ -f doc/endb.html ]; then - cp doc/endb.html $docdir/endb.html - chmod 644 $docdir/endb.html - fi - if [ -f doc/endians.html ]; then - cp doc/endians.html $docdir/endians.html - chmod 644 $docdir/endians.html - fi - if [ -f doc/estp.html ]; then - cp doc/estp.html $docdir/estp.html - chmod 644 $docdir/estp.html - fi - if [ -f doc/events.html ]; then - cp doc/events.html $docdir/events.html - chmod 644 $docdir/events.html - fi - if [ -f doc/filters.html ]; then - cp doc/filters.html $docdir/filters.html - chmod 644 $docdir/filters.html - fi - if [ -f doc/fsmonitor.html ]; then - cp doc/fsmonitor.html $docdir/fsmonitor.html - chmod 644 $docdir/fsmonitor.html - fi - if [ -f doc/ftpclient.html ]; then - cp doc/ftpclient.html $docdir/ftpclient.html - chmod 644 $docdir/ftpclient.html - fi - if [ -f doc/gc.html ]; then - cp doc/gc.html $docdir/gc.html - chmod 644 $docdir/gc.html - fi - if [ -f doc/graphics.html ]; then - cp doc/graphics.html $docdir/graphics.html - chmod 644 $docdir/graphics.html - fi - if [ -f doc/hashes.html ]; then - cp doc/hashes.html $docdir/hashes.html - chmod 644 $docdir/hashes.html - fi - if [ -f doc/htmlgen.html ]; then - cp doc/htmlgen.html $docdir/htmlgen.html - chmod 644 $docdir/htmlgen.html - fi - if [ -f doc/htmlparser.html ]; then - cp doc/htmlparser.html $docdir/htmlparser.html - chmod 644 $docdir/htmlparser.html - fi - if [ -f doc/httpclient.html ]; then - cp doc/httpclient.html $docdir/httpclient.html - chmod 644 $docdir/httpclient.html - fi - if [ -f doc/httpserver.html ]; then - cp doc/httpserver.html $docdir/httpserver.html - chmod 644 $docdir/httpserver.html - fi - if [ -f doc/intern.html ]; then - cp doc/intern.html $docdir/intern.html - chmod 644 $docdir/intern.html - fi - if [ -f doc/intsets.html ]; then - cp doc/intsets.html $docdir/intsets.html - chmod 644 $docdir/intsets.html - fi - if [ -f doc/irc.html ]; then - cp doc/irc.html $docdir/irc.html - chmod 644 $docdir/irc.html - fi - if [ -f doc/json.html ]; then - cp doc/json.html $docdir/json.html - chmod 644 $docdir/json.html - fi - if [ -f doc/lexbase.html ]; then - cp doc/lexbase.html $docdir/lexbase.html - chmod 644 $docdir/lexbase.html - fi - if [ -f doc/lib.html ]; then - cp doc/lib.html $docdir/lib.html - chmod 644 $docdir/lib.html - fi - if [ -f doc/lists.html ]; then - cp doc/lists.html $docdir/lists.html - chmod 644 $docdir/lists.html - fi - if [ -f doc/locks.html ]; then - cp doc/locks.html $docdir/locks.html - chmod 644 $docdir/locks.html - fi - if [ -f doc/macros.html ]; then - cp doc/macros.html $docdir/macros.html - chmod 644 $docdir/macros.html - fi - if [ -f doc/manual.html ]; then - cp doc/manual.html $docdir/manual.html - chmod 644 $docdir/manual.html - fi - if [ -f doc/marshal.html ]; then - cp doc/marshal.html $docdir/marshal.html - chmod 644 $docdir/marshal.html - fi - if [ -f doc/matchers.html ]; then - cp doc/matchers.html $docdir/matchers.html - chmod 644 $docdir/matchers.html - fi - if [ -f doc/math.html ]; then - cp doc/math.html $docdir/math.html - chmod 644 $docdir/math.html - fi - if [ -f doc/memfiles.html ]; then - cp doc/memfiles.html $docdir/memfiles.html - chmod 644 $docdir/memfiles.html - fi - if [ -f doc/mimetypes.html ]; then - cp doc/mimetypes.html $docdir/mimetypes.html - chmod 644 $docdir/mimetypes.html - fi - if [ -f doc/nimgrep.html ]; then - cp doc/nimgrep.html $docdir/nimgrep.html - chmod 644 $docdir/nimgrep.html - fi - if [ -f doc/niminst.html ]; then - cp doc/niminst.html $docdir/niminst.html - chmod 644 $docdir/niminst.html - fi - if [ -f doc/nimprof.html ]; then - cp doc/nimprof.html $docdir/nimprof.html - chmod 644 $docdir/nimprof.html - fi - if [ -f doc/nimrodc.html ]; then - cp doc/nimrodc.html $docdir/nimrodc.html - chmod 644 $docdir/nimrodc.html - fi - if [ -f doc/oids.html ]; then - cp doc/oids.html $docdir/oids.html - chmod 644 $docdir/oids.html - fi - if [ -f doc/os.html ]; then - cp doc/os.html $docdir/os.html - chmod 644 $docdir/os.html - fi - if [ -f doc/osproc.html ]; then - cp doc/osproc.html $docdir/osproc.html - chmod 644 $docdir/osproc.html - fi - if [ -f doc/overview.html ]; then - cp doc/overview.html $docdir/overview.html - chmod 644 $docdir/overview.html - fi - if [ -f doc/parsecfg.html ]; then - cp doc/parsecfg.html $docdir/parsecfg.html - chmod 644 $docdir/parsecfg.html - fi - if [ -f doc/parsecsv.html ]; then - cp doc/parsecsv.html $docdir/parsecsv.html - chmod 644 $docdir/parsecsv.html - fi - if [ -f doc/parseopt.html ]; then - cp doc/parseopt.html $docdir/parseopt.html - chmod 644 $docdir/parseopt.html - fi - if [ -f doc/parsesql.html ]; then - cp doc/parsesql.html $docdir/parsesql.html - chmod 644 $docdir/parsesql.html - fi - if [ -f doc/parseutils.html ]; then - cp doc/parseutils.html $docdir/parseutils.html - chmod 644 $docdir/parseutils.html - fi - if [ -f doc/parsexml.html ]; then - cp doc/parsexml.html $docdir/parsexml.html - chmod 644 $docdir/parsexml.html - fi - if [ -f doc/pegs.html ]; then - cp doc/pegs.html $docdir/pegs.html - chmod 644 $docdir/pegs.html - fi - if [ -f doc/queues.html ]; then - cp doc/queues.html $docdir/queues.html - chmod 644 $docdir/queues.html - fi - if [ -f doc/rdstdin.html ]; then - cp doc/rdstdin.html $docdir/rdstdin.html - chmod 644 $docdir/rdstdin.html - fi - if [ -f doc/re.html ]; then - cp doc/re.html $docdir/re.html - chmod 644 $docdir/re.html - fi - if [ -f doc/redis.html ]; then - cp doc/redis.html $docdir/redis.html - chmod 644 $docdir/redis.html - fi - if [ -f doc/ropes.html ]; then - cp doc/ropes.html $docdir/ropes.html - chmod 644 $docdir/ropes.html - fi - if [ -f doc/scgi.html ]; then - cp doc/scgi.html $docdir/scgi.html - chmod 644 $docdir/scgi.html - fi - if [ -f doc/sdl.html ]; then - cp doc/sdl.html $docdir/sdl.html - chmod 644 $docdir/sdl.html - fi - if [ -f doc/sdl_gfx.html ]; then - cp doc/sdl_gfx.html $docdir/sdl_gfx.html - chmod 644 $docdir/sdl_gfx.html - fi - if [ -f doc/sdl_image.html ]; then - cp doc/sdl_image.html $docdir/sdl_image.html - chmod 644 $docdir/sdl_image.html - fi - if [ -f doc/sdl_mixer.html ]; then - cp doc/sdl_mixer.html $docdir/sdl_mixer.html - chmod 644 $docdir/sdl_mixer.html - fi - if [ -f doc/sdl_mixer_nosmpeg.html ]; then - cp doc/sdl_mixer_nosmpeg.html $docdir/sdl_mixer_nosmpeg.html - chmod 644 $docdir/sdl_mixer_nosmpeg.html - fi - if [ -f doc/sdl_net.html ]; then - cp doc/sdl_net.html $docdir/sdl_net.html - chmod 644 $docdir/sdl_net.html - fi - if [ -f doc/sdl_ttf.html ]; then - cp doc/sdl_ttf.html $docdir/sdl_ttf.html - chmod 644 $docdir/sdl_ttf.html - fi - if [ -f doc/sequtils.html ]; then - cp doc/sequtils.html $docdir/sequtils.html - chmod 644 $docdir/sequtils.html - fi - if [ -f doc/sets.html ]; then - cp doc/sets.html $docdir/sets.html - chmod 644 $docdir/sets.html - fi - if [ -f doc/smpeg.html ]; then - cp doc/smpeg.html $docdir/smpeg.html - chmod 644 $docdir/smpeg.html - fi - if [ -f doc/smtp.html ]; then - cp doc/smtp.html $docdir/smtp.html - chmod 644 $docdir/smtp.html - fi - if [ -f doc/sockets.html ]; then - cp doc/sockets.html $docdir/sockets.html - chmod 644 $docdir/sockets.html - fi - if [ -f doc/sphinx.html ]; then - cp doc/sphinx.html $docdir/sphinx.html - chmod 644 $docdir/sphinx.html - fi - if [ -f doc/ssl.html ]; then - cp doc/ssl.html $docdir/ssl.html - chmod 644 $docdir/ssl.html - fi - if [ -f doc/streams.html ]; then - cp doc/streams.html $docdir/streams.html - chmod 644 $docdir/streams.html - fi - if [ -f doc/strtabs.html ]; then - cp doc/strtabs.html $docdir/strtabs.html - chmod 644 $docdir/strtabs.html - fi - if [ -f doc/strutils.html ]; then - cp doc/strutils.html $docdir/strutils.html - chmod 644 $docdir/strutils.html - fi - if [ -f doc/subexes.html ]; then - cp doc/subexes.html $docdir/subexes.html - chmod 644 $docdir/subexes.html - fi - if [ -f doc/system.html ]; then - cp doc/system.html $docdir/system.html - chmod 644 $docdir/system.html - fi - if [ -f doc/tables.html ]; then - cp doc/tables.html $docdir/tables.html - chmod 644 $docdir/tables.html - fi - if [ -f doc/terminal.html ]; then - cp doc/terminal.html $docdir/terminal.html - chmod 644 $docdir/terminal.html - fi - if [ -f doc/theindex.html ]; then - cp doc/theindex.html $docdir/theindex.html - chmod 644 $docdir/theindex.html - fi - if [ -f doc/threads.html ]; then - cp doc/threads.html $docdir/threads.html - chmod 644 $docdir/threads.html - fi - if [ -f doc/times.html ]; then - cp doc/times.html $docdir/times.html - chmod 644 $docdir/times.html - fi - if [ -f doc/tools.html ]; then - cp doc/tools.html $docdir/tools.html - chmod 644 $docdir/tools.html - fi - if [ -f doc/trmacros.html ]; then - cp doc/trmacros.html $docdir/trmacros.html - chmod 644 $docdir/trmacros.html - fi - if [ -f doc/tut1.html ]; then - cp doc/tut1.html $docdir/tut1.html - chmod 644 $docdir/tut1.html - fi - if [ -f doc/tut2.html ]; then - cp doc/tut2.html $docdir/tut2.html - chmod 644 $docdir/tut2.html - fi - if [ -f doc/typeinfo.html ]; then - cp doc/typeinfo.html $docdir/typeinfo.html - chmod 644 $docdir/typeinfo.html - fi - if [ -f doc/unicode.html ]; then - cp doc/unicode.html $docdir/unicode.html - chmod 644 $docdir/unicode.html - fi - if [ -f doc/unidecode.html ]; then - cp doc/unidecode.html $docdir/unidecode.html - chmod 644 $docdir/unidecode.html - fi - if [ -f doc/unsigned.html ]; then - cp doc/unsigned.html $docdir/unsigned.html - chmod 644 $docdir/unsigned.html - fi - if [ -f doc/uri.html ]; then - cp doc/uri.html $docdir/uri.html - chmod 644 $docdir/uri.html - fi - if [ -f doc/web.html ]; then - cp doc/web.html $docdir/web.html - chmod 644 $docdir/web.html - fi - if [ -f doc/xmldom.html ]; then - cp doc/xmldom.html $docdir/xmldom.html - chmod 644 $docdir/xmldom.html - fi - if [ -f doc/xmldomparser.html ]; then - cp doc/xmldomparser.html $docdir/xmldomparser.html - chmod 644 $docdir/xmldomparser.html - fi - if [ -f doc/xmlparser.html ]; then - cp doc/xmlparser.html $docdir/xmlparser.html - chmod 644 $docdir/xmlparser.html - fi - if [ -f doc/xmltree.html ]; then - cp doc/xmltree.html $docdir/xmltree.html - chmod 644 $docdir/xmltree.html - fi - if [ -f doc/zipfiles.html ]; then - cp doc/zipfiles.html $docdir/zipfiles.html - chmod 644 $docdir/zipfiles.html - fi - if [ -f doc/zmq.html ]; then - cp doc/zmq.html $docdir/zmq.html - chmod 644 $docdir/zmq.html - fi - if [ -f doc/mytest.cfg ]; then - cp doc/mytest.cfg $docdir/mytest.cfg - chmod 644 $docdir/mytest.cfg - fi - if [ -f doc/c2nim.pdf ]; then - cp doc/c2nim.pdf $docdir/c2nim.pdf - chmod 644 $docdir/c2nim.pdf - fi - if [ -f doc/gc.pdf ]; then - cp doc/gc.pdf $docdir/gc.pdf - chmod 644 $docdir/gc.pdf - fi - if [ -f doc/lib.pdf ]; then - cp doc/lib.pdf $docdir/lib.pdf - chmod 644 $docdir/lib.pdf - fi - if [ -f doc/manual.pdf ]; then - cp doc/manual.pdf $docdir/manual.pdf - chmod 644 $docdir/manual.pdf - fi - if [ -f doc/niminst.pdf ]; then - cp doc/niminst.pdf $docdir/niminst.pdf - chmod 644 $docdir/niminst.pdf - fi - if [ -f doc/nimrodc.pdf ]; then - cp doc/nimrodc.pdf $docdir/nimrodc.pdf - chmod 644 $docdir/nimrodc.pdf - fi - if [ -f doc/tut1.pdf ]; then - cp doc/tut1.pdf $docdir/tut1.pdf - chmod 644 $docdir/tut1.pdf - fi - if [ -f doc/tut2.pdf ]; then - cp doc/tut2.pdf $docdir/tut2.pdf - chmod 644 $docdir/tut2.pdf - fi - cp lib/nimbase.h $libdir/nimbase.h - chmod 644 $libdir/nimbase.h - cp lib/nimrtl.nim $libdir/nimrtl.nim - chmod 644 $libdir/nimrtl.nim - cp lib/prelude.nim $libdir/prelude.nim - chmod 644 $libdir/prelude.nim - cp lib/system.nim $libdir/system.nim - chmod 644 $libdir/system.nim - cp lib/nimrtl.nimrod.cfg $libdir/nimrtl.nimrod.cfg - chmod 644 $libdir/nimrtl.nimrod.cfg - cp lib/system/alloc.nim $libdir/system/alloc.nim - chmod 644 $libdir/system/alloc.nim - cp lib/system/ansi_c.nim $libdir/system/ansi_c.nim - chmod 644 $libdir/system/ansi_c.nim - cp lib/system/arithm.nim $libdir/system/arithm.nim - chmod 644 $libdir/system/arithm.nim - cp lib/system/assign.nim $libdir/system/assign.nim - chmod 644 $libdir/system/assign.nim - cp lib/system/atomics.nim $libdir/system/atomics.nim - chmod 644 $libdir/system/atomics.nim - cp lib/system/avltree.nim $libdir/system/avltree.nim - chmod 644 $libdir/system/avltree.nim - cp lib/system/cellsets.nim $libdir/system/cellsets.nim - chmod 644 $libdir/system/cellsets.nim - cp lib/system/cgprocs.nim $libdir/system/cgprocs.nim - chmod 644 $libdir/system/cgprocs.nim - cp lib/system/channels.nim $libdir/system/channels.nim - chmod 644 $libdir/system/channels.nim - cp lib/system/debugger.nim $libdir/system/debugger.nim - chmod 644 $libdir/system/debugger.nim - cp lib/system/dyncalls.nim $libdir/system/dyncalls.nim - chmod 644 $libdir/system/dyncalls.nim - cp lib/system/ecmasys.nim $libdir/system/ecmasys.nim - chmod 644 $libdir/system/ecmasys.nim - cp lib/system/embedded.nim $libdir/system/embedded.nim - chmod 644 $libdir/system/embedded.nim - cp lib/system/excpt.nim $libdir/system/excpt.nim - chmod 644 $libdir/system/excpt.nim - cp lib/system/gc.nim $libdir/system/gc.nim - chmod 644 $libdir/system/gc.nim - cp lib/system/gc2.nim $libdir/system/gc2.nim - chmod 644 $libdir/system/gc2.nim - cp lib/system/hti.nim $libdir/system/hti.nim - chmod 644 $libdir/system/hti.nim - cp lib/system/inclrtl.nim $libdir/system/inclrtl.nim - chmod 644 $libdir/system/inclrtl.nim - cp lib/system/mmdisp.nim $libdir/system/mmdisp.nim - chmod 644 $libdir/system/mmdisp.nim - cp lib/system/profiler.nim $libdir/system/profiler.nim - chmod 644 $libdir/system/profiler.nim - cp lib/system/repr.nim $libdir/system/repr.nim - chmod 644 $libdir/system/repr.nim - cp lib/system/reprjs.nim $libdir/system/reprjs.nim - chmod 644 $libdir/system/reprjs.nim - cp lib/system/sets.nim $libdir/system/sets.nim - chmod 644 $libdir/system/sets.nim - cp lib/system/sysio.nim $libdir/system/sysio.nim - chmod 644 $libdir/system/sysio.nim - cp lib/system/syslocks.nim $libdir/system/syslocks.nim - chmod 644 $libdir/system/syslocks.nim - cp lib/system/sysstr.nim $libdir/system/sysstr.nim - chmod 644 $libdir/system/sysstr.nim - cp lib/system/threads.nim $libdir/system/threads.nim - chmod 644 $libdir/system/threads.nim - cp lib/system/timers.nim $libdir/system/timers.nim - chmod 644 $libdir/system/timers.nim - cp lib/system/widestrs.nim $libdir/system/widestrs.nim - chmod 644 $libdir/system/widestrs.nim - cp lib/core/locks.nim $libdir/core/locks.nim - chmod 644 $libdir/core/locks.nim - cp lib/core/macros.nim $libdir/core/macros.nim - chmod 644 $libdir/core/macros.nim - cp lib/core/typeinfo.nim $libdir/core/typeinfo.nim - chmod 644 $libdir/core/typeinfo.nim - cp lib/core/unsigned.nim $libdir/core/unsigned.nim - chmod 644 $libdir/core/unsigned.nim - cp lib/pure/actors.nim $libdir/pure/actors.nim - chmod 644 $libdir/pure/actors.nim - cp lib/pure/algorithm.nim $libdir/pure/algorithm.nim - chmod 644 $libdir/pure/algorithm.nim - cp lib/pure/asyncio.nim $libdir/pure/asyncio.nim - chmod 644 $libdir/pure/asyncio.nim - cp lib/pure/base64.nim $libdir/pure/base64.nim - chmod 644 $libdir/pure/base64.nim - cp lib/pure/browsers.nim $libdir/pure/browsers.nim - chmod 644 $libdir/pure/browsers.nim - cp lib/pure/cgi.nim $libdir/pure/cgi.nim - chmod 644 $libdir/pure/cgi.nim - cp lib/pure/colors.nim $libdir/pure/colors.nim - chmod 644 $libdir/pure/colors.nim - cp lib/pure/complex.nim $libdir/pure/complex.nim - chmod 644 $libdir/pure/complex.nim - cp lib/pure/cookies.nim $libdir/pure/cookies.nim - chmod 644 $libdir/pure/cookies.nim - cp lib/pure/dynlib.nim $libdir/pure/dynlib.nim - chmod 644 $libdir/pure/dynlib.nim - cp lib/pure/encodings.nim $libdir/pure/encodings.nim - chmod 644 $libdir/pure/encodings.nim - cp lib/pure/endians.nim $libdir/pure/endians.nim - chmod 644 $libdir/pure/endians.nim - cp lib/pure/events.nim $libdir/pure/events.nim - chmod 644 $libdir/pure/events.nim - cp lib/pure/fsmonitor.nim $libdir/pure/fsmonitor.nim - chmod 644 $libdir/pure/fsmonitor.nim - cp lib/pure/ftpclient.nim $libdir/pure/ftpclient.nim - chmod 644 $libdir/pure/ftpclient.nim - cp lib/pure/gentabs.nim $libdir/pure/gentabs.nim - chmod 644 $libdir/pure/gentabs.nim - cp lib/pure/hashes.nim $libdir/pure/hashes.nim - chmod 644 $libdir/pure/hashes.nim - cp lib/pure/htmlgen.nim $libdir/pure/htmlgen.nim - chmod 644 $libdir/pure/htmlgen.nim - cp lib/pure/htmlparser.nim $libdir/pure/htmlparser.nim - chmod 644 $libdir/pure/htmlparser.nim - cp lib/pure/httpclient.nim $libdir/pure/httpclient.nim - chmod 644 $libdir/pure/httpclient.nim - cp lib/pure/httpserver.nim $libdir/pure/httpserver.nim - chmod 644 $libdir/pure/httpserver.nim - cp lib/pure/irc.nim $libdir/pure/irc.nim - chmod 644 $libdir/pure/irc.nim - cp lib/pure/json.nim $libdir/pure/json.nim - chmod 644 $libdir/pure/json.nim - cp lib/pure/lexbase.nim $libdir/pure/lexbase.nim - chmod 644 $libdir/pure/lexbase.nim - cp lib/pure/marshal.nim $libdir/pure/marshal.nim - chmod 644 $libdir/pure/marshal.nim - cp lib/pure/matchers.nim $libdir/pure/matchers.nim - chmod 644 $libdir/pure/matchers.nim - cp lib/pure/math.nim $libdir/pure/math.nim - chmod 644 $libdir/pure/math.nim - cp lib/pure/md5.nim $libdir/pure/md5.nim - chmod 644 $libdir/pure/md5.nim - cp lib/pure/memfiles.nim $libdir/pure/memfiles.nim - chmod 644 $libdir/pure/memfiles.nim - cp lib/pure/mimetypes.nim $libdir/pure/mimetypes.nim - chmod 644 $libdir/pure/mimetypes.nim - cp lib/pure/nimprof.nim $libdir/pure/nimprof.nim - chmod 644 $libdir/pure/nimprof.nim - cp lib/pure/oids.nim $libdir/pure/oids.nim - chmod 644 $libdir/pure/oids.nim - cp lib/pure/os.nim $libdir/pure/os.nim - chmod 644 $libdir/pure/os.nim - cp lib/pure/osproc.nim $libdir/pure/osproc.nim - chmod 644 $libdir/pure/osproc.nim - cp lib/pure/parsecfg.nim $libdir/pure/parsecfg.nim - chmod 644 $libdir/pure/parsecfg.nim - cp lib/pure/parsecsv.nim $libdir/pure/parsecsv.nim - chmod 644 $libdir/pure/parsecsv.nim - cp lib/pure/parseopt.nim $libdir/pure/parseopt.nim - chmod 644 $libdir/pure/parseopt.nim - cp lib/pure/parsesql.nim $libdir/pure/parsesql.nim - chmod 644 $libdir/pure/parsesql.nim - cp lib/pure/parseurl.nim $libdir/pure/parseurl.nim - chmod 644 $libdir/pure/parseurl.nim - cp lib/pure/parseutils.nim $libdir/pure/parseutils.nim - chmod 644 $libdir/pure/parseutils.nim - cp lib/pure/parsexml.nim $libdir/pure/parsexml.nim - chmod 644 $libdir/pure/parsexml.nim - cp lib/pure/pegs.nim $libdir/pure/pegs.nim - chmod 644 $libdir/pure/pegs.nim - cp lib/pure/redis.nim $libdir/pure/redis.nim - chmod 644 $libdir/pure/redis.nim - cp lib/pure/romans.nim $libdir/pure/romans.nim - chmod 644 $libdir/pure/romans.nim - cp lib/pure/ropes.nim $libdir/pure/ropes.nim - chmod 644 $libdir/pure/ropes.nim - cp lib/pure/scgi.nim $libdir/pure/scgi.nim - chmod 644 $libdir/pure/scgi.nim - cp lib/pure/smtp.nim $libdir/pure/smtp.nim - chmod 644 $libdir/pure/smtp.nim - cp lib/pure/sockets.nim $libdir/pure/sockets.nim - chmod 644 $libdir/pure/sockets.nim - cp lib/pure/streams.nim $libdir/pure/streams.nim - chmod 644 $libdir/pure/streams.nim - cp lib/pure/strtabs.nim $libdir/pure/strtabs.nim - chmod 644 $libdir/pure/strtabs.nim - cp lib/pure/strutils.nim $libdir/pure/strutils.nim - chmod 644 $libdir/pure/strutils.nim - cp lib/pure/subexes.nim $libdir/pure/subexes.nim - chmod 644 $libdir/pure/subexes.nim - cp lib/pure/terminal.nim $libdir/pure/terminal.nim - chmod 644 $libdir/pure/terminal.nim - cp lib/pure/times.nim $libdir/pure/times.nim - chmod 644 $libdir/pure/times.nim - cp lib/pure/typetraits.nim $libdir/pure/typetraits.nim - chmod 644 $libdir/pure/typetraits.nim - cp lib/pure/unicode.nim $libdir/pure/unicode.nim - chmod 644 $libdir/pure/unicode.nim - cp lib/pure/unittest.nim $libdir/pure/unittest.nim - chmod 644 $libdir/pure/unittest.nim - cp lib/pure/uri.nim $libdir/pure/uri.nim - chmod 644 $libdir/pure/uri.nim - cp lib/pure/xmldom.nim $libdir/pure/xmldom.nim - chmod 644 $libdir/pure/xmldom.nim - cp lib/pure/xmldomparser.nim $libdir/pure/xmldomparser.nim - chmod 644 $libdir/pure/xmldomparser.nim - cp lib/pure/xmlparser.nim $libdir/pure/xmlparser.nim - chmod 644 $libdir/pure/xmlparser.nim - cp lib/pure/xmltree.nim $libdir/pure/xmltree.nim - chmod 644 $libdir/pure/xmltree.nim - cp lib/pure/collections/critbits.nim $libdir/pure/collections/critbits.nim - chmod 644 $libdir/pure/collections/critbits.nim - cp lib/pure/collections/intsets.nim $libdir/pure/collections/intsets.nim - chmod 644 $libdir/pure/collections/intsets.nim - cp lib/pure/collections/lists.nim $libdir/pure/collections/lists.nim - chmod 644 $libdir/pure/collections/lists.nim - cp lib/pure/collections/queues.nim $libdir/pure/collections/queues.nim - chmod 644 $libdir/pure/collections/queues.nim - cp lib/pure/collections/sequtils.nim $libdir/pure/collections/sequtils.nim - chmod 644 $libdir/pure/collections/sequtils.nim - cp lib/pure/collections/sets.nim $libdir/pure/collections/sets.nim - chmod 644 $libdir/pure/collections/sets.nim - cp lib/pure/collections/tables.nim $libdir/pure/collections/tables.nim - chmod 644 $libdir/pure/collections/tables.nim - cp lib/impure/db_mongo.nim $libdir/impure/db_mongo.nim - chmod 644 $libdir/impure/db_mongo.nim - cp lib/impure/db_mysql.nim $libdir/impure/db_mysql.nim - chmod 644 $libdir/impure/db_mysql.nim - cp lib/impure/db_postgres.nim $libdir/impure/db_postgres.nim - chmod 644 $libdir/impure/db_postgres.nim - cp lib/impure/db_sqlite.nim $libdir/impure/db_sqlite.nim - chmod 644 $libdir/impure/db_sqlite.nim - cp lib/impure/dialogs.nim $libdir/impure/dialogs.nim - chmod 644 $libdir/impure/dialogs.nim - cp lib/impure/graphics.nim $libdir/impure/graphics.nim - chmod 644 $libdir/impure/graphics.nim - cp lib/impure/osinfo_posix.nim $libdir/impure/osinfo_posix.nim - chmod 644 $libdir/impure/osinfo_posix.nim - cp lib/impure/osinfo_win.nim $libdir/impure/osinfo_win.nim - chmod 644 $libdir/impure/osinfo_win.nim - cp lib/impure/rdstdin.nim $libdir/impure/rdstdin.nim - chmod 644 $libdir/impure/rdstdin.nim - cp lib/impure/re.nim $libdir/impure/re.nim - chmod 644 $libdir/impure/re.nim - cp lib/impure/ssl.nim $libdir/impure/ssl.nim - chmod 644 $libdir/impure/ssl.nim - cp lib/impure/web.nim $libdir/impure/web.nim - chmod 644 $libdir/impure/web.nim - cp lib/impure/zipfiles.nim $libdir/impure/zipfiles.nim - chmod 644 $libdir/impure/zipfiles.nim - cp lib/wrappers/claro.nim $libdir/wrappers/claro.nim - chmod 644 $libdir/wrappers/claro.nim - cp lib/wrappers/expat.nim $libdir/wrappers/expat.nim - chmod 644 $libdir/wrappers/expat.nim - cp lib/wrappers/iup.nim $libdir/wrappers/iup.nim - chmod 644 $libdir/wrappers/iup.nim - cp lib/wrappers/joyent_http_parser.nim $libdir/wrappers/joyent_http_parser.nim - chmod 644 $libdir/wrappers/joyent_http_parser.nim - cp lib/wrappers/libcurl.nim $libdir/wrappers/libcurl.nim - chmod 644 $libdir/wrappers/libcurl.nim - cp lib/wrappers/libffi.nim $libdir/wrappers/libffi.nim - chmod 644 $libdir/wrappers/libffi.nim - cp lib/wrappers/libsvm.nim $libdir/wrappers/libsvm.nim - chmod 644 $libdir/wrappers/libsvm.nim - cp lib/wrappers/libuv.nim $libdir/wrappers/libuv.nim - chmod 644 $libdir/wrappers/libuv.nim - cp lib/wrappers/mongo.nim $libdir/wrappers/mongo.nim - chmod 644 $libdir/wrappers/mongo.nim - cp lib/wrappers/mysql.nim $libdir/wrappers/mysql.nim - chmod 644 $libdir/wrappers/mysql.nim - cp lib/wrappers/odbcsql.nim $libdir/wrappers/odbcsql.nim - chmod 644 $libdir/wrappers/odbcsql.nim - cp lib/wrappers/openssl.nim $libdir/wrappers/openssl.nim - chmod 644 $libdir/wrappers/openssl.nim - cp lib/wrappers/pcre.nim $libdir/wrappers/pcre.nim - chmod 644 $libdir/wrappers/pcre.nim - cp lib/wrappers/pdcurses.nim $libdir/wrappers/pdcurses.nim - chmod 644 $libdir/wrappers/pdcurses.nim - cp lib/wrappers/postgres.nim $libdir/wrappers/postgres.nim - chmod 644 $libdir/wrappers/postgres.nim - cp lib/wrappers/python.nim $libdir/wrappers/python.nim - chmod 644 $libdir/wrappers/python.nim - cp lib/wrappers/sphinx.nim $libdir/wrappers/sphinx.nim - chmod 644 $libdir/wrappers/sphinx.nim - cp lib/wrappers/sqlite3.nim $libdir/wrappers/sqlite3.nim - chmod 644 $libdir/wrappers/sqlite3.nim - cp lib/wrappers/tcl.nim $libdir/wrappers/tcl.nim - chmod 644 $libdir/wrappers/tcl.nim - cp lib/wrappers/tinyc.nim $libdir/wrappers/tinyc.nim - chmod 644 $libdir/wrappers/tinyc.nim - cp lib/wrappers/tre.nim $libdir/wrappers/tre.nim - chmod 644 $libdir/wrappers/tre.nim - cp lib/wrappers/zmq.nim $libdir/wrappers/zmq.nim - chmod 644 $libdir/wrappers/zmq.nim - cp lib/wrappers/cairo/cairo.nim $libdir/wrappers/cairo/cairo.nim - chmod 644 $libdir/wrappers/cairo/cairo.nim - cp lib/wrappers/cairo/cairoft.nim $libdir/wrappers/cairo/cairoft.nim - chmod 644 $libdir/wrappers/cairo/cairoft.nim - cp lib/wrappers/cairo/cairowin32.nim $libdir/wrappers/cairo/cairowin32.nim - chmod 644 $libdir/wrappers/cairo/cairowin32.nim - cp lib/wrappers/cairo/cairoxlib.nim $libdir/wrappers/cairo/cairoxlib.nim - chmod 644 $libdir/wrappers/cairo/cairoxlib.nim - cp lib/wrappers/gtk/atk.nim $libdir/wrappers/gtk/atk.nim - chmod 644 $libdir/wrappers/gtk/atk.nim - cp lib/wrappers/gtk/gdk2.nim $libdir/wrappers/gtk/gdk2.nim - chmod 644 $libdir/wrappers/gtk/gdk2.nim - cp lib/wrappers/gtk/gdk2pixbuf.nim $libdir/wrappers/gtk/gdk2pixbuf.nim - chmod 644 $libdir/wrappers/gtk/gdk2pixbuf.nim - cp lib/wrappers/gtk/gdkglext.nim $libdir/wrappers/gtk/gdkglext.nim - chmod 644 $libdir/wrappers/gtk/gdkglext.nim - cp lib/wrappers/gtk/glib2.nim $libdir/wrappers/gtk/glib2.nim - chmod 644 $libdir/wrappers/gtk/glib2.nim - cp lib/wrappers/gtk/gtk2.nim $libdir/wrappers/gtk/gtk2.nim - chmod 644 $libdir/wrappers/gtk/gtk2.nim - cp lib/wrappers/gtk/gtkglext.nim $libdir/wrappers/gtk/gtkglext.nim - chmod 644 $libdir/wrappers/gtk/gtkglext.nim - cp lib/wrappers/gtk/gtkhtml.nim $libdir/wrappers/gtk/gtkhtml.nim - chmod 644 $libdir/wrappers/gtk/gtkhtml.nim - cp lib/wrappers/gtk/libglade2.nim $libdir/wrappers/gtk/libglade2.nim - chmod 644 $libdir/wrappers/gtk/libglade2.nim - cp lib/wrappers/gtk/pango.nim $libdir/wrappers/gtk/pango.nim - chmod 644 $libdir/wrappers/gtk/pango.nim - cp lib/wrappers/gtk/pangoutils.nim $libdir/wrappers/gtk/pangoutils.nim - chmod 644 $libdir/wrappers/gtk/pangoutils.nim - cp lib/wrappers/lua/lauxlib.nim $libdir/wrappers/lua/lauxlib.nim - chmod 644 $libdir/wrappers/lua/lauxlib.nim - cp lib/wrappers/lua/lua.nim $libdir/wrappers/lua/lua.nim - chmod 644 $libdir/wrappers/lua/lua.nim - cp lib/wrappers/lua/lualib.nim $libdir/wrappers/lua/lualib.nim - chmod 644 $libdir/wrappers/lua/lualib.nim - cp lib/wrappers/opengl/gl.nim $libdir/wrappers/opengl/gl.nim - chmod 644 $libdir/wrappers/opengl/gl.nim - cp lib/wrappers/opengl/glext.nim $libdir/wrappers/opengl/glext.nim - chmod 644 $libdir/wrappers/opengl/glext.nim - cp lib/wrappers/opengl/glu.nim $libdir/wrappers/opengl/glu.nim - chmod 644 $libdir/wrappers/opengl/glu.nim - cp lib/wrappers/opengl/glut.nim $libdir/wrappers/opengl/glut.nim - chmod 644 $libdir/wrappers/opengl/glut.nim - cp lib/wrappers/opengl/glx.nim $libdir/wrappers/opengl/glx.nim - chmod 644 $libdir/wrappers/opengl/glx.nim - cp lib/wrappers/opengl/opengl.nim $libdir/wrappers/opengl/opengl.nim - chmod 644 $libdir/wrappers/opengl/opengl.nim - cp lib/wrappers/opengl/wingl.nim $libdir/wrappers/opengl/wingl.nim - chmod 644 $libdir/wrappers/opengl/wingl.nim - cp lib/wrappers/readline/history.nim $libdir/wrappers/readline/history.nim - chmod 644 $libdir/wrappers/readline/history.nim - cp lib/wrappers/readline/readline.nim $libdir/wrappers/readline/readline.nim - chmod 644 $libdir/wrappers/readline/readline.nim - cp lib/wrappers/readline/rltypedefs.nim $libdir/wrappers/readline/rltypedefs.nim - chmod 644 $libdir/wrappers/readline/rltypedefs.nim - cp lib/wrappers/sdl/sdl.nim $libdir/wrappers/sdl/sdl.nim - chmod 644 $libdir/wrappers/sdl/sdl.nim - cp lib/wrappers/sdl/sdl_gfx.nim $libdir/wrappers/sdl/sdl_gfx.nim - chmod 644 $libdir/wrappers/sdl/sdl_gfx.nim - cp lib/wrappers/sdl/sdl_image.nim $libdir/wrappers/sdl/sdl_image.nim - chmod 644 $libdir/wrappers/sdl/sdl_image.nim - cp lib/wrappers/sdl/sdl_mixer.nim $libdir/wrappers/sdl/sdl_mixer.nim - chmod 644 $libdir/wrappers/sdl/sdl_mixer.nim - cp lib/wrappers/sdl/sdl_mixer_nosmpeg.nim $libdir/wrappers/sdl/sdl_mixer_nosmpeg.nim - chmod 644 $libdir/wrappers/sdl/sdl_mixer_nosmpeg.nim - cp lib/wrappers/sdl/sdl_net.nim $libdir/wrappers/sdl/sdl_net.nim - chmod 644 $libdir/wrappers/sdl/sdl_net.nim - cp lib/wrappers/sdl/sdl_ttf.nim $libdir/wrappers/sdl/sdl_ttf.nim - chmod 644 $libdir/wrappers/sdl/sdl_ttf.nim - cp lib/wrappers/sdl/smpeg.nim $libdir/wrappers/sdl/smpeg.nim - chmod 644 $libdir/wrappers/sdl/smpeg.nim - cp lib/wrappers/x11/cursorfont.nim $libdir/wrappers/x11/cursorfont.nim - chmod 644 $libdir/wrappers/x11/cursorfont.nim - cp lib/wrappers/x11/keysym.nim $libdir/wrappers/x11/keysym.nim - chmod 644 $libdir/wrappers/x11/keysym.nim - cp lib/wrappers/x11/x.nim $libdir/wrappers/x11/x.nim - chmod 644 $libdir/wrappers/x11/x.nim - cp lib/wrappers/x11/xatom.nim $libdir/wrappers/x11/xatom.nim - chmod 644 $libdir/wrappers/x11/xatom.nim - cp lib/wrappers/x11/xcms.nim $libdir/wrappers/x11/xcms.nim - chmod 644 $libdir/wrappers/x11/xcms.nim - cp lib/wrappers/x11/xf86dga.nim $libdir/wrappers/x11/xf86dga.nim - chmod 644 $libdir/wrappers/x11/xf86dga.nim - cp lib/wrappers/x11/xf86vmode.nim $libdir/wrappers/x11/xf86vmode.nim - chmod 644 $libdir/wrappers/x11/xf86vmode.nim - cp lib/wrappers/x11/xi.nim $libdir/wrappers/x11/xi.nim - chmod 644 $libdir/wrappers/x11/xi.nim - cp lib/wrappers/x11/xinerama.nim $libdir/wrappers/x11/xinerama.nim - chmod 644 $libdir/wrappers/x11/xinerama.nim - cp lib/wrappers/x11/xkb.nim $libdir/wrappers/x11/xkb.nim - chmod 644 $libdir/wrappers/x11/xkb.nim - cp lib/wrappers/x11/xkblib.nim $libdir/wrappers/x11/xkblib.nim - chmod 644 $libdir/wrappers/x11/xkblib.nim - cp lib/wrappers/x11/xlib.nim $libdir/wrappers/x11/xlib.nim - chmod 644 $libdir/wrappers/x11/xlib.nim - cp lib/wrappers/x11/xrandr.nim $libdir/wrappers/x11/xrandr.nim - chmod 644 $libdir/wrappers/x11/xrandr.nim - cp lib/wrappers/x11/xrender.nim $libdir/wrappers/x11/xrender.nim - chmod 644 $libdir/wrappers/x11/xrender.nim - cp lib/wrappers/x11/xresource.nim $libdir/wrappers/x11/xresource.nim - chmod 644 $libdir/wrappers/x11/xresource.nim - cp lib/wrappers/x11/xshm.nim $libdir/wrappers/x11/xshm.nim - chmod 644 $libdir/wrappers/x11/xshm.nim - cp lib/wrappers/x11/xutil.nim $libdir/wrappers/x11/xutil.nim - chmod 644 $libdir/wrappers/x11/xutil.nim - cp lib/wrappers/x11/xv.nim $libdir/wrappers/x11/xv.nim - chmod 644 $libdir/wrappers/x11/xv.nim - cp lib/wrappers/x11/xvlib.nim $libdir/wrappers/x11/xvlib.nim - chmod 644 $libdir/wrappers/x11/xvlib.nim - cp lib/wrappers/zip/libzip.nim $libdir/wrappers/zip/libzip.nim - chmod 644 $libdir/wrappers/zip/libzip.nim - cp lib/wrappers/zip/zlib.nim $libdir/wrappers/zip/zlib.nim - chmod 644 $libdir/wrappers/zip/zlib.nim - cp lib/wrappers/zip/zzip.nim $libdir/wrappers/zip/zzip.nim - chmod 644 $libdir/wrappers/zip/zzip.nim - cp lib/wrappers/zip/libzip_all.c $libdir/wrappers/zip/libzip_all.c - chmod 644 $libdir/wrappers/zip/libzip_all.c - cp lib/windows/mmsystem.nim $libdir/windows/mmsystem.nim - chmod 644 $libdir/windows/mmsystem.nim - cp lib/windows/nb30.nim $libdir/windows/nb30.nim - chmod 644 $libdir/windows/nb30.nim - cp lib/windows/ole2.nim $libdir/windows/ole2.nim - chmod 644 $libdir/windows/ole2.nim - cp lib/windows/psapi.nim $libdir/windows/psapi.nim - chmod 644 $libdir/windows/psapi.nim - cp lib/windows/shellapi.nim $libdir/windows/shellapi.nim - chmod 644 $libdir/windows/shellapi.nim - cp lib/windows/shfolder.nim $libdir/windows/shfolder.nim - chmod 644 $libdir/windows/shfolder.nim - cp lib/windows/windows.nim $libdir/windows/windows.nim - chmod 644 $libdir/windows/windows.nim - cp lib/windows/winlean.nim $libdir/windows/winlean.nim - chmod 644 $libdir/windows/winlean.nim - cp lib/posix/inotify.nim $libdir/posix/inotify.nim - chmod 644 $libdir/posix/inotify.nim - cp lib/posix/posix.nim $libdir/posix/posix.nim - chmod 644 $libdir/posix/posix.nim - cp lib/ecmas/dom.nim $libdir/ecmas/dom.nim - chmod 644 $libdir/ecmas/dom.nim - - echo "installation successful" -else - echo "Nimrod installation script" - echo "Usage: [sudo] sh install.sh DIR" - echo "Where DIR may be:" - echo " /usr/bin" - echo " /usr/local/bin" - echo " /opt" - echo " (treated like '/opt')" - echo "To deinstall, use the command:" - echo "sh deinstall.sh DIR" - exit 1 -fi - diff --git a/install.txt b/install.txt old mode 100755 new mode 100644 index 8021abfbb..eefdfb682 --- a/install.txt +++ b/install.txt @@ -103,4 +103,5 @@ On UNIX * ``bin/nimrod c koch`` * ``./koch boot -d:release`` +Installation on UNIX can then be done with ``koch install [dir]``. diff --git a/koch.nim b/koch.nim old mode 100755 new mode 100644 index 2a22d7ca5..883fffb6d --- a/koch.nim +++ b/koch.nim @@ -36,6 +36,7 @@ Options: --help, -h shows this help and quits Possible Commands: boot [options] bootstraps with given command line options + install [dir] installs to given directory clean cleans Nimrod project; removes generated files web generates the website csource [options] builds the C sources for installation @@ -84,7 +85,9 @@ proc inno(args: string) = NimrodVersion) proc install(args: string) = - exec("sh ./build.sh") + exec("nimrod cc -r tools/niminst/niminst --var:version=$# scripts compiler/nimrod.ini" % + NimrodVersion) + exec("sh ./install.sh $#" % args) proc web(args: string) = exec(("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" & diff --git a/lib/copying.txt b/lib/copying.txt old mode 100755 new mode 100644 diff --git a/lib/core/macros.nim b/lib/core/macros.nim old mode 100755 new mode 100644 index bf7510a92..949719316 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1,7 +1,7 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -21,9 +21,11 @@ type nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit, nnkFloat32Lit, nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit, nnkTripleStrLit, nnkNilLit, nnkMetaNode, nnkDotCall, - nnkCommand, nnkCall, nnkCallStrLit, nnkExprEqExpr, - nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, nnkInfix, - nnkPrefix, nnkPostfix, nnkPar, nnkCurly, nnkCurlyExpr, + nnkCommand, nnkCall, nnkCallStrLit, nnkInfix, + nnkPrefix, nnkPostfix, nnkHiddenCallConv, + nnkExprEqExpr, + nnkExprColonExpr, nnkIdentDefs, nnkVarTuple, + nnkPar, nnkObjConstr, nnkCurly, nnkCurlyExpr, nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange, nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr, nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkAccQuoted, @@ -31,7 +33,7 @@ type nnkClosedSymChoice, nnkOpenSymChoice, nnkHiddenStdConv, - nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, nnkCast, nnkStaticExpr, + nnkHiddenSubConv, nnkConv, nnkCast, nnkStaticExpr, nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange, nnkStringToCString, nnkCStringToString, nnkAsgn, @@ -128,12 +130,14 @@ proc `==`*(a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.} proc len*(n: PNimrodNode): int {.magic: "NLen".} ## returns the number of children of `n`. -proc add*(father, child: PNimrodNode) {.magic: "NAdd".} - ## adds the `child` to the `father` node +proc add*(father, child: PNimrodNode): PNimrodNode {.magic: "NAdd", discardable.} + ## Adds the `child` to the `father` node. Returns the + ## father node so that calls can be nested. -proc add*(father: PNimrodNode, children: varargs[PNimrodNode]) {. - magic: "NAddMultiple".} - ## adds each child of `children` to the `father` node +proc add*(father: PNimrodNode, children: varargs[PNimrodNode]): PNimrodNode {. + magic: "NAddMultiple", discardable.} + ## Adds each child of `children` to the `father` node. + ## Returns the `father` node so that calls can be nested. proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".} ## deletes `n` children of `father` starting at index `idx`. @@ -331,9 +335,11 @@ proc nestList*(theProc: TNimrodIdent, ## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``. var L = x.len result = newCall(theProc, x[L-2], x[L-1]) - var a = result for i in countdown(L-3, 0): - a = newCall(theProc, x[i], copyNimTree(a)) + # XXX the 'copyNimTree' here is necessary due to a bug in the evaluation + # engine that would otherwise create an endless loop here. :-( + # This could easily user code and so should be fixed in evals.nim somehow. + result = newCall(theProc, x[i], copyNimTree(result)) proc treeRepr*(n: PNimrodNode): string {.compileTime.} = ## Convert the AST `n` to a human-readable tree-like string. @@ -398,3 +404,9 @@ macro dumpLisp*(s: stmt): stmt = echo s.lispRepr ## ## See `dumpTree`. +macro dumpTreeImm*(s: stmt): stmt {.immediate.} = echo s.treeRepr + ## The ``immediate`` version of `dumpTree`. + +macro dumpLispImm*(s: stmt): stmt {.immediate.} = echo s.lispRepr + ## The ``immediate`` version of `dumpLisp`. + diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim old mode 100755 new mode 100644 index eb2a0c9e5..6f8081b5f --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -102,6 +102,16 @@ proc newAny(value: pointer, rawType: PNimType): TAny = result.value = value result.rawType = rawType +when defined(system.TVarSlot): + proc toAny*(x: TVarSlot): TAny {.inline.} = + ## constructs a ``TAny`` object from a variable slot ``x``. + ## This captures `x`'s address, so `x` can be modified with its + ## ``TAny`` wrapper! The client needs to ensure that the wrapper + ## **does not** live longer than `x`! + ## This is provided for easier reflection capabilities of a debugger. + result.value = x.address + result.rawType = x.typ + proc toAny*[T](x: var T): TAny {.inline.} = ## constructs a ``TAny`` object from `x`. This captures `x`'s address, so ## `x` can be modified with its ``TAny`` wrapper! The client needs to ensure diff --git a/lib/cycle.h b/lib/cycle.h old mode 100755 new mode 100644 diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim old mode 100755 new mode 100644 diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim old mode 100755 new mode 100644 diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim old mode 100755 new mode 100644 diff --git a/lib/impure/dialogs.nim b/lib/impure/dialogs.nim old mode 100755 new mode 100644 diff --git a/lib/impure/graphics.nim b/lib/impure/graphics.nim old mode 100755 new mode 100644 index 0ec2d3903..f9aadf995 --- a/lib/impure/graphics.nim +++ b/lib/impure/graphics.nim @@ -324,15 +324,15 @@ proc drawRect*(sur: PSurface, r: TRect, color: TColor) = var video = cast[PPixels](sur.s.pixels) var pitch = sur.s.pitch.int div ColSize if (r.x >= 0 and r.x <= sur.s.w) and (r.y >= 0 and r.y <= sur.s.h): - var minW = min(sur.s.w - r.x, r.width - 1) - var minH = min(sur.s.h - r.y, r.height - 1) + var minW = min(sur.s.w - r.x, r.width) + var minH = min(sur.s.h - r.y, r.height) # Draw Top for i in 0 .. minW - 1: setPix(video, pitch, r.x + i, r.y, color) setPix(video, pitch, r.x + i, r.y + minH - 1, color) # Draw bottom - # Draw left side + # Draw left side for i in 0 .. minH - 1: setPix(video, pitch, r.x, r.y + i, color) setPix(video, pitch, r.x + minW - 1, r.y + i, color) # Draw right side @@ -520,6 +520,11 @@ when isMainModule: surf.drawCircle((600, 500), 60, colRed) surf.fillRect((50, 50, 100, 100), colFuchsia) + surf.fillRect((150, 50, 100, 100), colGreen) + surf.drawRect((50, 150, 100, 100), colGreen) + surf.drawRect((150, 150, 100, 100), colAqua) + surf.drawRect((250, 150, 100, 100), colBlue) + surf.drawHorLine(250, 150, 100, colRed) surf.drawLineAA((592, 160), (592, 280), colPurple) diff --git a/lib/impure/osinfo_posix.nim b/lib/impure/osinfo_posix.nim old mode 100755 new mode 100644 diff --git a/lib/impure/osinfo_win.nim b/lib/impure/osinfo_win.nim old mode 100755 new mode 100644 diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim old mode 100755 new mode 100644 diff --git a/lib/impure/re.nim b/lib/impure/re.nim old mode 100755 new mode 100644 diff --git a/lib/impure/ssl.nim b/lib/impure/ssl.nim old mode 100755 new mode 100644 diff --git a/lib/impure/web.nim b/lib/impure/web.nim old mode 100755 new mode 100644 diff --git a/lib/impure/zipfiles.nim b/lib/impure/zipfiles.nim old mode 100755 new mode 100644 diff --git a/lib/ecmas/dom.nim b/lib/js/dom.nim old mode 100755 new mode 100644 similarity index 98% rename from lib/ecmas/dom.nim rename to lib/js/dom.nim index 2fb2085b4..d90067176 --- a/lib/ecmas/dom.nim +++ b/lib/js/dom.nim @@ -7,10 +7,10 @@ # distribution, for details about the copyright. # -## Declaration of the Document Object Model for the ECMAScript backend. +## Declaration of the Document Object Model for the JavaScript backend. -when not defined(ecmascript) and not defined(Nimdoc): - {.error: "This module only works on the ECMAScript platform".} +when not defined(js) and not defined(Nimdoc): + {.error: "This module only works on the JavaScript platform".} type TEventHandlers* {.importc.} = object of TObject diff --git a/lib/lgpl.txt b/lib/lgpl.txt old mode 100755 new mode 100644 diff --git a/lib/nimbase.h b/lib/nimbase.h old mode 100755 new mode 100644 index 4735cbb4f..fcbf80fc3 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -1,7 +1,7 @@ /* Nimrod's Runtime Library - (c) Copyright 2012 Andreas Rumpf + (c) Copyright 2013 Andreas Rumpf See the file "copying.txt", included in this distribution, for details about the copyright. @@ -440,9 +440,9 @@ struct TFrame { volatile TFrame F; \ F.procname = proc; F.filename = file; F.line = 0; F.len = 0; nimFrame(&F); -#define nimfrs(proc, file, slots) \ +#define nimfrs(proc, file, slots, length) \ volatile struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; TVarSlot s[slots];} F; \ - F.procname = proc; F.filename = file; F.line = 0; F.len = slots; nimFrame((TFrame*)&F); + F.procname = proc; F.filename = file; F.line = 0; F.len = length; nimFrame((TFrame*)&F); #define nimln(n, file) \ F.line = n; F.filename = file; diff --git a/lib/nimrtl.nim b/lib/nimrtl.nim old mode 100755 new mode 100644 diff --git a/lib/nimrtl.nimrod.cfg b/lib/nimrtl.nimrod.cfg old mode 100755 new mode 100644 diff --git a/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim old mode 100755 new mode 100644 similarity index 100% rename from packages/docutils/highlite.nim rename to lib/packages/docutils/highlite.nim diff --git a/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim old mode 100755 new mode 100644 similarity index 98% rename from packages/docutils/rst.nim rename to lib/packages/docutils/rst.nim index b22bdf6ce..54c17367f --- a/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -180,11 +180,11 @@ proc getIndentAux(L: var TLexer, start: int): int = L.bufpos = pos # no need to set back buf proc getIndent(L: var TLexer, tok: var TToken) = - inc(L.line) - tok.line = L.line tok.col = 0 tok.kind = tkIndent # skip the newline (but include it in the token!) tok.ival = getIndentAux(L, L.bufpos) + inc L.line + tok.line = L.line L.col = tok.ival tok.ival = max(tok.ival - L.baseIndent, 0) tok.symbol = "\n" & repeatChar(tok.ival) @@ -220,21 +220,26 @@ proc rawGetTok(L: var TLexer, tok: var TToken) = inc(L.col) tok.col = max(tok.col - L.baseIndent, 0) -proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq) = +proc getTokens(buffer: string, skipPounds: bool, tokens: var TTokenSeq): int = var L: TLexer var length = len(tokens) L.buf = cstring(buffer) - L.line = 1 # skip UTF-8 BOM + L.line = 0 # skip UTF-8 BOM if (L.buf[0] == '\xEF') and (L.buf[1] == '\xBB') and (L.buf[2] == '\xBF'): inc(L.bufpos, 3) L.skipPounds = skipPounds if skipPounds: - if L.buf[L.bufpos] == '#': inc(L.bufpos) - if L.buf[L.bufpos] == '#': inc(L.bufpos) + if L.buf[L.bufpos] == '#': + inc(L.bufpos) + inc(result) + if L.buf[L.bufpos] == '#': + inc(L.bufpos) + inc(result) L.baseIndent = 0 while L.buf[L.bufpos] == ' ': inc(L.bufpos) inc(L.baseIndent) + inc(result) while true: inc(length) setlen(tokens, length) @@ -314,7 +319,7 @@ proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string) = p.col + p.tok[p.idx].col, msgKind, arg) proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string, line, col: int) = - p.s.msgHandler(p.filename, p.line + line, + p.s.msgHandler(p.filename, p.line + line, p.col + col, msgKind, arg) proc rstMessage(p: TRstParser, msgKind: TMsgKind) = @@ -602,7 +607,8 @@ proc parsePostfix(p: var TRstParser, n: PRstNode): PRstNode = proc matchVerbatim(p: TRstParser, start: int, expr: string): int = result = start var j = 0 - while j < expr.len and continuesWith(expr, p.tok[result].symbol, j): + while j < expr.len and result < p.tok.len and + continuesWith(expr, p.tok[result].symbol, j): inc j, p.tok[result].symbol.len inc result if j < expr.len: result = 0 @@ -691,6 +697,7 @@ proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string, let line = p.tok[p.idx].line col = p.tok[p.idx].col + inc p.idx while true: case p.tok[p.idx].kind of tkPunct: @@ -709,7 +716,7 @@ proc parseUntil(p: var TRstParser, father: PRstNode, postfix: string, add(father, newRstNode(rnLeaf, " ")) inc(p.idx) if p.tok[p.idx].kind == tkIndent: - rstMessage(p, meExpected, postfix) + rstMessage(p, meExpected, postfix, line, col) break of tkWhite: add(father, newRstNode(rnLeaf, " ")) @@ -750,17 +757,14 @@ proc parseInline(p: var TRstParser, father: PRstNode) = case p.tok[p.idx].kind of tkPunct: if isInlineMarkupStart(p, "***"): - inc(p.idx) var n = newRstNode(rnTripleEmphasis) parseUntil(p, n, "***", true) add(father, n) elif isInlineMarkupStart(p, "**"): - inc(p.idx) var n = newRstNode(rnStrongEmphasis) parseUntil(p, n, "**", true) add(father, n) elif isInlineMarkupStart(p, "*"): - inc(p.idx) var n = newRstNode(rnEmphasis) parseUntil(p, n, "*", true) add(father, n) @@ -768,18 +772,15 @@ proc parseInline(p: var TRstParser, father: PRstNode) = inc(p.idx) add(father, parseMarkdownCodeblock(p)) elif isInlineMarkupStart(p, "``"): - inc(p.idx) var n = newRstNode(rnInlineLiteral) parseUntil(p, n, "``", false) add(father, n) elif isInlineMarkupStart(p, "`"): - inc(p.idx) var n = newRstNode(rnInterpretedText) parseUntil(p, n, "`", true) n = parsePostfix(p, n) add(father, n) elif isInlineMarkupStart(p, "|"): - inc(p.idx) var n = newRstNode(rnSubstitutionReferences) parseUntil(p, n, "|", false) add(father, n) @@ -872,7 +873,6 @@ proc parseSection(p: var TRstParser, result: PRstNode) proc parseField(p: var TRstParser): PRstNode = result = newRstNode(rnField) var col = p.tok[p.idx].col - inc(p.idx) # skip : var fieldname = newRstNode(rnFieldname) parseUntil(p, fieldname, ":", false) var fieldbody = newRstNode(rnFieldbody) @@ -1155,7 +1155,7 @@ proc parseSimpleTable(p: var TRstParser): PRstNode = q.col = cols[j] q.line = line - 1 q.filename = p.filename - getTokens(row[j], false, q.tok) + q.col += getTokens(row[j], false, q.tok) b = newRstNode(rnTableDataCell) add(b, parseDoc(q)) add(a, b) @@ -1458,11 +1458,11 @@ proc dirInclude(p: var TRstParser): PRstNode = if getFieldValue(n, "literal") != "": result = newRstNode(rnLiteralBlock) add(result, newRstNode(rnLeaf, readFile(path))) - else: + else: var q: TRstParser initParser(q, p.s) q.filename = filename - getTokens(readFile(path), false, q.tok) + q.col += getTokens(readFile(path), false, q.tok) # workaround a GCC bug; more like the interior pointer bug? #if find(q.tok[high(q.tok)].symbol, "\0\x01\x02") > 0: # InternalError("Too many binary zeros in include file") @@ -1633,7 +1633,6 @@ proc rstParse*(text, filename: string, initParser(p, newSharedState(options, findFile, msgHandler)) p.filename = filename p.line = line - p.col = column - getTokens(text, roSkipPounds in options, p.tok) + p.col = column + getTokens(text, roSkipPounds in options, p.tok) result = resolveSubs(p, parseDoc(p)) hasToc = p.hasToc diff --git a/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim similarity index 100% rename from packages/docutils/rstast.nim rename to lib/packages/docutils/rstast.nim diff --git a/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim similarity index 100% rename from packages/docutils/rstgen.nim rename to lib/packages/docutils/rstgen.nim diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim old mode 100755 new mode 100644 diff --git a/lib/prelude.nim b/lib/prelude.nim old mode 100755 new mode 100644 diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim old mode 100755 new mode 100644 diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index be375a1a1..88e3e848f 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -8,7 +8,8 @@ import sockets, os -## This module implements an asynchronous event loop for sockets. +## This module implements an asynchronous event loop together with asynchronous sockets +## which use this event loop. ## It is akin to Python's asyncore module. Many modules that use sockets ## have an implementation for this module, those modules should all have a ## ``register`` function which you should use to add the desired objects to a @@ -125,7 +126,7 @@ type handleTask*: proc (s: PAsyncSocket) {.closure.} - lineBuffer: TaintedString ## Temporary storage for ``recvLine`` + lineBuffer: TaintedString ## Temporary storage for ``readLine`` sendBuffer: string ## Temporary storage for ``send`` sslNeedAccept: bool proto: TProtocol @@ -435,7 +436,8 @@ proc delHandleWrite*(s: PAsyncSocket) = ## Removes the ``handleWrite`` event handler on ``s``. s.handleWrite = nil -proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool = +{.push warning[deprecated]: off.} +proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool {.deprecated.} = ## Behaves similar to ``sockets.recvLine``, however it handles non-blocking ## sockets properly. This function guarantees that ``line`` is a full line, ## if this function can only retrieve some data; it will save this data and @@ -443,6 +445,9 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool = ## ## Unlike ``sockets.recvLine`` this function will raise an EOS or ESSL ## exception if an error occurs. + ## + ## **Deprecated since version 0.9.2**: This function has been deprecated in + ## favour of readLine. setLen(line.string, 0) var dataReceived = "".TaintedString var ret = s.socket.recvLineAsync(dataReceived) @@ -463,6 +468,37 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool = of RecvFail: s.SocketError(async = true) result = false +{.pop.} + +proc readLine*(s: PAsyncSocket, line: var TaintedString): bool = + ## Behaves similar to ``sockets.readLine``, however it handles non-blocking + ## sockets properly. This function guarantees that ``line`` is a full line, + ## if this function can only retrieve some data; it will save this data and + ## add it to the result when a full line is retrieved, when this happens + ## False will be returned. True will only be returned if a full line has been + ## retrieved or the socket has been disconnected in which case ``line`` will + ## be set to "". + ## + ## This function will raise an EOS exception when a socket error occurs. + setLen(line.string, 0) + var dataReceived = "".TaintedString + var ret = s.socket.readLineAsync(dataReceived) + case ret + of ReadFullLine: + if s.lineBuffer.len > 0: + string(line).add(s.lineBuffer.string) + setLen(s.lineBuffer.string, 0) + string(line).add(dataReceived.string) + if string(line) == "": + line = "\c\L".TaintedString + result = true + of ReadPartialLine: + string(s.lineBuffer).add(dataReceived.string) + result = false + of ReadNone: + result = false + of ReadDisconnected: + result = true proc send*(sock: PAsyncSocket, data: string) = ## Sends ``data`` to socket ``sock``. This is basically a nicer implementation @@ -599,7 +635,7 @@ when isMainModule: proc testRead(s: PAsyncSocket, no: int) = echo("Reading! " & $no) var data = "" - if not s.recvLine(data): + if not s.readLine(data): OSError() if data == "": echo("Closing connection. " & $no) diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim old mode 100755 new mode 100644 diff --git a/lib/pure/browsers.nim b/lib/pure/browsers.nim old mode 100755 new mode 100644 diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim old mode 100755 new mode 100644 diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim old mode 100755 new mode 100644 diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim old mode 100755 new mode 100644 diff --git a/lib/pure/collections/queues.nim b/lib/pure/collections/queues.nim old mode 100755 new mode 100644 diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 298e7f27e..c2d638a8c 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -117,7 +117,7 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] = ## assert f2 == @["yellow"] accumulateResult(filter(seq1, pred)) -template filterIt*(seq1, pred: expr): expr {.immediate, dirty.} = +template filterIt*(seq1, pred: expr): expr {.immediate.} = ## Returns a new sequence with all the items that fulfilled the predicate. ## ## Unlike the `proc` version, the predicate needs to be an expression using @@ -128,9 +128,12 @@ template filterIt*(seq1, pred: expr): expr {.immediate, dirty.} = ## let ## temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] ## acceptable = filterIt(temperatures, it < 50 and it > -10) + ## notAcceptable = filterIt(temperatures, it > 50 or it < -10) ## assert acceptable == @[-2.0, 24.5, 44.31] + ## assert notAcceptable == @[-272.15, 99.9, -113.44] var result {.gensym.}: type(seq1) = @[] - for it in items(seq1): + for internalit in items(seq1): + let it {.inject.} = internalit if pred: result.add(it) result @@ -151,64 +154,163 @@ template toSeq*(iter: expr): expr {.immediate.} = for x in iter: add(result, x) result +template foldl*(sequence, operation: expr): expr = + ## Template to fold a sequence from left to right, returning the accumulation. + ## + ## The sequence is required to have at least a single element. Debug versions + ## of your program will assert in this situation but release versions will + ## happily go ahead. If the sequence has a single element it will be returned + ## without applying ``operation``. + ## + ## The ``operation`` parameter should be an expression which uses the + ## variables ``a`` and ``b`` for each step of the fold. Since this is a left + ## fold, for non associative binary operations like substraction think that + ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) - + ## 3). Example: + ## + ## .. code-block:: nimrod + ## let + ## numbers = @[5, 9, 11] + ## addition = foldl(numbers, a + b) + ## substraction = foldl(numbers, a - b) + ## multiplication = foldl(numbers, a * b) + ## words = @["nim", "rod", "is", "cool"] + ## concatenation = foldl(words, a & b) + ## assert addition == 25, "Addition is (((5)+9)+11)" + ## assert substraction == -15, "Substraction is (((5)-9)-11)" + ## assert multiplication == 495, "Multiplication is (((5)*9)*11)" + ## assert concatenation == "nimrodiscool" + assert sequence.len > 0, "Can't fold empty sequences" + var result {.gensym.}: type(sequence[0]) + result = sequence[0] + for i in countup(1, sequence.len - 1): + let + a {.inject.} = result + b {.inject.} = sequence[i] + result = operation + result + +template foldr*(sequence, operation: expr): expr = + ## Template to fold a sequence from right to left, returning the accumulation. + ## + ## The sequence is required to have at least a single element. Debug versions + ## of your program will assert in this situation but release versions will + ## happily go ahead. If the sequence has a single element it will be returned + ## without applying ``operation``. + ## + ## The ``operation`` parameter should be an expression which uses the + ## variables ``a`` and ``b`` for each step of the fold. Since this is a right + ## fold, for non associative binary operations like substraction think that + ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 - + ## (3))). Example: + ## + ## .. code-block:: nimrod + ## let + ## numbers = @[5, 9, 11] + ## addition = foldr(numbers, a + b) + ## substraction = foldr(numbers, a - b) + ## multiplication = foldr(numbers, a * b) + ## words = @["nim", "rod", "is", "cool"] + ## concatenation = foldr(words, a & b) + ## assert addition == 25, "Addition is (5+(9+(11)))" + ## assert substraction == 7, "Substraction is (5-(9-(11)))" + ## assert multiplication == 495, "Multiplication is (5*(9*(11)))" + ## assert concatenation == "nimrodiscool" + assert sequence.len > 0, "Can't fold empty sequences" + var result {.gensym.}: type(sequence[0]) + result = sequence[sequence.len - 1] + for i in countdown(sequence.len - 2, 0): + let + a {.inject.} = sequence[i] + b {.inject.} = result + result = operation + result + when isMainModule: import strutils - proc toStr(x: int): string {.procvar.} = $x - # concat test - let - s1 = @[1, 2, 3] - s2 = @[4, 5] - s3 = @[6, 7] - total = concat(s1, s2, s3) - assert total == @[1, 2, 3, 4, 5, 6, 7] + block: # concat test + let + s1 = @[1, 2, 3] + s2 = @[4, 5] + s3 = @[6, 7] + total = concat(s1, s2, s3) + assert total == @[1, 2, 3, 4, 5, 6, 7] - # duplicates test - let - dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] - dup2 = @["a", "a", "c", "d", "d"] - unique1 = distnct(dup1) - unique2 = distnct(dup2) - assert unique1 == @[1, 3, 4, 2, 8] - assert unique2 == @["a", "c", "d"] + block: # duplicates test + let + dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] + dup2 = @["a", "a", "c", "d", "d"] + unique1 = distnct(dup1) + unique2 = distnct(dup2) + assert unique1 == @[1, 3, 4, 2, 8] + assert unique2 == @["a", "c", "d"] - # zip test - let - short = @[1, 2, 3] - long = @[6, 5, 4, 3, 2, 1] - words = @["one", "two", "three"] - zip1 = zip(short, long) - zip2 = zip(short, words) - assert zip1 == @[(1, 6), (2, 5), (3, 4)] - assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] - assert zip1[2].b == 4 - assert zip2[2].b == "three" + block: # zip test + let + short = @[1, 2, 3] + long = @[6, 5, 4, 3, 2, 1] + words = @["one", "two", "three"] + zip1 = zip(short, long) + zip2 = zip(short, words) + assert zip1 == @[(1, 6), (2, 5), (3, 4)] + assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] + assert zip1[2].b == 4 + assert zip2[2].b == "three" - # filter proc test - let - colors = @["red", "yellow", "black"] - f1 = filter(colors, proc(x: string): bool = x.len < 6) - f2 = filter(colors) do (x: string) -> bool : x.len > 5 - assert f1 == @["red", "black"] - assert f2 == @["yellow"] + block: # filter proc test + let + colors = @["red", "yellow", "black"] + f1 = filter(colors, proc(x: string): bool = x.len < 6) + f2 = filter(colors) do (x: string) -> bool : x.len > 5 + assert f1 == @["red", "black"] + assert f2 == @["yellow"] - # filter iterator test - let numbers = @[1, 4, 5, 8, 9, 7, 4] - for n in filter(numbers, proc (x: int): bool = x mod 2 == 0): - echo($n) - # echoes 4, 8, 4 in separate lines + block: # filter iterator test + let numbers = @[1, 4, 5, 8, 9, 7, 4] + for n in filter(numbers, proc (x: int): bool = x mod 2 == 0): + echo($n) + # echoes 4, 8, 4 in separate lines - # filterIt test - let - temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] - acceptable = filterIt(temperatures, it < 50 and it > -10) - assert acceptable == @[-2.0, 24.5, 44.31] + block: # filterIt test + let + temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] + acceptable = filterIt(temperatures, it < 50 and it > -10) + notAcceptable = filterIt(temperatures, it > 50 or it < -10) + assert acceptable == @[-2.0, 24.5, 44.31] + assert notAcceptable == @[-272.15, 99.9, -113.44] - # toSeq test - let - numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: - if x mod 2 == 1: - result = true) - assert odd_numbers == @[1, 3, 5, 7, 9] + block: # toSeq test + let + numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: + if x mod 2 == 1: + result = true) + assert odd_numbers == @[1, 3, 5, 7, 9] + + block: # foldl tests + let + numbers = @[5, 9, 11] + addition = foldl(numbers, a + b) + substraction = foldl(numbers, a - b) + multiplication = foldl(numbers, a * b) + words = @["nim", "rod", "is", "cool"] + concatenation = foldl(words, a & b) + assert addition == 25, "Addition is (((5)+9)+11)" + assert substraction == -15, "Substraction is (((5)-9)-11)" + assert multiplication == 495, "Multiplication is (((5)*9)*11)" + assert concatenation == "nimrodiscool" + + block: # foldr tests + let + numbers = @[5, 9, 11] + addition = foldr(numbers, a + b) + substraction = foldr(numbers, a - b) + multiplication = foldr(numbers, a * b) + words = @["nim", "rod", "is", "cool"] + concatenation = foldr(words, a & b) + assert addition == 25, "Addition is (5+(9+(11)))" + assert substraction == 7, "Substraction is (5-(9-(11)))" + assert multiplication == 495, "Multiplication is (5*(9*(11)))" + assert concatenation == "nimrodiscool" echo "Finished doc tests" diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim old mode 100755 new mode 100644 diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim old mode 100755 new mode 100644 index 4290af08a..02d099c1f --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -159,8 +159,11 @@ proc del*[A, B](t: var TTable[A, B], key: A) = dec(t.counter) proc initTable*[A, B](initialSize=64): TTable[A, B] = - ## creates a new hash table that is empty. `initialSize` needs to be - ## a power of two. + ## creates a new hash table that is empty. + ## + ## `initialSize` needs to be a power of two. If you need to accept runtime + ## values for this you could use the ``nextPowerOfTwo`` proc from the + ## `math `_ module. assert isPowerOfTwo(initialSize) result.counter = 0 newSeq(result.data, initialSize) @@ -290,8 +293,11 @@ proc add*[A, B](t: var TOrderedTable[A, B], key: A, val: B) = AddImpl() proc initOrderedTable*[A, B](initialSize=64): TOrderedTable[A, B] = - ## creates a new ordered hash table that is empty. `initialSize` needs to be - ## a power of two. + ## creates a new ordered hash table that is empty. + ## + ## `initialSize` needs to be a power of two. If you need to accept runtime + ## values for this you could use the ``nextPowerOfTwo`` proc from the + ## `math `_ module. assert isPowerOfTwo(initialSize) result.counter = 0 result.first = -1 @@ -437,8 +443,11 @@ proc `[]=`*[A](t: var TCountTable[A], key: A, val: int) = PutImpl() proc initCountTable*[A](initialSize=64): TCountTable[A] = - ## creates a new count table that is empty. `initialSize` needs to be - ## a power of two. + ## creates a new count table that is empty. + ## + ## `initialSize` needs to be a power of two. If you need to accept runtime + ## values for this you could use the ``nextPowerOfTwo`` proc from the + ## `math `_ module. assert isPowerOfTwo(initialSize) result.counter = 0 newSeq(result.data, initialSize) diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim old mode 100755 new mode 100644 diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim old mode 100755 new mode 100644 diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim old mode 100755 new mode 100644 diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim old mode 100755 new mode 100644 diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim old mode 100755 new mode 100644 diff --git a/lib/pure/ftpclient.nim b/lib/pure/ftpclient.nim index b61793866..7893eafa0 100644 --- a/lib/pure/ftpclient.nim +++ b/lib/pure/ftpclient.nim @@ -124,7 +124,7 @@ template blockingOperation(sock: TSocket, body: stmt) {.immediate.} = proc expectReply(ftp: PFTPClient): TaintedString = result = TaintedString"" blockingOperation(ftp.getCSock()): - if not ftp.getCSock().recvLine(result): setLen(result.string, 0) + ftp.getCSock().readLine(result) proc send*(ftp: PFTPClient, m: string): TaintedString = ## Send a message to the server, and wait for a primary reply. @@ -280,19 +280,18 @@ proc getLines(ftp: PFTPClient, async: bool = false): bool = if ftp.dsockConnected: var r = TaintedString"" if ftp.isAsync: - if ftp.asyncDSock.recvLine(r): + if ftp.asyncDSock.readLine(r): if r.string == "": ftp.dsockConnected = false else: ftp.job.lines.add(r.string & "\n") else: assert(not async) - if ftp.dsock.recvLine(r): - if r.string == "": - ftp.dsockConnected = false - else: - ftp.job.lines.add(r.string & "\n") - else: OSError() + ftp.dsock.readLine(r) + if r.string == "": + ftp.dsockConnected = false + else: + ftp.job.lines.add(r.string & "\n") if not async: var readSocks: seq[TSocket] = @[ftp.getCSock()] diff --git a/lib/pure/gentabs.nim b/lib/pure/gentabs.nim old mode 100755 new mode 100644 diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim old mode 100755 new mode 100644 index ac6d3c3ce..cf0777ff7 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -35,7 +35,7 @@ proc `!$`*(h: THash): THash {.inline.} = proc hashData*(Data: Pointer, Size: int): THash = ## hashes an array of bytes of size `size` var h: THash = 0 - when defined(ecmascript): + when defined(js): var p: cstring asm """`p` = `Data`;""" else: @@ -48,12 +48,12 @@ proc hashData*(Data: Pointer, Size: int): THash = Dec(s) result = !$h -when defined(ecmascript): +when defined(js): var objectID = 0 proc hash*(x: Pointer): THash {.inline.} = ## efficient hashing of pointers - when defined(ecmascript): + when defined(js): asm """ if (typeof `x` == "object") { if ("_NimID" in `x`) diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim old mode 100755 new mode 100644 index 75af2c892..8d90f8589 --- a/lib/pure/htmlgen.nim +++ b/lib/pure/htmlgen.nim @@ -34,7 +34,10 @@ const proc getIdent(e: PNimrodNode): string {.compileTime.} = case e.kind of nnkIdent: result = normalize($e.ident) - of nnkAccQuoted: result = getIdent(e[0]) + of nnkAccQuoted: + result = getIdent(e[0]) + for i in 1 .. e.len-1: + result.add getIdent(e[i]) else: error("cannot extract identifier from node: " & toStrLit(e).strVal) proc delete[T](s: var seq[T], attr: T): bool = @@ -478,4 +481,5 @@ macro `var`*(e: expr): expr {.immediate.} = when isMainModule: var nim = "Nimrod" echo h1(a(href="http://nimrod-code.org", nim)) + echo form(action="test", `accept-charset` = "Content-Type") diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim old mode 100755 new mode 100644 diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim old mode 100755 new mode 100644 index 5be4af8a4..ab6cb04a9 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -50,6 +50,18 @@ ## any of the functions a url with the ``https`` schema, for example: ## ``https://github.com/``, you also have to compile with ``ssl`` defined like so: ## ``nimrod c -d:ssl ...``. +## +## Timeouts +## ======== +## Currently all functions support an optional timeout, by default the timeout is set to +## `-1` which means that the function will never time out. The timeout is +## measured in miliseconds, once it is set any call on a socket which may +## block will be susceptible to this timeout, however please remember that the +## function as a whole can take longer than the specified timeout, only +## individual internal calls on the socket are affected. In practice this means +## 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. import sockets, strutils, parseurl, parseutils, strtabs @@ -68,6 +80,8 @@ type ## and ``postContent`` proc, ## when the server returns an error +const defUserAgent* = "Nimrod httpclient/0.1" + proc httpError(msg: string) = var e: ref EInvalidProtocol new(e) @@ -80,49 +94,48 @@ proc fileError(msg: string) = e.msg = msg raise e -proc parseChunks(s: TSocket): string = +proc parseChunks(s: TSocket, timeout: int): string = result = "" var ri = 0 while true: var chunkSizeStr = "" var chunkSize = 0 - if s.recvLine(chunkSizeStr): - var i = 0 - if chunkSizeStr == "": - httpError("Server terminated connection prematurely") - while true: - case chunkSizeStr[i] - of '0'..'9': - chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0')) - of 'a'..'f': - chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10) - of 'A'..'F': - chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10) - of '\0': - break - of ';': - # http://tools.ietf.org/html/rfc2616#section-3.6.1 - # We don't care about chunk-extensions. - break - else: - httpError("Invalid chunk size: " & chunkSizeStr) - inc(i) + s.readLine(chunkSizeStr, timeout) + var i = 0 + if chunkSizeStr == "": + httpError("Server terminated connection prematurely") + while true: + case chunkSizeStr[i] + of '0'..'9': + chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0')) + of 'a'..'f': + chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10) + of 'A'..'F': + chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10) + of '\0': + break + of ';': + # http://tools.ietf.org/html/rfc2616#section-3.6.1 + # We don't care about chunk-extensions. + break + else: + httpError("Invalid chunk size: " & chunkSizeStr) + inc(i) if chunkSize <= 0: break result.setLen(ri+chunkSize) var bytesRead = 0 while bytesRead != chunkSize: - let ret = recv(s, addr(result[ri]), chunkSize-bytesRead) + let ret = recv(s, addr(result[ri]), chunkSize-bytesRead, timeout) ri += ret bytesRead += ret - s.skip(2) # Skip \c\L + s.skip(2, timeout) # Skip \c\L # Trailer headers will only be sent if the request specifies that we want # them: http://tools.ietf.org/html/rfc2616#section-3.6.1 -proc parseBody(s: TSocket, - headers: PStringTable): string = +proc parseBody(s: TSocket, headers: PStringTable, timeout: int): string = result = "" if headers["Transfer-Encoding"] == "chunked": - result = parseChunks(s) + result = parseChunks(s, timeout) else: # -REGION- Content-Length # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.3 @@ -133,7 +146,7 @@ proc parseBody(s: TSocket, var received = 0 while true: if received >= length: break - let r = s.recv(addr(result[received]), length-received) + let r = s.recv(addr(result[received]), length-received, timeout) if r == 0: break received += r if received != length: @@ -148,12 +161,12 @@ proc parseBody(s: TSocket, var buf = "" while True: buf = newString(4000) - let r = s.recv(addr(buf[0]), 4000) + let r = s.recv(addr(buf[0]), 4000, timeout) if r == 0: break buf.setLen(r) result.add(buf) -proc parseResponse(s: TSocket, getBody: bool): TResponse = +proc parseResponse(s: TSocket, getBody: bool, timeout: int): TResponse = var parsedStatus = false var linei = 0 var fullyRead = false @@ -162,41 +175,42 @@ proc parseResponse(s: TSocket, getBody: bool): TResponse = while True: line = "" linei = 0 - if s.recvLine(line): - if line == "": break # We've been disconnected. - if line == "\c\L": - fullyRead = true - break - if not parsedStatus: - # Parse HTTP version info and status code. - var le = skipIgnoreCase(line, "HTTP/", linei) - if le <= 0: httpError("invalid http version") - inc(linei, le) - le = skipIgnoreCase(line, "1.1", linei) - if le > 0: result.version = "1.1" - else: - le = skipIgnoreCase(line, "1.0", linei) - if le <= 0: httpError("unsupported http version") - result.version = "1.0" - inc(linei, le) - # Status code - linei.inc skipWhitespace(line, linei) - result.status = line[linei .. -1] - parsedStatus = true + s.readLine(line, timeout) + if line == "": break # We've been disconnected. + if line == "\c\L": + fullyRead = true + break + if not parsedStatus: + # Parse HTTP version info and status code. + var le = skipIgnoreCase(line, "HTTP/", linei) + if le <= 0: httpError("invalid http version") + inc(linei, le) + le = skipIgnoreCase(line, "1.1", linei) + if le > 0: result.version = "1.1" else: - # Parse headers - var name = "" - var le = parseUntil(line, name, ':', linei) - if le <= 0: httpError("invalid headers") - inc(linei, le) - if line[linei] != ':': httpError("invalid headers") - inc(linei) # Skip : - linei += skipWhitespace(line, linei) - - result.headers[name] = line[linei.. -1] - if not fullyRead: httpError("Connection was closed before full request has been made") + le = skipIgnoreCase(line, "1.0", linei) + if le <= 0: httpError("unsupported http version") + result.version = "1.0" + inc(linei, le) + # Status code + linei.inc skipWhitespace(line, linei) + result.status = line[linei .. -1] + parsedStatus = true + else: + # Parse headers + var name = "" + var le = parseUntil(line, name, ':', linei) + if le <= 0: httpError("invalid headers") + inc(linei, le) + if line[linei] != ':': httpError("invalid headers") + inc(linei) # Skip : + linei += skipWhitespace(line, linei) + + result.headers[name] = line[linei.. -1] + if not fullyRead: + httpError("Connection was closed before full request has been made") if getBody: - result.body = parseBody(s, result.headers) + result.body = parseBody(s, result.headers, timeout) else: result.body = "" @@ -227,9 +241,12 @@ else: proc request*(url: string, httpMethod = httpGET, extraHeaders = "", body = "", - sslContext: PSSLContext = defaultSSLContext): TResponse = + sslContext: PSSLContext = defaultSSLContext, + timeout = -1, userAgent = defUserAgent): 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 headers = substr($httpMethod, len("http")) headers.add(" /" & r.path & r.query) @@ -237,25 +254,32 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "", headers.add(" HTTP/1.1\c\L") add(headers, "Host: " & r.hostname & "\c\L") + if userAgent != "": + add(headers, "User-Agent: " & userAgent & "\c\L") add(headers, extraHeaders) add(headers, "\c\L") - + var s = socket() var port = TPort(80) if r.scheme == "https": when defined(ssl): sslContext.wrapSocket(s) else: - raise newException(EHttpRequestErr, "SSL support was not compiled in. Cannot connect over SSL.") + raise newException(EHttpRequestErr, + "SSL support is not available. Cannot connect over SSL.") port = TPort(443) if r.port != "": port = TPort(r.port.parseInt) - s.connect(r.hostname, port) + + if timeout == -1: + s.connect(r.hostname, port) + else: + s.connect(r.hostname, port, timeout) s.send(headers) if body != "": s.send(body) - result = parseResponse(s, httpMethod != httpHEAD) + result = parseResponse(s, httpMethod != httpHEAD, timeout) s.close() proc redirection(status: string): bool = @@ -263,56 +287,94 @@ proc redirection(status: string): bool = for i in items(redirectionNRs): if status.startsWith(i): return True + +proc getNewLocation(lastUrl: string, headers: PStringTable): string = + result = headers["Location"] + if result == "": httpError("location header expected") + # Relative URLs. (Not part of the spec, but soon will be.) + let r = parseURL(result) + if r.hostname == "" and r.path != "": + let origParsed = parseURL(lastUrl) + result = origParsed.hostname & "/" & r.path -proc get*(url: string, maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext): TResponse = +proc get*(url: string, extraHeaders = "", maxRedirects = 5, + sslContext: PSSLContext = defaultSSLContext, + timeout = -1, userAgent = defUserAgent): TResponse = ## | GETs the ``url`` and returns a ``TResponse`` object ## | This proc also handles redirection - result = request(url) + ## | 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) + var lastURL = url for i in 1..maxRedirects: if result.status.redirection(): - var locationHeader = result.headers["Location"] - if locationHeader == "": httpError("location header expected") - result = request(locationHeader, sslContext = sslContext) + let redirectTo = getNewLocation(lastURL, result.headers) + result = request(redirectTo, httpGET, extraHeaders, "", sslContext, + timeout, userAgent) + lastUrl = redirectTo -proc getContent*(url: string, sslContext: PSSLContext = defaultSSLContext): string = +proc getContent*(url: string, extraHeaders = "", maxRedirects = 5, + sslContext: PSSLContext = defaultSSLContext, + timeout = -1, userAgent = defUserAgent): string = ## | GETs the body and returns it as a string. ## | Raises exceptions for the status codes ``4xx`` and ``5xx`` - var r = get(url, sslContext = sslContext) + ## | 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) if r.status[0] in {'4','5'}: raise newException(EHTTPRequestErr, r.status) else: return r.body -proc post*(url: string, extraHeaders = "", body = "", - maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext): TResponse = +proc post*(url: string, extraHeaders = "", body = "", + maxRedirects = 5, + sslContext: PSSLContext = defaultSSLContext, + timeout = -1, userAgent = defUserAgent): TResponse = ## | POSTs ``body`` to the ``url`` and returns a ``TResponse`` object. ## | This proc adds the necessary Content-Length header. ## | 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. var xh = extraHeaders & "Content-Length: " & $len(body) & "\c\L" - result = request(url, httpPOST, xh, body, sslContext) + result = request(url, httpPOST, xh, body, sslContext, timeout, userAgent) + var lastUrl = "" for i in 1..maxRedirects: if result.status.redirection(): - var locationHeader = result.headers["Location"] - if locationHeader == "": httpError("location header expected") + let redirectTo = getNewLocation(lastURL, result.headers) var meth = if result.status != "307": httpGet else: httpPost - result = request(locationHeader, meth, xh, body) + result = request(redirectTo, meth, xh, body, sslContext, timeout, + userAgent) + lastUrl = redirectTo proc postContent*(url: string, extraHeaders = "", body = "", - sslContext: PSSLContext = defaultSSLContext): string = + maxRedirects = 5, + sslContext: PSSLContext = defaultSSLContext, + timeout = -1, userAgent = defUserAgent): string = ## | POSTs ``body`` to ``url`` and returns the response's body as a string ## | Raises exceptions for the status codes ``4xx`` and ``5xx`` - var r = post(url, extraHeaders, body) + ## | 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) if r.status[0] in {'4','5'}: raise newException(EHTTPRequestErr, r.status) else: return r.body proc downloadFile*(url: string, outputFilename: string, - sslContext: PSSLContext = defaultSSLContext) = - ## Downloads ``url`` and saves it to ``outputFilename`` + sslContext: PSSLContext = defaultSSLContext, + timeout = -1, userAgent = defUserAgent) = + ## | 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)) + f.write(getContent(url, sslContext = sslContext, timeout = timeout, + userAgent = userAgent)) f.close() else: fileError("Unable to open file") diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim old mode 100755 new mode 100644 index ce816b7d4..61399c2d8 --- a/lib/pure/httpserver.nim +++ b/lib/pure/httpserver.nim @@ -121,7 +121,7 @@ proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) = var buf = TaintedString"" var dataAvail = false while dataAvail: - dataAvail = recvLine(client, buf) + dataAvail = recvLine(client, buf) # TODO: This is incorrect. var L = toLower(buf.string) if L.startsWith("content-length:"): var i = len("content-length:") @@ -256,7 +256,7 @@ proc next*(s: var TServer) = s.headers = newStringTable(modeCaseInsensitive) #headers(s.client, "") var data = "" - while not s.client.recvLine(data): nil + s.client.readLine(data) if data == "": # Socket disconnected s.client.close() @@ -264,21 +264,21 @@ proc next*(s: var TServer) = return var header = "" while true: - if s.client.recvLine(header): - if header == "\c\L": break - if header != "": - var i = 0 - var key = "" - var value = "" - i = header.parseUntil(key, ':') - inc(i) # skip : - i += header.skipWhiteSpace(i) - i += header.parseUntil(value, {'\c', '\L'}, i) - s.headers[key] = value - else: - s.client.close() - next(s) - return + s.client.readLine(header) + if header == "\c\L": break + if header != "": + var i = 0 + var key = "" + var value = "" + i = header.parseUntil(key, ':') + inc(i) # skip : + i += header.skipWhiteSpace(i) + i += header.parseUntil(value, {'\c', '\L'}, i) + s.headers[key] = value + else: + s.client.close() + next(s) + return var i = skipWhitespace(data) if skipIgnoreCase(data, "GET") > 0: @@ -382,27 +382,27 @@ proc nextAsync(s: PAsyncHTTPServer) = s.headers = newStringTable(modeCaseInsensitive) #headers(s.client, "") var data = "" - while not s.client.recvLine(data): nil + s.client.readLine(data) if data == "": # Socket disconnected s.client.close() return var header = "" while true: - if s.client.recvLine(header): - if header == "\c\L": break - if header != "": - var i = 0 - var key = "" - var value = "" - i = header.parseUntil(key, ':') - inc(i) # skip : - i += header.skipWhiteSpace(i) - i += header.parseUntil(value, {'\c', '\L'}, i) - s.headers[key] = value - else: - s.client.close() - return + s.client.readLine(header) # TODO: Very inefficient here. Prone to DOS. + if header == "\c\L": break + if header != "": + var i = 0 + var key = "" + var value = "" + i = header.parseUntil(key, ':') + inc(i) # skip : + i += header.skipWhiteSpace(i) + i += header.parseUntil(value, {'\c', '\L'}, i) + s.headers[key] = value + else: + s.client.close() + return var i = skipWhitespace(data) if skipIgnoreCase(data, "GET") > 0: diff --git a/lib/pure/irc.nim b/lib/pure/irc.nim index aa4e2d557..74ad7e26a 100644 --- a/lib/pure/irc.nim +++ b/lib/pure/irc.nim @@ -345,10 +345,10 @@ proc poll*(irc: PIRC, ev: var TIRCEvent, var socks = @[irc.sock] var ret = socks.select(timeout) if socks.len() == 0 and ret != 0: - if irc.sock.recvLine(line): - ev = irc.processLine(line.string) - result = true - + irc.sock.readLine(line) + ev = irc.processLine(line.string) + result = true + if processOther(irc, ev): result = true proc getLag*(irc: PIRC): float = @@ -380,7 +380,7 @@ proc handleConnect(s: PAsyncSocket, irc: PAsyncIRC) = proc handleRead(s: PAsyncSocket, irc: PAsyncIRC) = var line = "".TaintedString - var ret = s.recvLine(line) + var ret = s.readLine(line) if ret: if line == "": var ev: TIRCEvent diff --git a/lib/pure/json.nim b/lib/pure/json.nim old mode 100755 new mode 100644 diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim old mode 100755 new mode 100644 diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim old mode 100755 new mode 100644 diff --git a/lib/pure/math.nim b/lib/pure/math.nim old mode 100755 new mode 100644 index f9ab6d0f8..d65330c4a --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -10,7 +10,7 @@ ## Constructive mathematics is naturally typed. -- Simon Thompson ## ## Basic math routines for Nimrod. -## This module is available for the ECMAScript target. +## This module is available for the JavaScript target. {.push debugger:off .} # the user does not want to trace a part # of the standard library! @@ -50,7 +50,7 @@ proc classify*(x: float): TFloatClass = ## classifies a floating point value. Returns `x`'s class as specified by ## `TFloatClass`. - # ECMAScript and most C compilers have no classify: + # JavaScript and most C compilers have no classify: if x == 0.0: if 1.0/x == Inf: return fcZero @@ -138,15 +138,15 @@ when not defined(windows): proc randomize*() ## initializes the random number generator with a "random" - ## number, i.e. a tickcount. Note: Does nothing for the ECMAScript target, - ## as ECMAScript does not support this. + ## number, i.e. a tickcount. Note: Does nothing for the JavaScript target, + ## as JavaScript does not support this. proc randomize*(seed: int) ## initializes the random number generator with a specific seed. - ## Note: Does nothing for the ECMAScript target, - ## as ECMAScript does not support this. + ## Note: Does nothing for the JavaScript target, + ## as JavaScript does not support this. -when not defined(ECMAScript): +when not defined(JS): proc sqrt*(x: float): float {.importc: "sqrt", header: "".} ## computes the square root of `x`. @@ -310,7 +310,7 @@ proc standardDeviation*(s: TRunningStat): float = {.pop.} {.pop.} -when isMainModule and not defined(ECMAScript): +when isMainModule and not defined(JS): # Verifies random seed initialization. let seed = gettime(nil) randomize(seed) diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim old mode 100755 new mode 100644 diff --git a/lib/pure/os.nim b/lib/pure/os.nim old mode 100755 new mode 100644 index 01daf5ad6..433a79c5e --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -794,8 +794,11 @@ when not defined(ENOENT): var ENOENT {.importc, header: "".}: cint proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} = - ## Removes the `file`. If this fails, `EOS` is raised. This does not fail - ## if the file never existed in the first place. + ## Removes the `file`. + ## + ## If this fails, `EOS` is raised. This does not fail if the file never + ## existed in the first place. Despite the name of this proc you can also + ## call it on directories, but they will only be removed if they are empty. if cremove(file) != 0'i32 and errno != ENOENT: OSError() proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", @@ -1076,8 +1079,12 @@ proc rawRemoveDir(dir: string) = proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [ FWriteDir, FReadDir].} = ## Removes the directory `dir` including all subdirectories and files - ## in `dir` (recursively). If this fails, `EOS` is raised. This does not fail - ## if the directory never existed in the first place. + ## in `dir` (recursively). + ## + ## If this fails, `EOS` is raised. This does not fail if the directory never + ## existed in the first place. If you need non recursive directory removal + ## which fails when the directory contains files use the `removeFile` proc + ## instead. for kind, path in walkDir(dir): case kind of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path) @@ -1103,8 +1110,15 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} = ## The full path is created. If this fails, `EOS` is raised. It does **not** ## fail if the path already exists because for most usages this does not ## indicate an error. + var omitNext = false + when defined(doslike): + omitNext = isAbsolute(dir) for i in 1.. dir.len-1: - if dir[i] in {dirsep, altsep}: rawCreateDir(substr(dir, 0, i-1)) + if dir[i] in {dirsep, altsep}: + if omitNext: + omitNext = false + else: + rawCreateDir(substr(dir, 0, i-1)) rawCreateDir(dir) proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim old mode 100755 new mode 100644 diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim old mode 100755 new mode 100644 diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim old mode 100755 new mode 100644 diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim old mode 100755 new mode 100644 diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim old mode 100755 new mode 100644 diff --git a/lib/pure/parseurl.nim b/lib/pure/parseurl.nim old mode 100755 new mode 100644 diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim old mode 100755 new mode 100644 diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim old mode 100755 new mode 100644 diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim old mode 100755 new mode 100644 diff --git a/lib/pure/redis.nim b/lib/pure/redis.nim old mode 100755 new mode 100644 diff --git a/lib/pure/romans.nim b/lib/pure/romans.nim old mode 100755 new mode 100644 diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim old mode 100755 new mode 100644 diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim old mode 100755 new mode 100644 diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim old mode 100755 new mode 100644 index abf268942..6a3f65279 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -28,9 +28,6 @@ ## For SSL support this module relies on OpenSSL. If you want to ## enable SSL, compile with ``-d:ssl``. -when not defined(ssl): - {.error: "The SMTP module should be compiled with SSL support. Compile with -d:ssl."} - import sockets, strutils, strtabs, base64, os type @@ -54,16 +51,11 @@ proc debugSend(smtp: TSMTP, cmd: string) = proc debugRecv(smtp: var TSMTP): TaintedString = var line = TaintedString"" - var ret = False - ret = smtp.sock.recvLine(line) + smtp.sock.readLine(line) - if ret: - if smtp.debug: - echo("S:" & line.string) - return line - else: - OSError() - return TaintedString"" + if smtp.debug: + echo("S:" & line.string) + return line proc quitExcpt(smtp: TSMTP, msg: string) = smtp.debugSend("QUIT") diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim old mode 100755 new mode 100644 index f233e53c8..2d602c86e --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -1,18 +1,28 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf, Dominik Picheta # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## This module implements a simple portable type-safe sockets layer. +## This module implements portable sockets, it supports a mix of different types +## of sockets. Sockets are buffered by default meaning that data will be +## received in ``BufferSize`` (4000) sized chunks, buffering +## behaviour can be disabled by setting the ``buffered`` parameter when calling +## the ``socket`` function to `False`. Be aware that some functions may not yet +## support buffered sockets (mainly the recvFrom function). ## -## Most procedures raise EOS on error. +## Most procedures raise EOS on error, but some may return ``-1`` or a boolean +## ``False``. ## -## For OpenSSL support compile with ``-d:ssl``. When using SSL be aware that -## most functions will then raise ``ESSL`` on SSL errors. +## SSL is supported through the OpenSSL library. This support can be activated +## by compiling with the ``-d:ssl`` switch. When an SSL socket is used it will +## raise ESSL exceptions when SSL errors occur. +## +## Asynchronous sockets are supported, however a better alternative is to use +## the `asyncio `_ module. {.deadCodeElim: on.} @@ -68,6 +78,7 @@ type sslHasPeekChar: bool sslPeekChar: char of false: nil + nonblocking: bool TSocket* = ref TSocketImpl @@ -110,6 +121,9 @@ type TRecvLineResult* = enum ## result for recvLineAsync RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail + TReadLineResult* = enum ## result for readLineAsync + ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone + ETimeout* = object of ESynch proc newTSocket(fd: int32, isBuff: bool): TSocket = @@ -118,6 +132,7 @@ proc newTSocket(fd: int32, isBuff: bool): TSocket = result.isBuffered = isBuff if isBuff: result.currPos = 0 + result.nonblocking = false let InvalidSocket*: TSocket = nil ## invalid socket @@ -196,7 +211,7 @@ else: proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, protocol: TProtocol = IPPROTO_TCP, buffered = true): TSocket = - ## creates a new socket; returns `InvalidSocket` if an error occurs. + ## Creates a new socket; returns `InvalidSocket` if an error occurs. when defined(Windows): result = newTSocket(winlean.socket(ord(domain), ord(typ), ord(protocol)), buffered) else: @@ -711,10 +726,10 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {. sizeof(value).TSockLen) < 0'i32: OSError() -proc connect*(socket: TSocket, name: string, port = TPort(0), +proc connect*(socket: TSocket, address: string, port = TPort(0), af: TDomain = AF_INET) {.tags: [FReadIO].} = - ## Connects socket to ``name``:``port``. ``Name`` can be an IP address or a - ## host name. If ``name`` is a host name, this function will try each IP + ## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a + ## host name. If ``address`` is a host name, this function will try each IP ## of that host name. ``htons`` is already performed on ``port`` so you must ## not do it. ## @@ -724,8 +739,7 @@ proc connect*(socket: TSocket, name: string, port = TPort(0), hints.ai_family = toInt(af) hints.ai_socktype = toInt(SOCK_STREAM) hints.ai_protocol = toInt(IPPROTO_TCP) - gaiNim(name, port, hints, aiList) - + gaiNim(address, port, hints, aiList) # try all possibilities: var success = false var it = aiList @@ -758,7 +772,7 @@ proc connect*(socket: TSocket, name: string, port = TPort(0), when false: var s: TSockAddrIn - s.sin_addr.s_addr = inet_addr(name) + s.sin_addr.s_addr = inet_addr(address) s.sin_port = sockets.htons(int16(port)) when defined(windows): s.sin_family = toU16(ord(af)) @@ -891,6 +905,10 @@ proc hasDataBuffered*(s: TSocket): bool = if s.isBuffered: result = s.bufLen > 0 and s.currPos != s.bufLen + when defined(ssl): + if s.isSSL and not result: + result = s.sslHasPeekChar + proc checkBuffer(readfds: var seq[TSocket]): int = ## Checks the buffer of each socket in ``readfds`` to see whether there is data. ## Removes the sockets from ``readfds`` and returns the count of removed sockets. @@ -906,8 +924,8 @@ proc checkBuffer(readfds: var seq[TSocket]): int = proc select*(readfds, writefds, exceptfds: var seq[TSocket], timeout = 500): int {.tags: [FReadIO].} = ## Traditional select function. This function will return the number of - ## sockets that are ready to be read from, written to, or which have errors - ## if there are none; 0 is returned. + ## sockets that are ready to be read from, written to, or which have errors. + ## If there are none; 0 is returned. ## ``Timeout`` is in miliseconds and -1 can be specified for no timeout. ## ## A socket is removed from the specific ``seq`` when it has data waiting to @@ -935,7 +953,7 @@ proc select*(readfds, writefds, exceptfds: var seq[TSocket], proc select*(readfds, writefds: var seq[TSocket], timeout = 500): int {.tags: [FReadIO].} = - ## variant of select with only a read and write list. + ## Variant of select with only a read and write list. let buffersFilled = checkBuffer(readfds) if buffersFilled > 0: return buffersFilled @@ -1019,7 +1037,10 @@ template retRead(flags, readBytes: int) = return res proc recv*(socket: TSocket, data: pointer, size: int): int {.tags: [FReadIO].} = - ## receives data from a socket + ## Receives data from a socket. + ## + ## **Note**: This is a low-level function, you may be interested in the higher + ## level versions of this function which are also named ``recv``. if size == 0: return if socket.isBuffered: if socket.bufLen == 0: @@ -1055,24 +1076,79 @@ proc recv*(socket: TSocket, data: pointer, size: int): int {.tags: [FReadIO].} = else: result = recv(socket.fd, data, size.cint, 0'i32) -proc recv*(socket: TSocket, data: var string, size: int): int = - ## higher-level version of the above +proc waitFor(socket: TSocket, waited: var float, timeout, size: int, + funcName: string): int {.tags: [FTime].} = + ## determines the amount of characters that can be read. Result will never + ## be larger than ``size``. For unbuffered sockets this will be ``1``. + ## For buffered sockets it can be as big as ``BufferSize``. + ## + ## If this function does not determine that there is data on the socket + ## within ``timeout`` ms, an ETimeout error will be raised. + result = 1 + if size <= 0: assert false + if timeout == -1: return size + if socket.isBuffered and socket.bufLen != 0 and socket.bufLen != socket.currPos: + result = socket.bufLen - socket.currPos + result = min(result, size) + else: + if timeout - int(waited * 1000.0) < 1: + raise newException(ETimeout, "Call to '" & funcName & "' timed out.") + + when defined(ssl): + if socket.isSSL: + if socket.hasDataBuffered: + # sslPeekChar is present. + return 1 + let sslPending = SSLPending(socket.sslHandle) + if sslPending != 0: + return sslPending + + var s = @[socket] + var startTime = epochTime() + let selRet = select(s, timeout - int(waited * 1000.0)) + if selRet < 0: OSError() + if selRet != 1: + raise newException(ETimeout, "Call to '" & funcName & "' timed out.") + waited += (epochTime() - startTime) + +proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int {. + tags: [FReadIO, FTime].} = + ## overload with a ``timeout`` parameter in miliseconds. + var waited = 0.0 # number of seconds already waited + + var read = 0 + while read < size: + let avail = waitFor(socket, waited, timeout, size-read, "recv") + var d = cast[cstring](data) + result = recv(socket, addr(d[read]), avail) + if result == 0: break + if result < 0: + return result + inc(read, result) + + result = read + +proc recv*(socket: TSocket, data: var string, size: int, timeout = -1): int = + ## Higher-level version of ``recv``. ## ## When 0 is returned the socket's connection has been closed. ## ## This function will throw an EOS exception when an error occurs. A value ## lower than 0 is never returned. ## + ## A timeout may be specified in miliseconds, if enough data is not received + ## within the time specified an ETimeout exception will be raised. + ## ## **Note**: ``data`` must be initialised. data.setLen(size) - result = recv(socket, cstring(data), size) + result = recv(socket, cstring(data), size, timeout) if result < 0: data.setLen(0) socket.SocketError(result) data.setLen(result) proc recvAsync*(socket: TSocket, data: var string, size: int): int = - ## Async version of the above. + ## Async version of ``recv``. ## ## When socket is non-blocking and no data is available on the socket, ## ``-1`` will be returned and ``data`` will be ``""``. @@ -1086,51 +1162,6 @@ proc recvAsync*(socket: TSocket, data: var string, size: int): int = result = -1 data.setLen(result) -proc waitFor(socket: TSocket, waited: var float, timeout: int): int {. - tags: [FTime].} = - ## returns the number of characters available to be read. In unbuffered - ## sockets this is always 1, otherwise this may as big as ``BufferSize``. - result = 1 - if socket.isBuffered and socket.bufLen != 0 and socket.bufLen != socket.currPos: - result = socket.bufLen - socket.currPos - else: - if timeout - int(waited * 1000.0) < 1: - raise newException(ETimeout, "Call to recv() timed out.") - var s = @[socket] - var startTime = epochTime() - if select(s, timeout - int(waited * 1000.0)) != 1: - raise newException(ETimeout, "Call to recv() timed out.") - waited += (epochTime() - startTime) - -proc recv*(socket: TSocket, data: pointer, size: int, timeout: int): int {. - tags: [FReadIO, FTime].} = - ## overload with a ``timeout`` parameter in miliseconds. - var waited = 0.0 # number of seconds already waited - - var read = 0 - while read < size: - let avail = waitFor(socket, waited, timeout) - var d = cast[cstring](data) - result = recv(socket, addr(d[read]), avail) - if result == 0: break - if result < 0: - return result - inc(read, result) - - result = read - -proc recv*(socket: TSocket, data: var string, size: int, timeout: int): int = - ## higher-level version of the above. - ## - ## Similar to the non-timeout version this will throw an EOS exception - ## when an error occurs. - data.setLen(size) - result = recv(socket, cstring(data), size, timeout) - if result < 0: - data.setLen(0) - socket.SocketError() - data.setLen(result) - proc peekChar(socket: TSocket, c: var char): int {.tags: [FReadIO].} = if socket.isBuffered: result = 1 @@ -1151,9 +1182,11 @@ proc peekChar(socket: TSocket, c: var char): int {.tags: [FReadIO].} = return result = recv(socket.fd, addr(c), 1, MSG_PEEK) -proc recvLine*(socket: TSocket, line: var TaintedString): bool {. - tags: [FReadIO].} = - ## retrieves a line from ``socket``. If a full line is received ``\r\L`` is not +proc recvLine*(socket: TSocket, line: var TaintedString, timeout = -1): bool {. + tags: [FReadIO, FTime], deprecated.} = + ## Receive a line of data from ``socket``. + ## + ## If a full line is received ``\r\L`` is not ## added to ``line``, however if solely ``\r\L`` is received then ``line`` ## will be set to it. ## @@ -1163,17 +1196,28 @@ proc recvLine*(socket: TSocket, line: var TaintedString): bool {. ## ## If the socket is disconnected, ``line`` will be set to ``""`` and ``True`` ## will be returned. + ## + ## A timeout can be specified in miliseconds, if data is not received within + ## the specified time an ETimeout exception will be raised. + ## + ## **Deprecated since version 0.9.2**: This function has been deprecated in + ## favour of readLine. + template addNLIfEmpty(): stmt = if line.len == 0: line.add("\c\L") + var waited = 0.0 + setLen(line.string, 0) while true: var c: char + discard waitFor(socket, waited, timeout, 1, "recvLine") var n = recv(socket, addr(c), 1) if n < 0: return elif n == 0: return true if c == '\r': + discard waitFor(socket, waited, timeout, 1, "recvLine") n = peekChar(socket, c) if n > 0 and c == '\L': discard recv(socket, addr(c), 1) @@ -1185,46 +1229,61 @@ proc recvLine*(socket: TSocket, line: var TaintedString): bool {. return true add(line.string, c) -proc recvLine*(socket: TSocket, line: var TaintedString, timeout: int): bool {. +proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {. tags: [FReadIO, FTime].} = - ## variant with a ``timeout`` parameter, the timeout parameter specifies - ## how many miliseconds to wait for data. + ## Reads a line of data from ``socket``. ## - ## ``ETimeout`` will be raised if ``timeout`` is exceeded. + ## If a full line is read ``\r\L`` is not + ## added to ``line``, however if solely ``\r\L`` is read then ``line`` + ## will be set to it. + ## + ## If the socket is disconnected, ``line`` will be set to ``""``. + ## + ## An EOS exception will be raised in the case of a socket error. + ## + ## A timeout can be specified in miliseconds, if data is not received within + ## the specified time an ETimeout exception will be raised. + template addNLIfEmpty(): stmt = if line.len == 0: line.add("\c\L") - - var waited = 0.0 # number of seconds already waited - + + var waited = 0.0 + setLen(line.string, 0) while true: var c: char - discard waitFor(socket, waited, timeout) + discard waitFor(socket, waited, timeout, 1, "readLine") var n = recv(socket, addr(c), 1) - if n < 0: return - elif n == 0: return true + if n < 0: OSError() + elif n == 0: return if c == '\r': - discard waitFor(socket, waited, timeout) + discard waitFor(socket, waited, timeout, 1, "readLine") n = peekChar(socket, c) if n > 0 and c == '\L': discard recv(socket, addr(c), 1) - elif n <= 0: return false + elif n <= 0: OSError() addNlIfEmpty() - return true + return elif c == '\L': addNlIfEmpty() - return true + return add(line.string, c) proc recvLineAsync*(socket: TSocket, - line: var TaintedString): TRecvLineResult {.tags: [FReadIO].} = - ## similar to ``recvLine`` but for non-blocking sockets. + line: var TaintedString): TRecvLineResult {.tags: [FReadIO], deprecated.} = + ## Similar to ``recvLine`` but designed for non-blocking sockets. + ## ## The values of the returned enum should be pretty self explanatory: - ## If a full line has been retrieved; ``RecvFullLine`` is returned. - ## If some data has been retrieved; ``RecvPartialLine`` is returned. - ## If the socket has been disconnected; ``RecvDisconnected`` is returned. - ## If call to ``recv`` failed; ``RecvFail`` is returned. + ## + ## * If a full line has been retrieved; ``RecvFullLine`` is returned. + ## * If some data has been retrieved; ``RecvPartialLine`` is returned. + ## * If the socket has been disconnected; ``RecvDisconnected`` is returned. + ## * If call to ``recv`` failed; ``RecvFail`` is returned. + ## + ## **Deprecated since version 0.9.2**: This function has been deprecated in + ## favour of readLineAsync. + setLen(line.string, 0) while true: var c: char @@ -1243,6 +1302,40 @@ proc recvLineAsync*(socket: TSocket, elif c == '\L': return RecvFullLine add(line.string, c) +proc readLineAsync*(socket: TSocket, + line: var TaintedString): TReadLineResult {.tags: [FReadIO].} = + ## Similar to ``recvLine`` but designed for non-blocking sockets. + ## + ## The values of the returned enum should be pretty self explanatory: + ## + ## * If a full line has been retrieved; ``ReadFullLine`` is returned. + ## * If some data has been retrieved; ``ReadPartialLine`` is returned. + ## * If the socket has been disconnected; ``ReadDisconnected`` is returned. + ## * If no data could be retrieved; ``ReadNone`` is returned. + ## * If call to ``recv`` failed; **an EOS exception is raised.** + setLen(line.string, 0) + + template errorOrNone = + socket.SocketError(async = true) + return ReadNone + + while true: + var c: char + var n = recv(socket, addr(c), 1) + if n < 0: + if line.len == 0: errorOrNone else: return ReadPartialLine + elif n == 0: + return (if line.len == 0: ReadDisconnected else: ReadPartialLine) + if c == '\r': + n = peekChar(socket, c) + if n > 0 and c == '\L': + discard recv(socket, addr(c), 1) + elif n <= 0: + if line.len == 0: errorOrNone else: return ReadPartialLine + return ReadFullLine + elif c == '\L': return ReadFullLine + add(line.string, c) + proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} = ## receives all the available data from the socket. ## Socket errors will result in an ``EOS`` error. @@ -1348,6 +1441,9 @@ proc recvFrom*(socket: TSocket, data: var string, length: int, ## Receives data from ``socket``. This function should normally be used with ## connection-less sockets (UDP sockets). ## + ## If an error occurs the return value will be ``-1``. Otherwise the return + ## value will be the length of data received. + ## ## **Warning:** This function does not yet have a buffered implementation, ## so when ``socket`` is buffered the non-buffered implementation will be ## used. Therefore if ``socket`` contains something in its buffer this @@ -1368,9 +1464,10 @@ proc recvFrom*(socket: TSocket, data: var string, length: int, proc recvFromAsync*(socket: TSocket, data: var String, length: int, address: var string, port: var TPort, flags = 0'i32): bool {.tags: [FReadIO].} = - ## Similar to ``recvFrom`` but raises an EOS error when an error occurs and - ## is also meant for non-blocking sockets. - ## Returns False if no messages could be received from ``socket``. + ## Variant of ``recvFrom`` for non-blocking sockets. Unlike ``recvFrom``, + ## this function will raise an EOS error whenever a socket error occurs. + ## + ## If there is no data to be read from the socket ``False`` will be returned. result = true var callRes = recvFrom(socket, data, length, address, port, flags) if callRes < 0: @@ -1394,14 +1491,19 @@ proc skip*(socket: TSocket) {.tags: [FReadIO], deprecated.} = while recv(socket, buf, bufSize) == bufSize: nil dealloc(buf) -proc skip*(socket: TSocket, size: int) = +proc skip*(socket: TSocket, size: int, timeout = -1) = ## Skips ``size`` amount of bytes. ## + ## An optional timeout can be specified in miliseconds, if skipping the + ## bytes takes longer than specified an ETimeout exception will be raised. + ## ## Returns the number of skipped bytes. + var waited = 0.0 var dummy = alloc(size) var bytesSkipped = 0 while bytesSkipped != size: - bytesSkipped += recv(socket, dummy, size-bytesSkipped) + let avail = waitFor(socket, waited, timeout, size-bytesSkipped, "skip") + bytesSkipped += recv(socket, dummy, avail) dealloc(dummy) proc send*(socket: TSocket, data: pointer, size: int): int {. @@ -1529,21 +1631,27 @@ proc setBlocking(s: TSocket, blocking: bool) = var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK if fcntl(s.fd, F_SETFL, mode) == -1: OSError() - -proc connect*(socket: TSocket, timeout: int, name: string, port = TPort(0), - af: TDomain = AF_INET) {.tags: [FReadIO].} = - ## Overload for ``connect`` to support timeouts. The ``timeout`` parameter - ## specifies the time in miliseconds of how long to wait for a connection - ## to be made. - ## - ## **Warning:** If ``socket`` is non-blocking then - ## this function will set blocking mode on ``socket`` to true. - socket.setBlocking(true) + s.nonblocking = not blocking - socket.connectAsync(name, port, af) +proc connect*(socket: TSocket, address: string, port = TPort(0), timeout: int, + af: TDomain = AF_INET) {.tags: [FReadIO, FWriteIO].} = + ## Connects to server as specified by ``address`` on port specified by ``port``. + ## + ## The ``timeout`` paremeter specifies the time in miliseconds to allow for + ## the connection to the server to be made. + let originalStatus = not socket.nonblocking + socket.setBlocking(false) + + socket.connectAsync(address, port, af) var s: seq[TSocket] = @[socket] if selectWrite(s, timeout) != 1: - raise newException(ETimeout, "Call to connect() timed out.") + raise newException(ETimeout, "Call to 'connect' timed out.") + else: + when defined(ssl): + if socket.isSSL: + socket.setBlocking(true) + doAssert socket.handshake() + socket.setBlocking(originalStatus) proc isSSL*(socket: TSocket): bool = return socket.isSSL ## Determines whether ``socket`` is a SSL socket. diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim old mode 100755 new mode 100644 index 232205ebd..764471b78 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -106,6 +106,12 @@ proc write*(s: PStream, x: string) = ## terminating zero is written. writeData(s, cstring(x), x.len) +proc writeln*(s: PStream, args: varargs[string, `$`]) = + ## writes one or more strings to the the stream `s` followed + ## by a new line. No length field or terminating zero is written. + for str in args: write(s, str) + write(s, "\n") + proc read[T](s: PStream, result: var T) = ## generic read procedure. Reads `result` from the stream `s`. if readData(s, addr(result), sizeof(T)) != sizeof(T): @@ -180,9 +186,7 @@ proc readLine*(s: PStream): TaintedString = if c == '\c': c = readChar(s) break - if c == '\b': - result.string.setLen(result.len - 1) - elif c == '\L' or c == '\0': + if c == '\L' or c == '\0': break else: result.string.add(c) @@ -240,7 +244,10 @@ type TFileStream* = object of TStream f: TFile -proc fsClose(s: PStream) = close(PFileStream(s).f) +proc fsClose(s: PStream) = + if PFileStream(s).f != nil: + close(PFileStream(s).f) + PFileStream(s).f = nil proc fsFlush(s: PStream) = flushFile(PFileStream(s).f) proc fsAtEnd(s: PStream): bool = return EndOfFile(PFileStream(s).f) proc fsSetPosition(s: PStream, pos: int) = setFilePos(PFileStream(s).f, pos) diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim old mode 100755 new mode 100644 diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim old mode 100755 new mode 100644 index 8b64434d8..b5f5a41eb --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -415,7 +415,15 @@ proc parseEnum*[T: enum](s: string, default: T): T = proc repeatChar*(count: int, c: Char = ' '): string {.noSideEffect, rtl, extern: "nsuRepeatChar".} = ## Returns a string of length `count` consisting only of - ## the character `c`. + ## the character `c`. You can use this proc to left align strings. Example: + ## + ## .. code-block:: nimrod + ## let + ## width = 15 + ## text1 = "Hello user!" + ## text2 = "This is a very long string" + ## echo text1 & repeatChar(max(0, width - text1.len)) & "|" + ## echo text2 & repeatChar(max(0, width - text2.len)) & "|" result = newString(count) for i in 0..count-1: result[i] = c @@ -429,7 +437,8 @@ proc align*(s: string, count: int): string {. noSideEffect, rtl, extern: "nsuAlignString".} = ## Aligns a string `s` with spaces, so that is of length `count`. Spaces are ## added before `s` resulting in right alignment. If ``s.len >= count``, no - ## spaces are added and `s` is returned unchanged. + ## spaces are added and `s` is returned unchanged. If you need to left align + ## a string use the repeatChar proc. if s.len < count: result = newString(count) var spaces = count - s.len @@ -754,7 +763,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect, else: add result, substr(s, i, j) i = j + 1 - # copy the rest: + # copy the rest: add result, substr(s, i) proc delete*(s: var string, first, last: int) {.noSideEffect, diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim old mode 100755 new mode 100644 diff --git a/lib/pure/times.nim b/lib/pure/times.nim old mode 100755 new mode 100644 index d84fbbb67..03a05aea1 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -9,7 +9,7 @@ ## This module contains routines and types for dealing with time. -## This module is available for the ECMAScript target. +## This module is available for the JavaScript target. {.push debugger:off.} # the user does not want to trace a part # of the standard library! @@ -29,10 +29,11 @@ var timezone {.importc, header: "".}: int tzname {.importc, header: "" .}: array[0..1, cstring] -when defined(posix): +when defined(posix) and not defined(JS): type TTimeImpl {.importc: "time_t", header: "".} = int TTime* = distinct TTimeImpl ## distinct type that represents a time + ## measured as number of seconds since the epoch Ttimeval {.importc: "struct timeval", header: "", final, pure.} = object ## struct timeval @@ -58,47 +59,47 @@ elif defined(windows): type TTime* = distinct TTimeImpl -elif defined(ECMAScript): +elif defined(JS): type TTime* {.final, importc.} = object - getDay: proc (): int - getFullYear: proc (): int - getHours: proc (): int - getMilliseconds: proc (): int - getMinutes: proc (): int - getMonth: proc (): int - getSeconds: proc (): int - getTime: proc (): int - getTimezoneOffset: proc (): int - getDate: proc (): int - getUTCDate: proc (): int - getUTCFullYear: proc (): int - getUTCHours: proc (): int - getUTCMilliseconds: proc (): int - getUTCMinutes: proc (): int - getUTCMonth: proc (): int - getUTCSeconds: proc (): int - getYear: proc (): int - parse: proc (s: cstring): TTime - setDate: proc (x: int) - setFullYear: proc (x: int) - setHours: proc (x: int) - setMilliseconds: proc (x: int) - setMinutes: proc (x: int) - setMonth: proc (x: int) - setSeconds: proc (x: int) - setTime: proc (x: int) - setUTCDate: proc (x: int) - setUTCFullYear: proc (x: int) - setUTCHours: proc (x: int) - setUTCMilliseconds: proc (x: int) - setUTCMinutes: proc (x: int) - setUTCMonth: proc (x: int) - setUTCSeconds: proc (x: int) - setYear: proc (x: int) - toGMTString: proc (): cstring - toLocaleString: proc (): cstring - UTC: proc (): int + getDay: proc (): int {.tags: [].} + getFullYear: proc (): int {.tags: [].} + getHours: proc (): int {.tags: [].} + getMilliseconds: proc (): int {.tags: [].} + getMinutes: proc (): int {.tags: [].} + getMonth: proc (): int {.tags: [].} + getSeconds: proc (): int {.tags: [].} + getTime: proc (): int {.tags: [].} + getTimezoneOffset: proc (): int {.tags: [].} + getDate: proc (): int {.tags: [].} + getUTCDate: proc (): int {.tags: [].} + getUTCFullYear: proc (): int {.tags: [].} + getUTCHours: proc (): int {.tags: [].} + getUTCMilliseconds: proc (): int {.tags: [].} + getUTCMinutes: proc (): int {.tags: [].} + getUTCMonth: proc (): int {.tags: [].} + getUTCSeconds: proc (): int {.tags: [].} + getUTCDay: proc (): int {.tags: [].} + getYear: proc (): int {.tags: [].} + parse: proc (s: cstring): TTime {.tags: [].} + setDate: proc (x: int) {.tags: [].} + setFullYear: proc (x: int) {.tags: [].} + setHours: proc (x: int) {.tags: [].} + setMilliseconds: proc (x: int) {.tags: [].} + setMinutes: proc (x: int) {.tags: [].} + setMonth: proc (x: int) {.tags: [].} + setSeconds: proc (x: int) {.tags: [].} + setTime: proc (x: int) {.tags: [].} + setUTCDate: proc (x: int) {.tags: [].} + setUTCFullYear: proc (x: int) {.tags: [].} + setUTCHours: proc (x: int) {.tags: [].} + setUTCMilliseconds: proc (x: int) {.tags: [].} + setUTCMinutes: proc (x: int) {.tags: [].} + setUTCMonth: proc (x: int) {.tags: [].} + setUTCSeconds: proc (x: int) {.tags: [].} + setYear: proc (x: int) {.tags: [].} + toGMTString: proc (): cstring {.tags: [].} + toLocaleString: proc (): cstring {.tags: [].} type TTimeInfo* = object of TObject ## represents a time in different parts @@ -115,13 +116,16 @@ type weekday*: TWeekDay ## The current day of the week. yearday*: range[0..365] ## The number of days since January 1, ## in the range 0 to 365. - ## Always 0 if the target is ECMAScript. + ## Always 0 if the target is JS. isDST*: bool ## Determines whether DST is in effect. Always ## ``False`` if time is UTC. tzname*: string ## The timezone this time is in. E.g. GMT timezone*: int ## The offset of the (non-DST) timezone in seconds ## west of UTC. + ## I make some assumptions about the data in here. Either + ## everything should be positive or everything negative. Zero is + ## fine too. Mixed signs will lead to unexpected results. TTimeInterval* {.pure.} = object ## a time interval miliseconds*: int ## The number of miliseconds seconds*: int ## The number of seconds @@ -131,23 +135,37 @@ type months*: int ## The number of months years*: int ## The number of years -proc getTime*(): TTime {.tags: [FTime].} ## gets the current calendar time -proc getLocalTime*(t: TTime): TTimeInfo +proc getTime*(): TTime {.tags: [FTime].} + ## gets the current calendar time as a UNIX epoch value (number of seconds + ## elapsed since 1970) with integer precission. Use epochTime for higher + ## resolution. +proc getLocalTime*(t: TTime): TTimeInfo {.tags: [FTime].} ## converts the calendar time `t` to broken-time representation, ## expressed relative to the user's specified time zone. -proc getGMTime*(t: TTime): TTimeInfo +proc getGMTime*(t: TTime): TTimeInfo {.tags: [FTime].} ## converts the calendar time `t` to broken-down time representation, ## expressed in Coordinated Universal Time (UTC). -proc TimeInfoToTime*(timeInfo: TTimeInfo): TTime +proc TimeInfoToTime*(timeInfo: TTimeInfo): TTime {.tags: [].} ## converts a broken-down time structure to ## calendar time representation. The function ignores the specified ## contents of the structure members `weekday` and `yearday` and recomputes ## them from the other information in the broken-down time structure. -proc `$` *(timeInfo: TTimeInfo): string +proc fromSeconds*(since1970: float): TTime {.tags: [].} + ## Takes a float which contains the number of seconds since the unix epoch and + ## returns a time object. + +proc fromSeconds*(since1970: int|int64): TTime = fromSeconds(float(since1970)) + ## Takes an int which contains the number of seconds since the unix epoch and + ## returns a time object. + +proc toSeconds*(time: TTime): float {.tags: [].} + ## Returns the time in seconds since the unix epoch. + +proc `$` *(timeInfo: TTimeInfo): string {.tags: [].} ## converts a `TTimeInfo` object to a string representation. -proc `$` *(time: TTime): string +proc `$` *(time: TTime): string {.tags: [].} ## converts a calendar time to a string representation. proc `-`*(a, b: TTime): int64 {. @@ -164,9 +182,14 @@ proc `<=` * (a, b: TTime): bool {. ## returns true iff ``a <= b``. result = a - b <= 0 -proc getTzname*(): tuple[nonDST, DST: string] {.tags: [FTime].} - ## returns the local timezone; ``nonDST`` is the name of the local non-DST - ## timezone, ``DST`` is the name of the local DST timezone. +proc `==`*(a, b: TTime): bool {.rtl, extern: "ntEqTime".} = + ## returns true if ``a == b``, that is if both times represent the same value + result = a - b == 0 + +when not defined(JS): + proc getTzname*(): tuple[nonDST, DST: string] {.tags: [FTime].} + ## returns the local timezone; ``nonDST`` is the name of the local non-DST + ## timezone, ``DST`` is the name of the local DST timezone. proc getTimezone*(): int {.tags: [FTime].} ## returns the offset of the local (non-DST) timezone in seconds west of UTC. @@ -203,10 +226,13 @@ proc getDaysInMonth(month: TMonth, year: int): int = of mApr, mJun, mSep, mNov: result = 30 else: result = 31 -proc calculateSeconds(a: TTimeInfo, interval: TTimeInterval): float = +proc toSeconds(a: TTimeInfo, interval: TTimeInterval): float = + ## Calculates how many seconds the interval is worth by adding up + ## all the fields + var anew = a var newinterv = interval - result = 0.0 + result = 0 newinterv.months += interval.years * 12 var curMonth = anew.month @@ -219,7 +245,7 @@ proc calculateSeconds(a: TTimeInfo, interval: TTimeInterval): float = curMonth.inc() result += float(newinterv.days * 24 * 60 * 60) result += float(newinterv.minutes * 60 * 60) - result += newinterv.seconds.float + result += float(newinterv.seconds) result += newinterv.miliseconds / 1000 proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo = @@ -227,26 +253,26 @@ proc `+`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo = ## ## **Note:** This has been only briefly tested and it may not be ## very accurate. - let t = timeInfoToTime(a) - let secs = calculateSeconds(a, interval) + let t = toSeconds(TimeInfoToTime(a)) + let secs = toSeconds(a, interval) if a.tzname == "UTC": - result = getGMTime(TTime(float(t) + secs)) + result = getGMTime(fromSeconds(t + secs)) else: - result = getLocalTime(TTime(float(t) + secs)) + result = getLocalTime(fromSeconds(t + secs)) proc `-`*(a: TTimeInfo, interval: TTimeInterval): TTimeInfo = ## subtracts ``interval`` time. ## ## **Note:** This has been only briefly tested, it is inaccurate especially ## when you subtract so much that you reach the Julian calendar. - let t = timeInfoToTime(a) - let secs = calculateSeconds(a, interval) + let t = toSeconds(TimeInfoToTime(a)) + let secs = toSeconds(a, interval) if a.tzname == "UTC": - result = getGMTime(TTime(float(t) - secs)) + result = getGMTime(fromSeconds(t - secs)) else: - result = getLocalTime(TTime(float(t) - secs)) + result = getLocalTime(fromSeconds(t - secs)) -when not defined(ECMAScript): +when not defined(JS): proc epochTime*(): float {.rtl, extern: "nt$1", tags: [FTime].} ## gets time after the UNIX epoch (1970) in seconds. It is a float ## because sub-second resolution is likely to be supported (depending @@ -265,8 +291,7 @@ when not defined(ECMAScript): ## doWork() ## echo "CPU time [s] ", cpuTime() - t0 -when not defined(ECMAScript): - +when not defined(JS): # C wrapper: type structTM {.importc: "struct tm", final.} = object @@ -286,17 +311,22 @@ when not defined(ECMAScript): TClock {.importc: "clock_t".} = distinct int proc localtime(timer: PTime): PTimeInfo {. - importc: "localtime", header: "".} - proc gmtime(timer: PTime): PTimeInfo {.importc: "gmtime", header: "".} - proc timec(timer: PTime): TTime {.importc: "time", header: "".} - proc mktime(t: structTM): TTime {.importc: "mktime", header: "".} + importc: "localtime", header: "", tags: [].} + proc gmtime(timer: PTime): PTimeInfo {. + importc: "gmtime", header: "", tags: [].} + proc timec(timer: PTime): TTime {. + importc: "time", header: "", tags: [].} + proc mktime(t: structTM): TTime {. + importc: "mktime", header: "", tags: [].} proc asctime(tblock: structTM): CString {. - importc: "asctime", header: "".} - proc ctime(time: PTime): CString {.importc: "ctime", header: "".} + importc: "asctime", header: "", tags: [].} + proc ctime(time: PTime): CString {. + importc: "ctime", header: "", tags: [].} # strftime(s: CString, maxsize: int, fmt: CString, t: tm): int {. # importc: "strftime", header: "".} - proc clock(): TClock {.importc: "clock", header: "".} - proc difftime(a, b: TTime): float {.importc: "difftime", header: "".} + proc clock(): TClock {.importc: "clock", header: "", tags: [FTime].} + proc difftime(a, b: TTime): float {.importc: "difftime", header: "", + tags: [].} var clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int @@ -407,7 +437,11 @@ when not defined(ECMAScript): proc getTimezone(): int = return timezone - + + proc fromSeconds(since1970: float): TTime = TTime(since1970) + + proc toSeconds(time: TTime): float = float(time) + when not defined(useNimRtl): proc epochTime(): float = when defined(posix): @@ -427,9 +461,13 @@ when not defined(ECMAScript): proc cpuTime(): float = result = toFloat(int(clock())) / toFloat(clocksPerSec) -else: - proc newDate(): TTime {.importc: "new Date", nodecl.} - proc getTime(): TTime = return newDate() +elif defined(JS): + proc newDate(): TTime {.importc: "new Date".} + proc newDate(value: float): TTime {.importc: "new Date".} + proc newDate(value: string): TTime {.importc: "new Date".} + proc getTime(): TTime = + # Warning: This is something different in JS. + return newDate() const weekDays: array [0..6, TWeekDay] = [ @@ -452,7 +490,7 @@ else: result.monthday = t.getUTCDate() result.month = TMonth(t.getUTCMonth()) result.year = t.getUTCFullYear() - result.weekday = weekDays[t.getDay()] + result.weekday = weekDays[t.getUTCDay()] result.yearday = 0 proc TimeInfoToTime*(timeInfo: TTimeInfo): TTime = @@ -477,6 +515,13 @@ else: ## get the miliseconds from the start of the program return int(getTime() - startMilsecs) + proc valueOf(time: TTime): float {.importcpp: "getTime", tags:[]} + + proc fromSeconds(since1970: float): TTime = result = newDate(since1970) + + proc toSeconds(time: TTime): float = result = time.valueOf() / 1000 + + proc getTimezone(): int = result = newDate().getTimezoneOffset() proc getDateStr*(): string {.rtl, extern: "nt$1", tags: [FTime].} = ## gets the current date as a string of the format ``YYYY-MM-DD``. @@ -671,7 +716,7 @@ when isMainModule: # $ date --date='@2147483647' # Tue 19 Jan 03:14:07 GMT 2038 - var t = getGMTime(TTime(2147483647)) + var t = getGMTime(fromSeconds(2147483647)) echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038" @@ -679,19 +724,21 @@ when isMainModule: " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == "19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC" - var t2 = getGMTime(TTime(160070789)) # Mon 27 Jan 16:06:29 GMT 1975 + var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975 assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == "27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 0 00 00:00 UTC" - when sizeof(TTime) == 8: - var t3 = getGMTime(TTime(889067643645)) # Fri 7 Jun 19:20:45 BST 30143 + when not defined(JS) and sizeof(TTime) == 8: + var t3 = getGMTime(fromSeconds(889067643645)) # Fri 7 Jun 19:20:45 BST 30143 assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == "7 07 Fri Friday 6 06 18 18 20 20 6 06 Jun June 45 45 P PM 3 43 143 0143 30143 0 00 00:00 UTC" assert t3.format(":,[]()-/") == ":,[]()-/" - var t4 = getGMTime(TTime(876124714)) # Mon 6 Oct 08:58:34 BST 1997 + var t4 = getGMTime(fromSeconds(876124714)) # Mon 6 Oct 08:58:34 BST 1997 assert t4.format("M MM MMM MMMM") == "10 10 Oct October" - \ No newline at end of file + # Interval tests + assert((t4 - initInterval(years = 2)).format("yyyy") == "1995") + assert((t4 - initInterval(years = 7, minutes = 34, seconds = 24)).format("yyyy mm ss") == "1990 24 10") diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index f76573788..142178a86 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -132,6 +132,11 @@ proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} = result = newString(1) result[0] = chr(i) +proc `$`*(runes: seq[TRune]): string = + ## converts a sequence of runes to a string + result = "" + for rune in runes: result.add(rune.toUTF8) + const alphaRanges = [ 0x00d8, 0x00f6, # - @@ -1208,3 +1213,10 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} = result = irune(toLower(ar)) - irune(toLower(br)) if result != 0: return result = a.len - b.len + +when isMainModule: + let + someString = "öÑ" + someRunes = @[runeAt(someString, 0), runeAt(someString, 2)] + compared = (someString == $someRunes) + assert compared == true diff --git a/lib/pure/unidecode/unidecode.dat b/lib/pure/unidecode/unidecode.dat old mode 100755 new mode 100644 diff --git a/lib/pure/unidecode/unidecode.nim b/lib/pure/unidecode/unidecode.nim old mode 100755 new mode 100644 diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index fce84bea4..71f4d498b 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -17,7 +17,13 @@ ## It is loosely based on C++'s boost.test and Haskell's QuickTest import - macros, terminal, os + macros + +when defined(stdout): + import os + +when not defined(ECMAScript): + import terminal type TTestStatus* = enum OK, FAILED @@ -52,12 +58,15 @@ proc testDone(name: string, s: TTestStatus) = program_result += 1 if OutputLevel != PRINT_NONE and (OutputLevel == PRINT_ALL or s == FAILED): - var color = (if s == OK: fgGreen else: fgRed) - - if ColorOutput: - styledEcho styleBright, color, "[", $s, "] ", fgWhite, name, "\n" + template rawPrint() = echo("[", $s, "] ", name, "\n") + when not defined(ECMAScript): + if ColorOutput and not defined(ECMAScript): + var color = (if s == OK: fgGreen else: fgRed) + styledEcho styleBright, color, "[", $s, "] ", fgWhite, name, "\n" + else: + rawPrint() else: - echo "[", $s, "] ", name, "\n" + rawPrint() template test*(name: expr, body: stmt): stmt {.immediate, dirty.} = bind shouldRun, checkpoints, testDone @@ -87,7 +96,8 @@ template fail* = for msg in items(checkpoints): echo msg - if AbortOnError: quit(1) + when not defined(ECMAScript): + if AbortOnError: quit(1) TestStatusIMPL = FAILED checkpoints = @[] @@ -171,14 +181,19 @@ macro expect*(exceptions: varargs[expr], body: stmt): stmt {.immediate.} = result = getAst(expectBody(errorTypes, exp.lineinfo, body)) -## Reading settings -var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string +when defined(stdout): + ## Reading settings + var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string + + AbortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR") + ColorOutput = not existsEnv("NIMTEST_NO_COLOR") + +else: + var envOutLvl = "" # TODO + ColorOutput = false if envOutLvl.len > 0: for opt in countup(low(TOutputLevel), high(TOutputLevel)): if $opt == envOutLvl: OutputLevel = opt break - -AbortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR") -ColorOutput = not existsEnv("NIMTEST_NO_COLOR") diff --git a/lib/pure/xmldom.nim b/lib/pure/xmldom.nim old mode 100755 new mode 100644 diff --git a/lib/pure/xmldomparser.nim b/lib/pure/xmldomparser.nim old mode 100755 new mode 100644 diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim old mode 100755 new mode 100644 diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim old mode 100755 new mode 100644 index 1a1467e8f..8cf484bfc --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -109,6 +109,7 @@ iterator items*(n: PXmlNode): PXmlNode {.inline.} = proc attrs*(n: PXmlNode): PXmlAttributes {.inline.} = ## gets the attributes belonging to `n`. + ## Returns `nil` if attributes have not been initialised for this node. assert n.k == xnElement result = n.fAttr @@ -282,5 +283,7 @@ proc child*(n: PXmlNode, name: string): PXmlNode = proc attr*(n: PXmlNode, name: string): string = ## Finds the first attribute of `n` with a name of `name`. + ## Returns "" on failure. assert n.kind == xnElement + if n.attrs == nil: return "" return n.attrs[name] diff --git a/lib/system.nim b/lib/system.nim old mode 100755 new mode 100644 index b4c265f62..5dafd6e13 --- a/lib/system.nim +++ b/lib/system.nim @@ -184,11 +184,11 @@ proc `..`*[T](b: T): TSlice[T] {.noSideEffect, inline.} = when not defined(niminheritable): {.pragma: inheritable.} -when not defined(EcmaScript) and not defined(NimrodVM): +when not defined(JS) and not defined(NimrodVM): type - TGenericSeq* {.compilerproc, pure, inheritable.} = object + TGenericSeq {.compilerproc, pure, inheritable.} = object len, reserved: int - PGenericSeq* {.exportc.} = ptr TGenericSeq + PGenericSeq {.exportc.} = ptr TGenericSeq # len and space without counting the terminating zero: NimStringDesc {.compilerproc, final.} = object of TGenericSeq data: array[0..100_000_000, char] @@ -370,9 +370,34 @@ proc newSeq*[T](s: var seq[T], len: int) {.magic: "NewSeq", noSideEffect.} ## creates a new sequence of type ``seq[T]`` with length ``len``. ## This is equivalent to ``s = @[]; setlen(s, len)``, but more ## efficient since no reallocation is needed. + ## + ## Note that the sequence will be filled with uninitialized entries, which + ## can be a problem for sequences containing strings. After the creation of + ## the sequence you should assign entries to the sequence instead of adding + ## them. Example: + ## + ## .. code-block:: nimrod + ## var inputStrings : seq[string] + ## newSeq(inputStrings, 3) + ## inputStrings[0] = "The fourth" + ## inputStrings[1] = "assignment" + ## inputStrings[2] = "would crash" + ## #inputStrings[3] = "out of bounds" proc newSeq*[T](len = 0): seq[T] = ## creates a new sequence of type ``seq[T]`` with length ``len``. + ## + ## Note that the sequence will be filled with uninitialized entries, which + ## can be a problem for sequences containing strings. After the creation of + ## the sequence you should assign entries to the sequence instead of adding + ## them. Example: + ## + ## .. code-block:: nimrod + ## var inputStrings = newSeq[string](3) + ## inputStrings[0] = "The fourth" + ## inputStrings[1] = "assignment" + ## inputStrings[2] = "would crash" + ## #inputStrings[3] = "out of bounds" newSeq(result, len) proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {. @@ -900,7 +925,7 @@ template sysAssert(cond: bool, msg: string) = include "system/inclrtl" -when not defined(ecmascript) and not defined(nimrodVm) and hostOS != "standalone": +when not defined(JS) and not defined(nimrodVm) and hostOS != "standalone": include "system/cgprocs" proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.} @@ -1170,7 +1195,7 @@ proc `$` *(x: int64): string {.magic: "Int64ToStr", noSideEffect.} ## converted to a decimal string. when not defined(NimrodVM): - when not defined(ECMAScript): + when not defined(JS): proc `$` *(x: uint64): string {.noSideEffect.} ## The stingify operator for an unsigned integer argument. Returns `x` ## converted to a decimal string. @@ -1278,10 +1303,11 @@ proc min*(x, y: int32): int32 {.magic: "MinI", noSideEffect.} proc min*(x, y: int64): int64 {.magic: "MinI64", noSideEffect.} ## The minimum value of two integers. -proc min*[T](x: varargs[T]): T = - ## The minimum value of `x`. +proc min*[T](x: varargs[T]): T = + ## The minimum value of `x`. ``T`` needs to have a ``<`` operator. result = x[0] - for i in 1..high(x): result = min(result, x[i]) + for i in 1..high(x): + if x[i] < result: result = x[i] proc max*(x, y: int): int {.magic: "MaxI", noSideEffect.} proc max*(x, y: int8): int8 {.magic: "MaxI", noSideEffect.} @@ -1290,10 +1316,11 @@ proc max*(x, y: int32): int32 {.magic: "MaxI", noSideEffect.} proc max*(x, y: int64): int64 {.magic: "MaxI64", noSideEffect.} ## The maximum value of two integers. -proc max*[T](x: varargs[T]): T = - ## The maximum value of `x`. +proc max*[T](x: varargs[T]): T = + ## The maximum value of `x`. ``T`` needs to have a ``<`` operator. result = x[0] - for i in 1..high(x): result = max(result, x[i]) + for i in 1..high(x): + if result < x[i]: result = x[i] proc clamp*[T](x, a, b: T): T = ## limits the value ``x`` within the interval [a, b] @@ -1422,7 +1449,7 @@ proc `&` *[T](x: T, y: seq[T]): seq[T] {.noSideEffect.} = result[y.len] = x when not defined(NimrodVM): - when not defined(ECMAScript): + when not defined(JS): proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} = result = cast[pointer](x) else: @@ -1508,30 +1535,31 @@ proc map*[T](data: var openArray[T], op: proc (x: var T) {.closure.}) = ## # --> ["142", "242", "342", "442"] for i in 0..data.len-1: op(data[i]) -iterator fields*[T: tuple](x: T): TObject {. +iterator fields*[T: tuple|object](x: T): TObject {. magic: "Fields", noSideEffect.} ## iterates over every field of `x`. Warning: This really transforms ## the 'for' and unrolls the loop. The current implementation also has a bug ## that affects symbol binding in the loop body. -iterator fields*[S: tuple, T: tuple](x: S, y: T): tuple[a, b: expr] {. +iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: expr] {. magic: "Fields", noSideEffect.} ## iterates over every field of `x` and `y`. ## Warning: This is really transforms the 'for' and unrolls the loop. ## The current implementation also has a bug that affects symbol binding ## in the loop body. -iterator fieldPairs*[T: tuple](x: T): TObject {. +iterator fieldPairs*[T: tuple|object](x: T): TObject {. magic: "FieldPairs", noSideEffect.} ## iterates over every field of `x`. Warning: This really transforms ## the 'for' and unrolls the loop. The current implementation also has a bug ## that affects symbol binding in the loop body. -iterator fieldPairs*[S: tuple, T: tuple](x: S, y: T): tuple[a, b: expr] {. +iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[ + a, b: expr] {. magic: "FieldPairs", noSideEffect.} ## iterates over every field of `x` and `y`. ## Warning: This really transforms the 'for' and unrolls the loop. ## The current implementation also has a bug that affects symbol binding ## in the loop body. -proc `==`*[T: tuple](x, y: T): bool = +proc `==`*[T: tuple|object](x, y: T): bool = ## generic ``==`` operator for tuples that is lifted from the components ## of `x` and `y`. for a, b in fields(x, y): @@ -1556,7 +1584,7 @@ proc `<`*[T: tuple](x, y: T): bool = if c > 0: return false return false -proc `$`*[T: tuple](x: T): string = +proc `$`*[T: tuple|object](x: T): string = ## generic ``$`` operator for tuples that is lifted from the components ## of `x`. Example: ## @@ -1571,6 +1599,18 @@ proc `$`*[T: tuple](x: T): string = result.add($value) result.add(")") +proc `$`*[T: set](x: T): string = + ## generic ``$`` operator for sets that is lifted from the components + ## of `x`. Example: + ## + ## .. code-block:: nimrod + ## ${23, 45} == "{23, 45}" + result = "{" + for value in items(x): + if result.len > 1: result.add(", ") + result.add($value) + result.add("}") + when false: proc `$`*[T](a: openArray[T]): string = ## generic ``$`` operator for open arrays that is lifted from the elements @@ -1649,10 +1689,6 @@ const nimrodStackTrace = compileOption("stacktrace") # of the code var - dbgLineHook*: proc () {.nimcall.} - ## set this variable to provide a procedure that should be called before - ## each executed instruction. This should only be used by debuggers! - ## Only code compiled with the ``debugger:on`` switch calls this hook. globalRaiseHook*: proc (e: ref E_Base): bool {.nimcall.} ## with this hook you can influence exception handling on a global level. ## If not nil, every 'raise' statement ends up calling this hook. Ordinary @@ -1690,15 +1726,16 @@ var ## continues and the program is terminated. type - PFrame = ptr TFrame - TFrame {.importc, nodecl, final.} = object - prev: PFrame - procname: CString - line: int # current line number - filename: CString - len: int # length of slots (when not debugging always zero) + PFrame* = ptr TFrame ## represents a runtime frame of the call stack; + ## part of the debugger API. + TFrame* {.importc, nodecl, final.} = object ## the frame itself + prev*: PFrame ## previous frame; used for chaining the call stack + procname*: cstring ## name of the proc that is currently executing + line*: int ## line number of the proc that is currently executing + filename*: cstring ## filename of the proc that is currently executing + len*: int ## length of the inspectable slots -when not defined(ECMAScript): +when not defined(JS): {.push stack_trace:off, profiler:off.} proc add*(x: var string, y: cstring) {.noStackFrame.} = var i = 0 @@ -1724,7 +1761,7 @@ proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo", tags: [FWriteIO].} ## is converted to a string via ``$``, so it works for user-defined ## types that have an overloaded ``$`` operator. ## It is roughly equivalent to ``writeln(stdout, x); flush(stdout)``, but - ## available for the ECMAScript target too. + ## available for the JavaScript target too. ## Unlike other IO operations this is guaranteed to be thread-safe as ## ``echo`` is very often used for debugging convenience. @@ -1747,7 +1784,7 @@ proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo".} ## get type information for `x`. Ordinary code should not use this, but ## the `typeinfo` module instead. -when not defined(EcmaScript): #and not defined(NimrodVM): +when not defined(JS): #and not defined(NimrodVM): {.push stack_trace: off, profiler:off.} when not defined(NimrodVM): @@ -2166,7 +2203,7 @@ when not defined(EcmaScript): #and not defined(NimrodVM): `result` = *((NI*) `x`.ClEnv) < 0; """.} -elif defined(ecmaScript): +elif defined(JS): # Stubs: proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = nil @@ -2192,8 +2229,8 @@ elif defined(ecmaScript): proc deallocShared(p: pointer) = nil proc reallocShared(p: pointer, newsize: int): pointer = nil - when defined(ecmaScript): - include "system/ecmasys" + when defined(JS): + include "system/jssys" include "system/reprjs" elif defined(NimrodVM): proc cmp(x, y: string): int = @@ -2370,12 +2407,40 @@ proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.} ## converts the AST of `x` into a string representation. This is very useful ## for debugging. -proc InstantiationInfo*(index = -1): tuple[filename: string, line: int] {. - magic: "InstantiationInfo", noSideEffect.} +proc InstantiationInfo*(index = -1, fullPaths = false): tuple[ + filename: string, line: int] {. magic: "InstantiationInfo", noSideEffect.} ## provides access to the compiler's instantiation stack line information. - ## This is only useful for advanced meta programming. See the implementation - ## of `assert` for an example. - + ## + ## This proc is mostly useful for meta programming (eg. ``assert`` template) + ## to retrieve information about the current filename and line number. + ## Example: + ## + ## .. code-block:: nimrod + ## import strutils + ## + ## template testException(exception, code: expr): stmt = + ## try: + ## let pos = instantiationInfo() + ## discard(code) + ## echo "Test failure at $1:$2 with '$3'" % [pos.filename, + ## $pos.line, astToStr(code)] + ## assert false, "A test expecting failure succeeded?" + ## except exception: + ## nil + ## + ## proc tester(pos: int): int = + ## let + ## a = @[1, 2, 3] + ## result = a[pos] + ## + ## when isMainModule: + ## testException(EInvalidIndex, tester(30)) + ## testException(EInvalidIndex, tester(1)) + ## # --> Test failure at example.nim:20 with 'tester(1)' + +template CurrentSourcePath*: string = InstantiationInfo(-1, true).filename + ## returns the full file-system path of the current source + proc raiseAssert*(msg: string) {.noinline.} = raise newException(EAssertionFailed, msg) @@ -2437,7 +2502,7 @@ proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} = ## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not ## perform deep copies of `s`. This is only useful for optimization ## purposes. - when not defined(EcmaScript) and not defined(NimrodVM): + when not defined(JS) and not defined(NimrodVM): var s = cast[PGenericSeq](s) s.reserved = s.reserved or seqShallowFlag @@ -2445,7 +2510,7 @@ proc shallow*(s: var string) {.noSideEffect, inline.} = ## marks a string `s` as `shallow`:idx:. Subsequent assignments will not ## perform deep copies of `s`. This is only useful for optimization ## purposes. - when not defined(EcmaScript) and not defined(NimrodVM): + when not defined(JS) and not defined(NimrodVM): var s = cast[PGenericSeq](s) s.reserved = s.reserved or seqShallowFlag @@ -2500,3 +2565,10 @@ proc safeAdd*(x: var string, y: string) = if x == nil: x = y else: x.add(y) +proc locals*(): TObject {.magic: "Locals", noSideEffect.} = + ## generates a tuple constructor expression listing all the local variables + ## in the current scope. This is quite fast as it does not rely + ## on any debug or runtime information. Note that in constrast to what + ## the official signature says, the return type is not ``TObject`` but a + ## tuple of a structure that depends on the current scope. + nil diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim old mode 100755 new mode 100644 index 127df755e..744c26d36 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -303,7 +303,35 @@ iterator elements(t: TIntSet): int {.inline.} = w = w shr 1 inc(i) r = r.next - + +proc isSmallChunk(c: PChunk): bool {.inline.} = + return c.size <= SmallChunkSize-smallChunkOverhead() + +proc chunkUnused(c: PChunk): bool {.inline.} = + result = not c.used + +iterator allObjects(m: TMemRegion): pointer {.inline.} = + for s in elements(m.chunkStarts): + # we need to check here again as it could have been modified: + if s in m.chunkStarts: + let c = cast[PChunk](s shl PageShift) + if not chunkUnused(c): + if isSmallChunk(c): + var c = cast[PSmallChunk](c) + + let size = c.size + var a = cast[TAddress](addr(c.data)) + let limit = a + c.acc + while a <% limit: + yield cast[pointer](a) + a = a +% size + else: + let c = cast[PBigChunk](c) + yield addr(c.data) + +proc isCell(p: pointer): bool {.inline.} = + result = cast[ptr TFreeCell](p).zeroField >% 1 + # ------------- chunk management ---------------------------------------------- proc pageIndex(c: PChunk): int {.inline.} = result = cast[TAddress](c) shr PageShift @@ -398,12 +426,6 @@ proc ListRemove[T](head: var T, c: T) {.inline.} = c.next = nil c.prev = nil -proc isSmallChunk(c: PChunk): bool {.inline.} = - return c.size <= SmallChunkSize-smallChunkOverhead() - -proc chunkUnused(c: PChunk): bool {.inline.} = - result = not c.used - proc updatePrevSize(a: var TMemRegion, c: PBigChunk, prevSize: int) {.inline.} = var ri = cast[PChunk](cast[TAddress](c) +% c.size) diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim old mode 100755 new mode 100644 diff --git a/lib/system/arithm.nim b/lib/system/arithm.nim old mode 100755 new mode 100644 diff --git a/lib/system/assign.nim b/lib/system/assign.nim old mode 100755 new mode 100644 diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim old mode 100755 new mode 100644 diff --git a/lib/system/cellsets.nim b/lib/system/cellsets.nim old mode 100755 new mode 100644 diff --git a/lib/system/cgprocs.nim b/lib/system/cgprocs.nim old mode 100755 new mode 100644 diff --git a/lib/system/channels.nim b/lib/system/channels.nim old mode 100755 new mode 100644 diff --git a/lib/system/debugger.nim b/lib/system/debugger.nim old mode 100755 new mode 100644 index f234c9daf..eade1707f --- a/lib/system/debugger.nim +++ b/lib/system/debugger.nim @@ -7,187 +7,95 @@ # distribution, for details about the copyright. # -# This file implements the embedded debugger that can be linked -# with the application. Mostly we do not use dynamic memory here as that -# would interfere with the GC and trigger ON/OFF errors if the -# user program corrupts memory. Unfortunately, for dispaying -# variables we use the ``system.repr()`` proc which uses Nimrod -# strings and thus allocates memory from the heap. Pity, but -# I do not want to implement ``repr()`` twice. +## This file implements basic features for any debugger. type - TStaticStr {.pure, final.} = object - len: int - data: array[0..100, char] - - TDbgState = enum - dbOff, # debugger is turned off - dbStepInto, # debugger is in tracing mode - dbStepOver, - dbSkipCurrent, - dbQuiting, # debugger wants to quit - dbBreakpoints # debugger is only interested in breakpoints - - TDbgBreakpoint {.final.} = object - low, high: int # range from low to high; if disabled - # both low and high are set to their negative values - # this makes the check faster and safes memory - filename: cstring - name: TStaticStr # name of breakpoint - - TVarSlot {.compilerproc, final.} = object # variable slots used for debugger: - address: pointer - typ: PNimType - name: cstring # for globals this is "module.name" + TVarSlot* {.compilerproc, final.} = object ## a slot in a frame + address*: pointer ## the variable's address + typ*: PNimType ## the variable's type + name*: cstring ## the variable's name; for globals this is "module.name" PExtendedFrame = ptr TExtendedFrame - TExtendedFrame {.final.} = object # If the debugger is enabled the compiler - # provides an extended frame. Of course - # only slots that are - # needed are allocated and not 10_000, - # except for the global data description. + TExtendedFrame = object # If the debugger is enabled the compiler + # provides an extended frame. Of course + # only slots that are + # needed are allocated and not 10_000, + # except for the global data description. f: TFrame slots: array[0..10_000, TVarSlot] var - dbgUser: TStaticStr # buffer for user input; first command is ``step_into`` - # needs to be global cause we store the last command - # in it - dbgState: TDbgState # state of debugger - dbgBP: array[0..127, TDbgBreakpoint] # breakpoints - dbgBPlen: int - - dbgSkipToFrame: PFrame # frame to be skipped to - dbgGlobalData: TExtendedFrame # this reserves much space, but # for now it is the most practical way - maxDisplayRecDepth: int = 5 # do not display too much data! +proc dbgRegisterGlobal(name: cstring, address: pointer, + typ: PNimType) {.compilerproc.} = + let i = dbgGlobalData.f.len + if i >= high(dbgGlobalData.slots): + #debugOut("[Warning] cannot register global ") + return + dbgGlobalData.slots[i].name = name + dbgGlobalData.slots[i].typ = typ + dbgGlobalData.slots[i].address = address + inc(dbgGlobalData.f.len) -proc setLen(s: var TStaticStr, newLen=0) = - s.len = newLen - s.data[newLen] = '\0' +proc getLocal*(frame: PFrame; slot: int): TVarSlot {.inline.} = + ## retrieves the meta data for the local variable at `slot`. CAUTION: An + ## invalid `slot` value causes a corruption! + result = cast[PExtendedFrame](frame).slots[slot] -proc add(s: var TStaticStr, c: char) = - if s.len < high(s.data)-1: - s.data[s.len] = c - s.data[s.len+1] = '\0' - inc s.len +proc getGlobalLen*(): int {.inline.} = + ## gets the number of registered globals. + result = dbgGlobalData.f.len -proc add(s: var TStaticStr, c: cstring) = - var i = 0 - while c[i] != '\0': - add s, c[i] - inc i +proc getGlobal*(slot: int): TVarSlot {.inline.} = + ## retrieves the meta data for the global variable at `slot`. CAUTION: An + ## invalid `slot` value causes a corruption! + result = dbgGlobalData.slots[slot] -proc assign(s: var TStaticStr, c: cstring) = - setLen(s) - add s, c +# ------------------- breakpoint support ------------------------------------ -proc `==`(a, b: TStaticStr): bool = - if a.len == b.len: - for i in 0 .. a.len-1: - if a.data[i] != b.data[i]: return false - return true +type + TBreakpoint* = object ## represents a break point + low*, high*: int ## range from low to high; if disabled + ## both low and high are set to their negative values + filename*: cstring ## the filename of the breakpoint -proc `==`(a: TStaticStr, b: cstring): bool = - result = c_strcmp(a.data, b) == 0 +var + dbgBP: array[0..127, TBreakpoint] # breakpoints + dbgBPlen: int + dbgBPbloom: int64 # we use a bloom filter to speed up breakpoint checking + + dbgFilenames*: array[0..300, cstring] ## registered filenames; + ## 'nil' terminated + dbgFilenameLen: int -proc findBreakpoint(name: TStaticStr): int = - # returns -1 if not found - for i in countdown(dbgBPlen-1, 0): - if name == dbgBP[i].name: return i - return -1 +proc dbgRegisterFilename(filename: cstring) {.compilerproc.} = + # XXX we could check for duplicates here for DLL support + dbgFilenames[dbgFilenameLen] = filename + inc dbgFilenameLen -proc write(f: TFile, s: TStaticStr) = - write(f, cstring(s.data)) +proc dbgRegisterBreakpoint(line: int, + filename, name: cstring) {.compilerproc.} = + let x = dbgBPlen + if x >= high(dbgBP): + #debugOut("[Warning] cannot register breakpoint") + return + inc(dbgBPlen) + dbgBP[x].filename = filename + dbgBP[x].low = line + dbgBP[x].high = line + dbgBPbloom = dbgBPbloom or line -proc ListBreakPoints() = - write(stdout, "*** endb| Breakpoints:\n") - for i in 0 .. dbgBPlen-1: - write(stdout, dbgBP[i].name) - write(stdout, ": ") - write(stdout, abs(dbgBP[i].low)) - write(stdout, "..") - write(stdout, abs(dbgBP[i].high)) - write(stdout, dbgBP[i].filename) - if dbgBP[i].low < 0: - write(stdout, " [disabled]\n") - else: - write(stdout, "\n") - write(stdout, "***\n") - -proc openAppend(filename: cstring): TFile = - var p: pointer = fopen(filename, "ab") - if p != nil: - result = cast[TFile](p) - write(result, "----------------------------------------\n") - -proc dbgRepr(p: pointer, typ: PNimType): string = - var cl: TReprClosure - initReprClosure(cl) - cl.recDepth = maxDisplayRecDepth - # locks for the GC turned out to be a bad idea... - # inc(recGcLock) - result = "" - reprAux(result, p, typ, cl) - # dec(recGcLock) - deinitReprClosure(cl) - -proc writeVariable(stream: TFile, slot: TVarSlot) = - write(stream, slot.name) - write(stream, " = ") - writeln(stream, dbgRepr(slot.address, slot.typ)) - -proc ListFrame(stream: TFile, f: PExtendedFrame) = - write(stream, "*** endb| Frame (") - write(stream, f.f.len) - write(stream, " slots):\n") - for i in 0 .. f.f.len-1: - writeVariable(stream, f.slots[i]) - write(stream, "***\n") - -proc ListVariables(stream: TFile, f: PExtendedFrame) = - write(stream, "*** endb| Frame (") - write(stream, f.f.len) - write(stream, " slots):\n") - for i in 0 .. f.f.len-1: - writeln(stream, f.slots[i].name) - write(stream, "***\n") - -proc debugOut(msg: cstring) = - # the *** *** markers are for easy recognition of debugger - # output for external frontends. - write(stdout, "*** endb| ") - write(stdout, msg) - write(stdout, "***\n") - -proc dbgFatal(msg: cstring) = - debugOut(msg) - dbgAborting = True # the debugger wants to abort - quit(1) - -proc findVariable(frame: PExtendedFrame, varname: cstring): int = - for i in 0 .. frame.f.len - 1: - if c_strcmp(frame.slots[i].name, varname) == 0: return i - return -1 - -proc dbgShowCurrentProc(dbgFramePointer: PFrame) = - if dbgFramePointer != nil: - write(stdout, "*** endb| now in proc: ") - write(stdout, dbgFramePointer.procname) - write(stdout, " ***\n") - else: - write(stdout, "*** endb| (proc name not available) ***\n") - -proc dbgShowExecutionPoint() = - write(stdout, "*** endb| ") - write(stdout, framePtr.filename) - write(stdout, "(") - write(stdout, framePtr.line) - write(stdout, ") ") - write(stdout, framePtr.procname) - write(stdout, " ***\n") +proc addBreakpoint*(filename: cstring, lo, hi: int): bool = + let x = dbgBPlen + if x >= high(dbgBP): return false + inc(dbgBPlen) + result = true + dbgBP[x].filename = filename + dbgBP[x].low = lo + dbgBP[x].high = hi + for line in lo..hi: dbgBPbloom = dbgBPbloom or line const FileSystemCaseInsensitive = defined(windows) or defined(dos) or defined(os2) @@ -207,9 +115,8 @@ proc fileMatches(c, bp: cstring): bool = return false var i = 0 while i < blen: - var x, y: char - x = bp[i] - y = c[i+clen-blen] + var x = bp[i] + var y = c[i+clen-blen] when FileSystemCaseInsensitive: if x >= 'A' and x <= 'Z': x = chr(ord(x) - ord('A') + ord('a')) if y >= 'A' and y <= 'Z': y = chr(ord(y) - ord('A') + ord('a')) @@ -217,334 +124,29 @@ proc fileMatches(c, bp: cstring): bool = inc(i) return true -proc dbgBreakpointReached(line: int): int = - for i in 0..dbgBPlen-1: - if line >= dbgBP[i].low and line <= dbgBP[i].high and - fileMatches(framePtr.filename, dbgBP[i].filename): return i - return -1 +proc canonFilename*(filename: cstring): cstring = + ## returns 'nil' if the filename cannot be found. + for i in 0 .. 0 +proc flip*(b: ptr TBreakpoint) = + ## enables or disables 'b' depending on its current state. + b.low = -b.low; b.high = -b.high -proc scanFilename(src: cstring, a: var TStaticStr, start: int): int = - result = start - setLen a - # skip whitespace: - while src[result] in {'\t', ' '}: inc(result) - while src[result] notin {'\t', ' ', '\0'}: - add(a, src[result]) - inc(result) +proc checkBreakpoints*(filename: cstring, line: int): ptr TBreakpoint = + ## in which breakpoint (if any) we are. + if (dbgBPbloom and line) != line: return nil + for b in listBreakpoints(): + if line >= b.low and line <= b.high and filename == b.filename: return b -proc scanNumber(src: cstring, a: var int, start: int): int = - result = start - a = 0 - while src[result] in {'\t', ' '}: inc(result) - while true: - case src[result] - of '0'..'9': a = a * 10 + ord(src[result]) - ord('0') - of '_': nil # skip underscores (nice for long line numbers) - else: break - inc(result) - -proc dbgHelp() = - debugOut(""" -list of commands (see the manual for further help): - GENERAL -h, help display this help message -q, quit quit the debugger and the program - repeat the previous debugger command - EXECUTING -s, step single step, stepping into routine calls -n, next single step, without stepping into routine calls -f, skipcurrent continue execution until the current routine finishes -c, continue, r, run continue execution until the next breakpoint -i, ignore continue execution, ignore all breakpoints - BREAKPOINTS -b, break [fromline [toline]] [file] - set a new breakpoint named 'name' for line and file - if line or file are omitted the current one is used -breakpoints display the entire breakpoint list -disable disable a breakpoint -enable enable a breakpoint - DATA DISPLAY -e, eval evaluate the expression -o, out evaluate and write it to -w, where display the current execution point -stackframe [file] display current stack frame [and write it to file] -u, up go up in the call stack -d, down go down in the call stack -bt, backtrace display the entire call stack -l, locals display available local variables -g, globals display available global variables -maxdisplay set the display's recursion maximum -""") - -proc InvalidCommand() = - debugOut("[Warning] invalid command ignored (type 'h' for help) ") - -proc hasExt(s: cstring): bool = - # returns true if s has a filename extension - var i = 0 - while s[i] != '\0': - if s[i] == '.': return true - inc i - -proc setBreakPoint(s: cstring, start: int) = - var dbgTemp: TStaticStr - var i = scanWord(s, dbgTemp, start) - if i <= start: - InvalidCommand() - return - if dbgBPlen >= high(dbgBP): - debugOut("[Warning] no breakpoint could be set; out of breakpoint space ") - return - var x = dbgBPlen - inc(dbgBPlen) - dbgBP[x].name = dbgTemp - i = scanNumber(s, dbgBP[x].low, i) - if dbgBP[x].low == 0: - # set to current line: - dbgBP[x].low = framePtr.line - i = scanNumber(s, dbgBP[x].high, i) - if dbgBP[x].high == 0: # set to low: - dbgBP[x].high = dbgBP[x].low - i = scanFilename(s, dbgTemp, i) - if dbgTemp.len != 0: - debugOut("[Warning] explicit filename for breakpoint not supported") - when false: - if not hasExt(dbgTemp.data): add(dbgTemp, ".nim") - dbgBP[x].filename = dbgTemp - dbgBP[x].filename = framePtr.filename - else: # use current filename - dbgBP[x].filename = framePtr.filename - # skip whitespace: - while s[i] in {' ', '\t'}: inc(i) - if s[i] != '\0': - dec(dbgBPLen) # remove buggy breakpoint - InvalidCommand() - -proc BreakpointSetEnabled(s: cstring, start, enabled: int) = - var dbgTemp: TStaticStr - var i = scanWord(s, dbgTemp, start) - if i <= start: - InvalidCommand() - return - var x = findBreakpoint(dbgTemp) - if x < 0: debugOut("[Warning] breakpoint does not exist ") - elif enabled * dbgBP[x].low < 0: # signs are different? - dbgBP[x].low = -dbgBP[x].low - dbgBP[x].high = -dbgBP[x].high - -proc dbgEvaluate(stream: TFile, s: cstring, start: int, - currFrame: PExtendedFrame) = - var dbgTemp: tstaticstr - var i = scanWord(s, dbgTemp, start) - while s[i] in {' ', '\t'}: inc(i) - var f = currFrame - if s[i] == '.': - inc(i) # skip '.' - add(dbgTemp, '.') - i = scanAndAppendWord(s, dbgTemp, i) - # search for global var: - f = addr(dbgGlobalData) - if s[i] != '\0': - debugOut("[Warning] could not parse expr ") - return - var j = findVariable(f, dbgTemp.data) - if j < 0: - debugOut("[Warning] could not find variable ") - return - writeVariable(stream, f.slots[j]) - -proc dbgOut(s: cstring, start: int, currFrame: PExtendedFrame) = - var dbgTemp: tstaticstr - var i = scanFilename(s, dbgTemp, start) - if dbgTemp.len == 0: - InvalidCommand() - return - var stream = openAppend(dbgTemp.data) - if stream == nil: - debugOut("[Warning] could not open or create file ") - return - dbgEvaluate(stream, s, i, currFrame) - close(stream) - -proc dbgStackFrame(s: cstring, start: int, currFrame: PExtendedFrame) = - var dbgTemp: TStaticStr - var i = scanFilename(s, dbgTemp, start) - if dbgTemp.len == 0: - # just write it to stdout: - ListFrame(stdout, currFrame) - else: - var stream = openAppend(dbgTemp.data) - if stream == nil: - debugOut("[Warning] could not open or create file ") - return - ListFrame(stream, currFrame) - close(stream) - -proc readLine(f: TFile, line: var TStaticStr): bool = - while True: - var c = fgetc(f) - if c < 0'i32: - if line.len > 0: break - else: return false - if c == 10'i32: break # LF - if c == 13'i32: # CR - c = fgetc(f) # is the next char LF? - if c != 10'i32: ungetc(c, f) # no, put the character back - break - add line, chr(int(c)) - result = true - -proc dbgWriteStackTrace(f: PFrame) -proc CommandPrompt() = - # if we return from this routine, user code executes again - var - again = True - dbgFramePtr = framePtr # for going down and up the stack - dbgDown = 0 # how often we did go down - dbgTemp: TStaticStr - - while again: - write(stdout, "*** endb| >>") - let oldLen = dbgUser.len - dbgUser.len = 0 - if not readLine(stdin, dbgUser): break - if dbgUser.len == 0: dbgUser.len = oldLen - # now look what we have to do: - var i = scanWord(dbgUser.data, dbgTemp, 0) - template `?`(x: expr): expr = dbgTemp == cstring(x) - if ?"s" or ?"step": - dbgState = dbStepInto - again = false - elif ?"n" or ?"next": - dbgState = dbStepOver - dbgSkipToFrame = framePtr - again = false - elif ?"f" or ?"skipcurrent": - dbgState = dbSkipCurrent - dbgSkipToFrame = framePtr.prev - again = false - elif ?"c" or ?"continue" or ?"r" or ?"run": - dbgState = dbBreakpoints - again = false - elif ?"i" or ?"ignore": - dbgState = dbOff - again = false - elif ?"h" or ?"help": - dbgHelp() - elif ?"q" or ?"quit": - dbgState = dbQuiting - dbgAborting = True - again = false - quit(1) # BUGFIX: quit with error code > 0 - elif ?"e" or ?"eval": - dbgEvaluate(stdout, dbgUser.data, i, cast[PExtendedFrame](dbgFramePtr)) - elif ?"o" or ?"out": - dbgOut(dbgUser.data, i, cast[PExtendedFrame](dbgFramePtr)) - elif ?"stackframe": - dbgStackFrame(dbgUser.data, i, cast[PExtendedFrame](dbgFramePtr)) - elif ?"w" or ?"where": - dbgShowExecutionPoint() - elif ?"l" or ?"locals": - ListVariables(stdout, cast[PExtendedFrame](dbgFramePtr)) - elif ?"g" or ?"globals": - ListVariables(stdout, addr(dbgGlobalData)) - elif ?"u" or ?"up": - if dbgDown <= 0: - debugOut("[Warning] cannot go up any further ") - else: - dbgFramePtr = framePtr - for j in 0 .. dbgDown-2: # BUGFIX - dbgFramePtr = dbgFramePtr.prev - dec(dbgDown) - dbgShowCurrentProc(dbgFramePtr) - elif ?"d" or ?"down": - if dbgFramePtr != nil: - inc(dbgDown) - dbgFramePtr = dbgFramePtr.prev - dbgShowCurrentProc(dbgFramePtr) - else: - debugOut("[Warning] cannot go down any further ") - elif ?"bt" or ?"backtrace": - dbgWriteStackTrace(framePtr) - elif ?"b" or ?"break": - setBreakPoint(dbgUser.data, i) - elif ?"breakpoints": - ListBreakPoints() - elif ?"disable": - BreakpointSetEnabled(dbgUser.data, i, -1) - elif ?"enable": - BreakpointSetEnabled(dbgUser.data, i, +1) - elif ?"maxdisplay": - var parsed: int - i = scanNumber(dbgUser.data, parsed, i) - if dbgUser.data[i-1] in {'0'..'9'}: - if parsed == 0: maxDisplayRecDepth = -1 - else: maxDisplayRecDepth = parsed - else: - InvalidCommand() - else: InvalidCommand() - -proc endbStep() = - # we get into here if an unhandled exception has been raised - # XXX: do not allow the user to run the program any further? - # XXX: BUG: the frame is lost here! - dbgShowExecutionPoint() - CommandPrompt() - -proc checkForBreakpoint() = - let i = dbgBreakpointReached(framePtr.line) - if i >= 0: - write(stdout, "*** endb| reached ") - write(stdout, dbgBP[i].name) - write(stdout, " in ") - write(stdout, framePtr.filename) - write(stdout, "(") - write(stdout, framePtr.line) - write(stdout, ") ") - write(stdout, framePtr.procname) - write(stdout, " ***\n") - CommandPrompt() - -# interface to the user program: - -proc dbgRegisterBreakpoint(line: int, - filename, name: cstring) {.compilerproc.} = - let x = dbgBPlen - if x >= high(dbgBP): - debugOut("[Warning] cannot register breakpoint") - return - inc(dbgBPlen) - dbgBP[x].name.assign(name) - dbgBP[x].filename = filename - dbgBP[x].low = line - dbgBP[x].high = line - -proc dbgRegisterGlobal(name: cstring, address: pointer, - typ: PNimType) {.compilerproc.} = - let i = dbgGlobalData.f.len - if i >= high(dbgGlobalData.slots): - debugOut("[Warning] cannot register global ") - return - dbgGlobalData.slots[i].name = name - dbgGlobalData.slots[i].typ = typ - dbgGlobalData.slots[i].address = address - inc(dbgGlobalData.f.len) +# ------------------- watchpoint support ------------------------------------ type THash = int @@ -648,10 +250,6 @@ proc genericHashAux(dest: Pointer, mt: PNimType, shallow: bool, var s = cast[ppointer](dest)[] if s != nil: result = result !& genericHashAux(s, mt.base, shallow, result) - # hash the object header: - #const headerSize = sizeof(int)*2 - #result = result !& hash(cast[pointer](cast[int](s) -% headerSize), - # headerSize) else: result = h !& hash(dest, mt.size) # hash raw bits @@ -667,7 +265,7 @@ proc dbgRegisterWatchpoint(address: pointer, name: cstring, Watchpoints[i].address = address return if L >= watchPoints.high: - debugOut("[Warning] cannot register watchpoint") + #debugOut("[Warning] cannot register watchpoint") return Watchpoints[L].name = name Watchpoints[L].address = address @@ -678,99 +276,28 @@ proc dbgRegisterWatchpoint(address: pointer, name: cstring, proc dbgUnregisterWatchpoints*() = WatchpointsLen = 0 -proc dbgWriteStackTrace(f: PFrame) = - const - firstCalls = 32 - var - it = f - i = 0 - total = 0 - tempFrames: array [0..127, PFrame] - # setup long head: - while it != nil and i <= high(tempFrames)-firstCalls: - tempFrames[i] = it - inc(i) - inc(total) - it = it.prev - # go up the stack to count 'total': - var b = it - while it != nil: - inc(total) - it = it.prev - var skipped = 0 - if total > len(tempFrames): - # skip N - skipped = total-i-firstCalls+1 - for j in 1..skipped: - if b != nil: b = b.prev - # create '...' entry: - tempFrames[i] = nil - inc(i) - # setup short tail: - while b != nil and i <= high(tempFrames): - tempFrames[i] = b - inc(i) - b = b.prev - for j in countdown(i-1, 0): - if tempFrames[j] == nil: - write(stdout, "(") - write(stdout, skipped) - write(stdout, " calls omitted) ...") - else: - write(stdout, tempFrames[j].filename) - if tempFrames[j].line > 0: - write(stdout, '(') - write(stdout, tempFrames[j].line) - write(stdout, ')') - write(stdout, ' ') - write(stdout, tempFrames[j].procname) - write(stdout, "\n") - -proc strstr(s1, s2: cstring): cstring {.importc, header: "".} +var + dbgLineHook*: proc () {.nimcall.} + ## set this variable to provide a procedure that should be called before + ## each executed instruction. This should only be used by debuggers! + ## Only code compiled with the ``debugger:on`` switch calls this hook. -proc interestingFilename(filename: cstring): bool = - #result = strstr(filename, "/rst.nim") == nil - result = true + dbgWatchpointHook*: proc (watchpointName: cstring) {.nimcall.} proc checkWatchpoints = let L = WatchpointsLen for i in 0.. repeat the previous debugger command + EXECUTING +s, step single step, stepping into routine calls +n, next single step, without stepping into routine calls +f, skipcurrent continue execution until the current routine finishes +c, continue, r, run continue execution until the next breakpoint +i, ignore continue execution, ignore all breakpoints + BREAKPOINTS +b, break [fromline [toline]] [file] + set a new breakpoint for line and file + if line or file are omitted the current one is used +breakpoints display the entire breakpoint list +toggle fromline [file] enable or disable a breakpoint +filenames list all valid filenames + DATA DISPLAY +e, eval evaluate the expression +o, out evaluate and write it to +w, where display the current execution point +stackframe [file] display current stack frame [and write it to file] +u, up go up in the call stack +d, down go down in the call stack +bt, backtrace display the entire call stack +l, locals display available local variables +g, globals display available global variables +maxdisplay set the display's recursion maximum +""") + +proc InvalidCommand() = + debugOut("[Warning] invalid command ignored (type 'h' for help) ") + +proc hasExt(s: cstring): bool = + # returns true if s has a filename extension + var i = 0 + while s[i] != '\0': + if s[i] == '.': return true + inc i + +proc parseBreakpoint(s: cstring, start: int): TBreakpoint = + var dbgTemp: TStaticStr + var i = scanNumber(s, result.low, start) + if result.low == 0: result.low = framePtr.line + i = scanNumber(s, result.high, i) + if result.high == 0: result.high = result.low + i = scanFilename(s, dbgTemp, i) + if dbgTemp.len != 0: + if not hasExt(dbgTemp.data): add(dbgTemp, ".nim") + result.filename = canonFilename(dbgTemp.data.cstring) + if result.filename.isNil: + debugOut("[Warning] no breakpoint could be set; unknown filename ") + return + else: + result.filename = framePtr.filename + +proc createBreakPoint(s: cstring, start: int) = + let br = parseBreakpoint(s, start) + if not br.filename.isNil: + if not addBreakpoint(br.filename, br.low, br.high): + debugOut("[Warning] no breakpoint could be set; out of breakpoint space ") + +proc BreakpointToggle(s: cstring, start: int) = + var a = parseBreakpoint(s, start) + if not a.filename.isNil: + var b = checkBreakpoints(a.filename, a.low) + if not b.isNil: b.flip + else: debugOut("[Warning] unknown breakpoint ") + +proc dbgEvaluate(stream: TFile, s: cstring, start: int, f: PFrame) = + var dbgTemp: tstaticstr + var i = scanWord(s, dbgTemp, start) + while s[i] in {' ', '\t'}: inc(i) + var v: TVarSlot + if s[i] == '.': + inc(i) + add(dbgTemp, '.') + i = scanAndAppendWord(s, dbgTemp, i) + for i in 0 .. getGlobalLen()-1: + let v = getGlobal(i) + if c_strcmp(v.name, dbgTemp.data) == 0: + writeVariable(stream, v) + else: + for i in 0 .. f.len-1: + let v = getLocal(f, i) + if c_strcmp(v.name, dbgTemp.data) == 0: + writeVariable(stream, v) + +proc dbgOut(s: cstring, start: int, currFrame: PFrame) = + var dbgTemp: tstaticstr + var i = scanFilename(s, dbgTemp, start) + if dbgTemp.len == 0: + InvalidCommand() + return + var stream = openAppend(dbgTemp.data) + if stream == nil: + debugOut("[Warning] could not open or create file ") + return + dbgEvaluate(stream, s, i, currFrame) + close(stream) + +proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) = + var dbgTemp: TStaticStr + var i = scanFilename(s, dbgTemp, start) + if dbgTemp.len == 0: + # just write it to stdout: + ListFrame(stdout, currFrame) + else: + var stream = openAppend(dbgTemp.data) + if stream == nil: + debugOut("[Warning] could not open or create file ") + return + ListFrame(stream, currFrame) + close(stream) + +proc readLine(f: TFile, line: var TStaticStr): bool = + while True: + var c = fgetc(f) + if c < 0'i32: + if line.len > 0: break + else: return false + if c == 10'i32: break # LF + if c == 13'i32: # CR + c = fgetc(f) # is the next char LF? + if c != 10'i32: ungetc(c, f) # no, put the character back + break + add line, chr(int(c)) + result = true + +proc ListFilenames() = + write(stdout, EndbBeg) + write(stdout, "| Files:\n") + var i = 0 + while true: + let x = dbgFilenames[i] + if x.isNil: break + write(stdout, x) + write(stdout, "\n") + inc i + write(stdout, EndbEnd) + +proc dbgWriteStackTrace(f: PFrame) +proc CommandPrompt() = + # if we return from this routine, user code executes again + var + again = True + dbgFramePtr = framePtr # for going down and up the stack + dbgDown = 0 # how often we did go down + dbgTemp: TStaticStr + + while again: + write(stdout, "*** endb| >>") + let oldLen = dbgUser.len + dbgUser.len = 0 + if not readLine(stdin, dbgUser): break + if dbgUser.len == 0: dbgUser.len = oldLen + # now look what we have to do: + var i = scanWord(dbgUser.data, dbgTemp, 0) + template `?`(x: expr): expr = dbgTemp == cstring(x) + if ?"s" or ?"step": + dbgState = dbStepInto + again = false + elif ?"n" or ?"next": + dbgState = dbStepOver + dbgSkipToFrame = framePtr + again = false + elif ?"f" or ?"skipcurrent": + dbgState = dbSkipCurrent + dbgSkipToFrame = framePtr.prev + again = false + elif ?"c" or ?"continue" or ?"r" or ?"run": + dbgState = dbBreakpoints + again = false + elif ?"i" or ?"ignore": + dbgState = dbOff + again = false + elif ?"h" or ?"help": + dbgHelp() + elif ?"q" or ?"quit": + dbgState = dbQuiting + dbgAborting = True + again = false + quit(1) # BUGFIX: quit with error code > 0 + elif ?"e" or ?"eval": + dbgEvaluate(stdout, dbgUser.data, i, dbgFramePtr) + elif ?"o" or ?"out": + dbgOut(dbgUser.data, i, dbgFramePtr) + elif ?"stackframe": + dbgStackFrame(dbgUser.data, i, dbgFramePtr) + elif ?"w" or ?"where": + dbgShowExecutionPoint() + elif ?"l" or ?"locals": + ListLocals(stdout, dbgFramePtr) + elif ?"g" or ?"globals": + ListGlobals(stdout) + elif ?"u" or ?"up": + if dbgDown <= 0: + debugOut("[Warning] cannot go up any further ") + else: + dbgFramePtr = framePtr + for j in 0 .. dbgDown-2: # BUGFIX + dbgFramePtr = dbgFramePtr.prev + dec(dbgDown) + dbgShowCurrentProc(dbgFramePtr) + elif ?"d" or ?"down": + if dbgFramePtr != nil: + inc(dbgDown) + dbgFramePtr = dbgFramePtr.prev + dbgShowCurrentProc(dbgFramePtr) + else: + debugOut("[Warning] cannot go down any further ") + elif ?"bt" or ?"backtrace": + dbgWriteStackTrace(framePtr) + elif ?"b" or ?"break": + createBreakPoint(dbgUser.data, i) + elif ?"breakpoints": + ListBreakPoints() + elif ?"toggle": + BreakpointToggle(dbgUser.data, i) + elif ?"filenames": + ListFilenames() + elif ?"maxdisplay": + var parsed: int + i = scanNumber(dbgUser.data, parsed, i) + if dbgUser.data[i-1] in {'0'..'9'}: + if parsed == 0: maxDisplayRecDepth = -1 + else: maxDisplayRecDepth = parsed + else: + InvalidCommand() + else: InvalidCommand() + +proc endbStep() = + # we get into here if an unhandled exception has been raised + # XXX: do not allow the user to run the program any further? + # XXX: BUG: the frame is lost here! + dbgShowExecutionPoint() + CommandPrompt() + +proc dbgWriteStackTrace(f: PFrame) = + const + firstCalls = 32 + var + it = f + i = 0 + total = 0 + tempFrames: array [0..127, PFrame] + # setup long head: + while it != nil and i <= high(tempFrames)-firstCalls: + tempFrames[i] = it + inc(i) + inc(total) + it = it.prev + # go up the stack to count 'total': + var b = it + while it != nil: + inc(total) + it = it.prev + var skipped = 0 + if total > len(tempFrames): + # skip N + skipped = total-i-firstCalls+1 + for j in 1..skipped: + if b != nil: b = b.prev + # create '...' entry: + tempFrames[i] = nil + inc(i) + # setup short tail: + while b != nil and i <= high(tempFrames): + tempFrames[i] = b + inc(i) + b = b.prev + for j in countdown(i-1, 0): + if tempFrames[j] == nil: + write(stdout, "(") + write(stdout, skipped) + write(stdout, " calls omitted) ...") + else: + write(stdout, tempFrames[j].filename) + if tempFrames[j].line > 0: + write(stdout, '(') + write(stdout, tempFrames[j].line) + write(stdout, ')') + write(stdout, ' ') + write(stdout, tempFrames[j].procname) + write(stdout, "\n") + +proc checkForBreakpoint = + let b = checkBreakpoints(framePtr.filename, framePtr.line) + if b != nil: + write(stdout, "*** endb| reached ") + write(stdout, framePtr.filename) + write(stdout, "(") + write(stdout, framePtr.line) + write(stdout, ") ") + write(stdout, framePtr.procname) + write(stdout, " ***\n") + CommandPrompt() + +proc lineHookImpl() {.nimcall.} = + case dbgState + of dbStepInto: + # we really want the command prompt here: + dbgShowExecutionPoint() + CommandPrompt() + of dbSkipCurrent, dbStepOver: # skip current routine + if framePtr == dbgSkipToFrame: + dbgShowExecutionPoint() + CommandPrompt() + else: + # breakpoints are wanted though (I guess) + checkForBreakpoint() + of dbBreakpoints: + # debugger is only interested in breakpoints + checkForBreakpoint() + else: nil + +proc watchpointHookImpl(name: cstring) {.nimcall.} = + dbgWriteStackTrace(framePtr) + debugOut(name) + +proc initDebugger {.inline.} = + dbgState = dbStepInto + dbgUser.len = 1 + dbgUser.data[0] = 's' + dbgWatchpointHook = watchpointHookImpl + dbgLineHook = lineHookImpl diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim old mode 100755 new mode 100644 diff --git a/lib/system/gc.nim b/lib/system/gc.nim index bd8c7a5da..0ab5f4d94 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -18,9 +18,6 @@ # for soft real time applications (like games). {.push profiler:off.} -# XXX there is still a slight chance of leaking cycles as we don't add cycle -# candidates in 'incRef' - const CycleIncrease = 2 # is a multiplicative increase InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow @@ -201,14 +198,21 @@ proc decRef(c: PCell) {.inline.} = proc incRef(c: PCell) {.inline.} = gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr") - c.refcount = c.refCount +% rcIncrement and not colorMask + c.refcount = c.refCount +% rcIncrement + # and not colorMask #writeCell("incRef", c) - #if canBeCycleRoot(c): - # rtlAddCycleRoot(c) + if canBeCycleRoot(c): + rtlAddCycleRoot(c) proc nimGCref(p: pointer) {.compilerProc, inline.} = incRef(usrToCell(p)) proc nimGCunref(p: pointer) {.compilerProc, inline.} = decRef(usrToCell(p)) +proc GC_addCycleRoot*[T](p: ref T) {.inline.} = + ## adds 'p' to the cycle candidate set for the cycle collector. It is + ## necessary if you used the 'acyclic' pragma for optimization + ## purposes and need to break cycles manually. + rtlAddCycleRoot(usrToCell(cast[pointer](p))) + proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} = sysAssert(allocInv(gch.region), "begin nimGCunrefNoCycle") var c = usrToCell(p) diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim old mode 100755 new mode 100644 diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 246a42870..9ebc27a9f 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -7,11 +7,18 @@ # distribution, for details about the copyright. # -# A simple mark&sweep garbage collector for Nimrod. +# A simple mark&sweep garbage collector for Nimrod. Define the +# symbol ``gcUseBitvectors`` to generate a variant of this GC. {.push profiler:off.} const InitialThreshold = 4*1024*1024 # X MB because marking&sweeping is slow + withBitvectors = defined(gcUseBitvectors) + # bitvectors are significantly faster for GC-bench, but slower for + # bootstrapping and use more memory + rcWhite = 0 + rcGrey = 1 # unused + rcBlack = 2 template mulThreshold(x): expr {.immediate.} = x * 2 @@ -41,7 +48,8 @@ type # non-zero count table stackBottom: pointer cycleThreshold: int - allocated, marked: TCellSet + when withBitvectors: + allocated, marked: TCellSet tempStack: TCellSeq # temporary stack for recursion elimination recGcLock: int # prevent recursion via finalizers; no thread lock region: TMemRegion # garbage collected region @@ -125,9 +133,11 @@ proc prepareDealloc(cell: PCell) = proc nimGCref(p: pointer) {.compilerProc, inline.} = # we keep it from being collected by pretending it's not even allocated: - excl(gch.allocated, usrToCell(p)) + when withBitvectors: excl(gch.allocated, usrToCell(p)) + else: usrToCell(p).refcount = rcBlack proc nimGCunref(p: pointer) {.compilerProc, inline.} = - incl(gch.allocated, usrToCell(p)) + when withBitvectors: incl(gch.allocated, usrToCell(p)) + else: usrToCell(p).refcount = rcWhite proc initGC() = when not defined(useNimRtl): @@ -136,8 +146,9 @@ proc initGC() = gch.stat.maxThreshold = 0 gch.stat.maxStackSize = 0 init(gch.tempStack) - Init(gch.allocated) - init(gch.marked) + when withBitvectors: + Init(gch.allocated) + init(gch.marked) proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) = var d = cast[TAddress](dest) @@ -200,7 +211,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer = res.line = framePtr.prev.line res.refcount = 0 release(gch) - incl(gch.allocated, res) + when withBitvectors: incl(gch.allocated, res) result = cellToUsr(res) {.pop.} @@ -246,11 +257,11 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = zeroMem(cast[pointer](cast[TAddress](res)+% oldsize +% sizeof(TCell)), newsize-oldsize) sysAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "growObj: 3") - excl(gch.allocated, ol) + when withBitvectors: excl(gch.allocated, ol) when reallyDealloc: rawDealloc(gch.region, ol) else: zeroMem(ol, sizeof(TCell)) - incl(gch.allocated, res) + when withBitvectors: incl(gch.allocated, res) release(gch) result = cellToUsr(res) when defined(memProfiler): nimProfile(newsize-oldsize) @@ -263,14 +274,25 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} = # ----------------- collector ----------------------------------------------- proc mark(gch: var TGcHeap, c: PCell) = - incl(gch.marked, c) - gcAssert gch.tempStack.len == 0, "stack not empty!" - forAllChildren(c, waMarkPrecise) - while gch.tempStack.len > 0: - dec gch.tempStack.len - var d = gch.tempStack.d[gch.tempStack.len] - if not containsOrIncl(gch.marked, d): - forAllChildren(d, waMarkPrecise) + when withBitvectors: + incl(gch.marked, c) + gcAssert gch.tempStack.len == 0, "stack not empty!" + forAllChildren(c, waMarkPrecise) + while gch.tempStack.len > 0: + dec gch.tempStack.len + var d = gch.tempStack.d[gch.tempStack.len] + if not containsOrIncl(gch.marked, d): + forAllChildren(d, waMarkPrecise) + else: + c.refCount = rcBlack + gcAssert gch.tempStack.len == 0, "stack not empty!" + forAllChildren(c, waMarkPrecise) + while gch.tempStack.len > 0: + dec gch.tempStack.len + var d = gch.tempStack.d[gch.tempStack.len] + if d.refcount == rcWhite: + d.refCount = rcBlack + forAllChildren(d, waMarkPrecise) proc doOperation(p: pointer, op: TWalkOp) = if p == nil: return @@ -298,9 +320,17 @@ proc freeCyclicCell(gch: var TGcHeap, c: PCell) = zeroMem(c, sizeof(TCell)) proc sweep(gch: var TGcHeap) = - for c in gch.allocated.elementsExcept(gch.marked): - gch.allocated.excl(c) - freeCyclicCell(gch, c) + when withBitvectors: + for c in gch.allocated.elementsExcept(gch.marked): + gch.allocated.excl(c) + freeCyclicCell(gch, c) + else: + for x in allObjects(gch.region): + if isCell(x): + # cast to PCell is correct here: + var c = cast[PCell](x) + if c.refcount == rcBlack: c.refcount = rcWhite + else: freeCyclicCell(gch, c) proc markGlobals(gch: var TGcHeap) = for i in 0 .. < globalMarkersLen: globalMarkers[i]() @@ -451,8 +481,9 @@ proc collectCTBody(gch: var TGcHeap) = sweep(gch) inc(gch.stat.collections) - deinit(gch.marked) - init(gch.marked) + when withBitvectors: + deinit(gch.marked) + init(gch.marked) gch.cycleThreshold = max(InitialThreshold, getOccupiedMem().mulThreshold) gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold) sysAssert(allocInv(gch.region), "collectCT: end") diff --git a/lib/system/hti.nim b/lib/system/hti.nim old mode 100755 new mode 100644 diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim old mode 100755 new mode 100644 diff --git a/lib/system/ecmasys.nim b/lib/system/jssys.nim old mode 100755 new mode 100644 similarity index 98% rename from lib/system/ecmasys.nim rename to lib/system/jssys.nim index f78b7f68c..1c43bfdc7 --- a/lib/system/ecmasys.nim +++ b/lib/system/jssys.nim @@ -42,7 +42,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} = result = newString(1) result[0] = x -proc getCurrentExceptionMsg(): string = +proc getCurrentExceptionMsg*(): string = if excHandler != nil: return $excHandler.exc.msg return "" @@ -151,7 +151,7 @@ proc cstrToNimstr(c: cstring): string {.noStackFrame, compilerproc.} = return result; """ -proc toEcmaStr(s: string): cstring {.noStackFrame, compilerproc.} = +proc toJSStr(s: string): cstring {.noStackFrame, compilerproc.} = asm """ var len = `s`.length-1; var result = new Array(len); @@ -304,7 +304,7 @@ when defined(kwin): asm """ var buf = ""; for (var i = 0; i < arguments.length; ++i) { - buf += `toEcmaStr`(arguments[i]); + buf += `toJSStr`(arguments[i]); } print(buf); """ @@ -316,7 +316,7 @@ elif defined(nodejs): asm """ var buf = ""; for (var i = 0; i < arguments.length; ++i) { - buf += `toEcmaStr`(arguments[i]); + buf += `toJSStr`(arguments[i]); } console.log(buf); """ @@ -338,7 +338,7 @@ else: if node == nil: raise newException(EIO, " element does not exist yet!") asm """ for (var i = 0; i < arguments.length; ++i) { - var x = `toEcmaStr`(arguments[i]); + var x = `toJSStr`(arguments[i]); `node`.appendChild(document.createTextNode(x)) } """ @@ -491,6 +491,7 @@ proc toU32(a: int): int32 {.noStackFrame, compilerproc.} = proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b +type NimString = string # hack for hti.nim include "system/hti" proc isFatPointer(ti: PNimType): bool = diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim old mode 100755 new mode 100644 index eee98fd52..9f37e95c1 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -1,7 +1,7 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -312,6 +312,8 @@ else: elif defined(gcMarkAndSweep): # XXX use 'compileOption' here include "system/gc_ms" + elif defined(gcGenerational): + include "system/gc_genms" else: include "system/gc" diff --git a/lib/system/profiler.nim b/lib/system/profiler.nim old mode 100755 new mode 100644 diff --git a/lib/system/repr.nim b/lib/system/repr.nim old mode 100755 new mode 100644 diff --git a/lib/system/sets.nim b/lib/system/sets.nim old mode 100755 new mode 100644 diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim old mode 100755 new mode 100644 index c7accbac0..3d5a53f03 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -36,7 +36,9 @@ proc ftell(f: TFile): int {.importc: "ftell", noDecl, tags: [].} proc setvbuf(stream: TFile, buf: pointer, typ, size: cint): cint {. importc, nodecl, tags: [].} +{.push stackTrace:off, profiler:off.} proc write(f: TFile, c: cstring) = fputs(c, f) +{.pop.} var IOFBF {.importc: "_IOFBF", nodecl.}: cint diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim old mode 100755 new mode 100644 diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim old mode 100755 new mode 100644 index 2e60c6153..d62a987ff --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -73,12 +73,13 @@ proc cstrToNimstr(str: CString): NimString {.compilerRtl.} = result = toNimstr(str, c_strlen(str)) proc copyString(src: NimString): NimString {.compilerRtl.} = - if (src.reserved and seqShallowFlag) != 0: - result = src - elif src != nil: - result = rawNewString(src.space) - result.len = src.len - c_memcpy(result.data, src.data, (src.len + 1) * sizeof(Char)) + if src != nil: + if (src.reserved and seqShallowFlag) != 0: + result = src + else: + result = rawNewString(src.space) + result.len = src.len + c_memcpy(result.data, src.data, (src.len + 1) * sizeof(Char)) proc copyStringRC1(src: NimString): NimString {.compilerRtl.} = if src != nil: diff --git a/lib/system/threads.nim b/lib/system/threads.nim old mode 100755 new mode 100644 diff --git a/lib/windows/mmsystem.nim b/lib/windows/mmsystem.nim old mode 100755 new mode 100644 diff --git a/lib/windows/nb30.nim b/lib/windows/nb30.nim old mode 100755 new mode 100644 diff --git a/lib/windows/ole2.nim b/lib/windows/ole2.nim old mode 100755 new mode 100644 diff --git a/lib/windows/psapi.nim b/lib/windows/psapi.nim old mode 100755 new mode 100644 diff --git a/lib/windows/shellapi.nim b/lib/windows/shellapi.nim old mode 100755 new mode 100644 diff --git a/lib/windows/shfolder.nim b/lib/windows/shfolder.nim old mode 100755 new mode 100644 diff --git a/lib/windows/windows.nim b/lib/windows/windows.nim old mode 100755 new mode 100644 diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/cairo/cairo.nim b/lib/wrappers/cairo/cairo.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/cairo/cairoft.nim b/lib/wrappers/cairo/cairoft.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/cairo/cairowin32.nim b/lib/wrappers/cairo/cairowin32.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/cairo/cairoxlib.nim b/lib/wrappers/cairo/cairoxlib.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/claro.nim b/lib/wrappers/claro.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/expat.nim b/lib/wrappers/expat.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/gtk/atk.nim b/lib/wrappers/gtk/atk.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/gtk/gdk2.nim b/lib/wrappers/gtk/gdk2.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/gtk/gdk2pixbuf.nim b/lib/wrappers/gtk/gdk2pixbuf.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/gtk/gdkglext.nim b/lib/wrappers/gtk/gdkglext.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/gtk/glib2.nim b/lib/wrappers/gtk/glib2.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/gtk/gtk2.nim b/lib/wrappers/gtk/gtk2.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/gtk/gtkglext.nim b/lib/wrappers/gtk/gtkglext.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/gtk/gtkhtml.nim b/lib/wrappers/gtk/gtkhtml.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/gtk/libglade2.nim b/lib/wrappers/gtk/libglade2.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/gtk/pango.nim b/lib/wrappers/gtk/pango.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/gtk/pangoutils.nim b/lib/wrappers/gtk/pangoutils.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/iup.nim b/lib/wrappers/iup.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/libcurl.nim b/lib/wrappers/libcurl.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/lua/lauxlib.nim b/lib/wrappers/lua/lauxlib.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/lua/lua.nim b/lib/wrappers/lua/lua.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/lua/lualib.nim b/lib/wrappers/lua/lualib.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/odbcsql.nim b/lib/wrappers/odbcsql.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/opengl/gl.nim b/lib/wrappers/opengl/gl.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/opengl/glext.nim b/lib/wrappers/opengl/glext.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/opengl/glu.nim b/lib/wrappers/opengl/glu.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/opengl/glut.nim b/lib/wrappers/opengl/glut.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/opengl/glx.nim b/lib/wrappers/opengl/glx.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/opengl/wingl.nim b/lib/wrappers/opengl/wingl.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim old mode 100755 new mode 100644 index 438774a15..ca4ad6a99 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -233,6 +233,7 @@ proc SSL_read*(ssl: PSSL, buf: pointer, num: int): cint{.cdecl, dynlib: DLLSSLNa proc SSL_write*(ssl: PSSL, buf: cstring, num: int): cint{.cdecl, dynlib: DLLSSLName, importc.} proc SSL_get_error*(s: PSSL, ret_code: cInt): cInt{.cdecl, dynlib: DLLSSLName, importc.} proc SSL_accept*(ssl: PSSL): cInt{.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_pending*(ssl: PSSL): cInt{.cdecl, dynlib: DLLSSLName, importc.} proc BIO_new_ssl_connect*(ctx: PSSL_CTX): PBIO{.cdecl, dynlib: DLLSSLName, importc.} @@ -323,7 +324,6 @@ else: dynlib: DLLSSLName, importc.} proc SslWrite*(ssl: PSSL, buf: SslPtr, num: cInt): cInt{.cdecl, dynlib: DLLSSLName, importc.} - proc SslPending*(ssl: PSSL): cInt{.cdecl, dynlib: DLLSSLName, importc.} proc SslGetVersion*(ssl: PSSL): cstring{.cdecl, dynlib: DLLSSLName, importc.} proc SslGetPeerCertificate*(ssl: PSSL): PX509{.cdecl, dynlib: DLLSSLName, importc.} diff --git a/lib/wrappers/pcre.nim b/lib/wrappers/pcre.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/postgres.nim b/lib/wrappers/postgres.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/python.nim b/lib/wrappers/python.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/readline/history.nim b/lib/wrappers/readline/history.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/readline/readline.nim b/lib/wrappers/readline/readline.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/readline/rltypedefs.nim b/lib/wrappers/readline/rltypedefs.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/readline/tweaked/history.h b/lib/wrappers/readline/tweaked/history.h old mode 100755 new mode 100644 diff --git a/lib/wrappers/readline/tweaked/readline.h b/lib/wrappers/readline/tweaked/readline.h old mode 100755 new mode 100644 diff --git a/lib/wrappers/readline/tweaked/rltypedefs.h b/lib/wrappers/readline/tweaked/rltypedefs.h old mode 100755 new mode 100644 diff --git a/lib/wrappers/readline/tweaked/tilde.h b/lib/wrappers/readline/tweaked/tilde.h old mode 100755 new mode 100644 diff --git a/lib/wrappers/sdl/sdl.nim b/lib/wrappers/sdl/sdl.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/sdl/sdl_gfx.nim b/lib/wrappers/sdl/sdl_gfx.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/sdl/sdl_image.nim b/lib/wrappers/sdl/sdl_image.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/sdl/sdl_mixer.nim b/lib/wrappers/sdl/sdl_mixer.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/sdl/sdl_mixer_nosmpeg.nim b/lib/wrappers/sdl/sdl_mixer_nosmpeg.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/sdl/sdl_net.nim b/lib/wrappers/sdl/sdl_net.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/sdl/sdl_ttf.nim b/lib/wrappers/sdl/sdl_ttf.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/sdl/smpeg.nim b/lib/wrappers/sdl/smpeg.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/sphinx.nim b/lib/wrappers/sphinx.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/sqlite3.nim b/lib/wrappers/sqlite3.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/tcl.nim b/lib/wrappers/tcl.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/tinyc.nim b/lib/wrappers/tinyc.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/tre.nim b/lib/wrappers/tre.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/cursorfont.nim b/lib/wrappers/x11/cursorfont.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/keysym.nim b/lib/wrappers/x11/keysym.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/x.nim b/lib/wrappers/x11/x.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xatom.nim b/lib/wrappers/x11/xatom.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xcms.nim b/lib/wrappers/x11/xcms.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xf86dga.nim b/lib/wrappers/x11/xf86dga.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xf86vmode.nim b/lib/wrappers/x11/xf86vmode.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xi.nim b/lib/wrappers/x11/xi.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xinerama.nim b/lib/wrappers/x11/xinerama.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xkb.nim b/lib/wrappers/x11/xkb.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xkblib.nim b/lib/wrappers/x11/xkblib.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xlib.nim b/lib/wrappers/x11/xlib.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xrandr.nim b/lib/wrappers/x11/xrandr.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xrender.nim b/lib/wrappers/x11/xrender.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xresource.nim b/lib/wrappers/x11/xresource.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xshm.nim b/lib/wrappers/x11/xshm.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xutil.nim b/lib/wrappers/x11/xutil.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xv.nim b/lib/wrappers/x11/xv.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/x11/xvlib.nim b/lib/wrappers/x11/xvlib.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/zip/libzip.nim b/lib/wrappers/zip/libzip.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/zip/libzip_all.c b/lib/wrappers/zip/libzip_all.c old mode 100755 new mode 100644 diff --git a/lib/wrappers/zip/zlib.nim b/lib/wrappers/zip/zlib.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/zip/zzip.nim b/lib/wrappers/zip/zzip.nim old mode 100755 new mode 100644 diff --git a/lib/wrappers/zmq.nim b/lib/wrappers/zmq.nim old mode 100755 new mode 100644 diff --git a/readme.md b/readme.md index 3e39b5f77..ee24cd683 100644 --- a/readme.md +++ b/readme.md @@ -42,7 +42,7 @@ $ bin/nimrod c koch $ ./koch boot -d:release ``` -The install script (``install.sh``) may then be used to install Nimrod, or you +``koch install [dir]`` may then be used to install Nimrod, or you can simply add it to your PATH. The above steps can be performed on Windows in a similar fashion, the diff --git a/readme.txt b/readme.txt old mode 100755 new mode 100644 index 3e39b5f77..ee24cd683 --- a/readme.txt +++ b/readme.txt @@ -42,7 +42,7 @@ $ bin/nimrod c koch $ ./koch boot -d:release ``` -The install script (``install.sh``) may then be used to install Nimrod, or you +``koch install [dir]`` may then be used to install Nimrod, or you can simply add it to your PATH. The above steps can be performed on Windows in a similar fashion, the diff --git a/start.bat b/start.bat old mode 100755 new mode 100644 diff --git a/tests/caas/basic-recompile.txt b/tests/caas/basic-recompile.txt new file mode 100644 index 000000000..9c943fe88 --- /dev/null +++ b/tests/caas/basic-recompile.txt @@ -0,0 +1,7 @@ +main.nim +> c +SuccessX +> c +! Processing +SuccessX + diff --git a/tests/caas/compile-then-def.txt b/tests/caas/compile-then-def.txt new file mode 100644 index 000000000..a5080c50d --- /dev/null +++ b/tests/caas/compile-then-def.txt @@ -0,0 +1,9 @@ +main.nim +> c +SuccessX +> idetools --track:main.nim,5,18 --def main.nim +strutils.toUpper +SuccessX +> idetools --track:main.nim,5,18 --def main.nim +strutils.toUpper +SuccessX diff --git a/tests/caas/def-then-compile.txt b/tests/caas/def-then-compile.txt new file mode 100644 index 000000000..efa4dcbb3 --- /dev/null +++ b/tests/caas/def-then-compile.txt @@ -0,0 +1,7 @@ +main.nim +> idetools --track:main.nim,5,18 --def main.nim +strutils.toUpper +SuccessX +> c +SuccessX + diff --git a/tests/caas/imported.nim b/tests/caas/imported.nim new file mode 100644 index 000000000..a4bc5c0e6 --- /dev/null +++ b/tests/caas/imported.nim @@ -0,0 +1,3 @@ +proc `+++`*(a,b: string): string = + return a & " " & b + diff --git a/tests/caas/main.nim b/tests/caas/main.nim new file mode 100644 index 000000000..fafeff93b --- /dev/null +++ b/tests/caas/main.nim @@ -0,0 +1,7 @@ +import imported, strutils + +proc main = + var t1 = "text" + var t2 = t1.toUpper + echo(t1 +++ t2) + diff --git a/tests/caasdriver.nim b/tests/caasdriver.nim new file mode 100644 index 000000000..3f3d3671e --- /dev/null +++ b/tests/caasdriver.nim @@ -0,0 +1,86 @@ +import osproc, streams, os, strutils, re + +type + TNimrodSession* = object + nim: PProcess + +proc dirname(path: string): string = path.splitPath()[0] + +var + TesterDir = getAppDir() + NimrodBin = TesterDir / "../bin/nimrod" + +proc startNimrodSession*(project: string): TNimrodSession = + result.nim = startProcess(NimrodBin, + workingDir = project.dirname, + args = ["serve", "--server.type:stdin", project]) + +proc doCommand*(session: var TNimrodSession, command: string): string = + session.nim.inputStream.write(command & "\n") + session.nim.inputStream.flush + + result = "" + + while true: + var line = TaintedString("") + if session.nim.outputStream.readLine(line): + if line.string == "": break + result.add(line.string & "\n") + else: + result = "FAILED TO EXECUTE: " & command & "\n" & result + break + +proc close(session: var TNimrodSession) {.destructor.} = + session.nim.close + +proc doScenario(script: string, output: PStream): bool = + result = true + + var f = open(script) + var project = TaintedString("") + + if f.readLine(project): + var + s = startNimrodSession(script.dirname / project.string) + tline = TaintedString("") + lastOutput = "" + ln = 1 + + while f.readLine(tline): + var line = tline.string + inc ln + if line.strip.len == 0: continue + + if line.startsWith(">"): + lastOutput = s.doCommand(line.substr(1).strip) + output.writeln line, "\n", lastOutput + else: + var expectMatch = true + var pattern = line + if line.startsWith("!"): + pattern = line.substr(1).strip + expectMatch = false + + var actualMatch = lastOutput.find(re(pattern)) != -1 + + if expectMatch == actualMatch: + output.writeln "SUCCESS ", line + else: + output.writeln "FAILURE ", line + result = false + +iterator caasTestsRunner*(filter = ""): tuple[test, output: string, + status: bool] = + for scenario in os.walkFiles(TesterDir / "caas/*.txt"): + if filter.len > 0 and find(scenario, filter) == -1: continue + var outStream = newStringStream() + let r = doScenario(scenario, outStream) + yield (scenario, outStream.data, r) + +when isMainModule: + var filter = "" + if paramCount() > 0: filter = paramStr(1) + + for t, o, r in caasTestsRunner(filter): + echo t, "\n", o + diff --git a/tests/compile/mrecmod.nim b/tests/compile/mrecmod.nim old mode 100755 new mode 100644 diff --git a/tests/compile/mrecmod2.nim b/tests/compile/mrecmod2.nim old mode 100755 new mode 100644 diff --git a/tests/compile/mvarious.nim b/tests/compile/mvarious.nim old mode 100755 new mode 100644 diff --git a/tests/compile/sunset.tmpl b/tests/compile/sunset.tmpl old mode 100755 new mode 100644 diff --git a/tests/compile/tambsym2.nim b/tests/compile/tambsym2.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tarrindx.nim b/tests/compile/tarrindx.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tassign.nim b/tests/compile/tassign.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tcan_alias_generic.nim b/tests/compile/tcan_alias_generic.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tcan_alias_specialised_generic.nim b/tests/compile/tcan_alias_specialised_generic.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tcan_inherit_generic.nim b/tests/compile/tcan_inherit_generic.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tcan_specialise_generic.nim b/tests/compile/tcan_specialise_generic.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tccgen1.nim b/tests/compile/tccgen1.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tcmdline.nim b/tests/compile/tcmdline.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tcodegenbug1.nim b/tests/compile/tcodegenbug1.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tcolors.nim b/tests/compile/tcolors.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tconsteval.nim b/tests/compile/tconsteval.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tconstraints.nim b/tests/compile/tconstraints.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tconvcolors.nim b/tests/compile/tconvcolors.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tconvert.nim b/tests/compile/tconvert.nim old mode 100755 new mode 100644 index f96cf2688..a8ddcf119 --- a/tests/compile/tconvert.nim +++ b/tests/compile/tconvert.nim @@ -36,3 +36,11 @@ for i in 1 .. count-1: echo(surface.WriteToPNG(outFile)) surface.Destroy() +type TFoo = object + +converter toPtr*(some: var TFoo): ptr TFoo = (addr some) + + +proc zoot(x: ptr TFoo) = nil +var x: Tfoo +zoot(x) diff --git a/tests/compile/tcputime.nim b/tests/compile/tcputime.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tdialogs.nim b/tests/compile/tdialogs.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tdictdestruct.nim b/tests/compile/tdictdestruct.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tdllvar.nim b/tests/compile/tdllvar.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tdumpast.nim b/tests/compile/tdumpast.nim old mode 100755 new mode 100644 diff --git a/tests/compile/techo.nim b/tests/compile/techo.nim old mode 100755 new mode 100644 diff --git a/tests/compile/temptyecho.nim b/tests/compile/temptyecho.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tendian.nim b/tests/compile/tendian.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tenum.nim b/tests/compile/tenum.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tenum2.nim b/tests/compile/tenum2.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tenum3.nim b/tests/compile/tenum3.nim old mode 100755 new mode 100644 diff --git a/tests/compile/teval1.nim b/tests/compile/teval1.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tfib.nim b/tests/compile/tfib.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tforwty.nim b/tests/compile/tforwty.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tforwty2.nim b/tests/compile/tforwty2.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tgeneric2.nim b/tests/compile/tgeneric2.nim index b9b8e5a62..56803017a 100644 --- a/tests/compile/tgeneric2.nim +++ b/tests/compile/tgeneric2.nim @@ -8,4 +8,8 @@ proc foo(models: seq[TX]): seq[int] = for model in models.items: result.add model["foobar"] - +type + obj = object + field: TTable[string, string] +var t: Obj +discard initTable[type(t.field), string]() diff --git a/tests/compile/tgenericmatcher.nim b/tests/compile/tgenericmatcher.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tgenericmatcher2.nim b/tests/compile/tgenericmatcher2.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tgenericprocvar.nim b/tests/compile/tgenericprocvar.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tgenericrefs.nim b/tests/compile/tgenericrefs.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tgenericvariant.nim b/tests/compile/tgenericvariant.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tgetstartmilsecs.nim b/tests/compile/tgetstartmilsecs.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tgtk.nim b/tests/compile/tgtk.nim old mode 100755 new mode 100644 diff --git a/tests/compile/thallo.nim b/tests/compile/thallo.nim old mode 100755 new mode 100644 diff --git a/tests/compile/thexrange.nim b/tests/compile/thexrange.nim old mode 100755 new mode 100644 diff --git a/tests/compile/thygienictempl.nim b/tests/compile/thygienictempl.nim index 98983dc55..3cdbac40e 100644 --- a/tests/compile/thygienictempl.nim +++ b/tests/compile/thygienictempl.nim @@ -8,3 +8,11 @@ template t() = echo(foo) var foo = 12 t() + + +template test_in(a, b, c: expr): bool {.immediate, dirty.} = + var result {.gensym.}: bool = false + false + +when isMainModule: + assert test_in(ret2, "test", str_val) diff --git a/tests/compile/tident.nim b/tests/compile/tident.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tindent1.nim b/tests/compile/tindent1.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tio.nim b/tests/compile/tio.nim old mode 100755 new mode 100644 diff --git a/tests/compile/titer.nim b/tests/compile/titer.nim old mode 100755 new mode 100644 diff --git a/tests/compile/titer2.nim b/tests/compile/titer2.nim old mode 100755 new mode 100644 diff --git a/tests/compile/titer_no_tuple_unpack.nim b/tests/compile/titer_no_tuple_unpack.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tlastmod.nim b/tests/compile/tlastmod.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tlexer.nim b/tests/compile/tlexer.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tlibs.nim b/tests/compile/tlibs.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tlinearscanend.nim b/tests/compile/tlinearscanend.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tloops.nim b/tests/compile/tloops.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tmacro1.nim b/tests/compile/tmacro1.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tmacro2.nim b/tests/compile/tmacro2.nim new file mode 100644 index 000000000..e7bc648db --- /dev/null +++ b/tests/compile/tmacro2.nim @@ -0,0 +1,59 @@ +import macros,json + +var decls{.compileTime.}: seq[PNimrodNode] = @[] +var impls{.compileTime.}: seq[PNimrodNode] = @[] + +macro importImpl_forward(name, returns): stmt {.immediate.} = + result = newNimNode(nnkEmpty) + var func_name = newNimNode(nnkAccQuoted) + func_name.add newIdentNode("import") + func_name.add name + + var res = newNimNode(nnkProcDef) + res.add newNimNode(nnkPostfix) + res[0].add newIdentNode("*") + res[0].add func_name + res.add newNimNode(nnkEmpty) + res.add newNimNode(nnkEmpty) + res.add newNimNode(nnkFormalParams) + res[3].add returns + var p1 = newNimNode(nnkIdentDefs) + p1.add newIdentNode("dat") + p1.add newIdentNOde("PJsonNode") + p1.add newNimNode(nnkEmpty) + res[3].add p1 + var p2 = newNimNode(nnkIdentDefs) + p2.add newIdentNode("errors") + p2.add newNimNode(nnkVarTy) + p2.add newNimNode(nnkEmpty) + p2[1].add newNimNOde(nnkBracketExpr) + p2[1][0].add newIdentNode("seq") + p2[1][0].add newIdentNode("string") + res[3].add p2 + + res.add newNimNode(nnkEmpty) + res.add newNimNode(nnkEmpty) + res.add newNimNode(nnkEmpty) + + decls.add res + echo(repr(res)) + +macro importImpl(name, returns: expr, body: stmt): stmt {.immediate.} = + #var res = getAST(importImpl_forward(name, returns)) + discard getAST(importImpl_forward(name, returns)) + var res = copyNimTree(decls[decls.high]) + res[6] = body + echo repr(res) + impls.add res + +macro okayy:stmt = + result = newNimNode(nnkStmtList) + for node in decls: result.add node + for node in impls: result.add node + +importimpl(Item, int): + echo 42 +importImpl(Foo, int16): + echo 77 + +okayy \ No newline at end of file diff --git a/tests/compile/tmacrostmt.nim b/tests/compile/tmacrostmt.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tmarshal.nim b/tests/compile/tmarshal.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tmath.nim b/tests/compile/tmath.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tmatrix1.nim b/tests/compile/tmatrix1.nim new file mode 100644 index 000000000..0adf30b57 --- /dev/null +++ b/tests/compile/tmatrix1.nim @@ -0,0 +1,19 @@ +discard """ + output: "right proc called" +""" + +type + TMatrixNM*[M, N, T] = object + aij*: array[M, array[N, T]] + TMatrix2x2*[T] = TMatrixNM[range[0..1], range[0..1], T] + TMatrix3x3*[T] = TMatrixNM[range[0..2], range[0..2], T] + +proc test*[T] (matrix: TMatrix2x2[T]) = + echo "wrong proc called" + +proc test*[T] (matrix: TMatrix3x3[T]) = + echo "right proc called" + +var matrix: TMatrix3x3[float] + +matrix.test diff --git a/tests/compile/tmatrix2.nim b/tests/compile/tmatrix2.nim new file mode 100644 index 000000000..442096e93 --- /dev/null +++ b/tests/compile/tmatrix2.nim @@ -0,0 +1,22 @@ +discard """ + output: "5.0000000000000000e+00" +""" + +type + TMatrixNM*[M, N, T] = object + aij*: T + TVectorN*[N, T] = TMatrixNM[range[0..0], N, T] + TVector3*[T] = TVectorN[range[0..2], T] + +proc coeffRef*[M, N, T] (matrix: var TMatrixNM[M, N, T], a: M, b: N): var T = + return matrix.aij + +proc coeffRef*[N, T] (vector: var TVectorN[N, T], i: N): var T = vector.aij + +var + testVar: TVector3[float] + +testVar.aij = 2.0 +testVar.coeffRef(1) = 5.0 + +echo testVar.aij diff --git a/tests/compile/tmodulealias.nim b/tests/compile/tmodulealias.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tnamedparams.nim b/tests/compile/tnamedparams.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tnew.nim b/tests/compile/tnew.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tnewlibs.nim b/tests/compile/tnewlibs.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tnewsets.nim b/tests/compile/tnewsets.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tnewuns.nim b/tests/compile/tnewuns.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tnimrodnode_for_runtime.nim b/tests/compile/tnimrodnode_for_runtime.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tnoargopenarray.nim b/tests/compile/tnoargopenarray.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tobjconstr2.nim b/tests/compile/tobjconstr2.nim new file mode 100644 index 000000000..ef5446999 --- /dev/null +++ b/tests/compile/tobjconstr2.nim @@ -0,0 +1,8 @@ +type TFoo{.exportc.} = object + x:int + +var s{.exportc.}: seq[TFoo] = @[] + +s.add TFoo(x: 42) + +echo s[0].x diff --git a/tests/compile/tobjcov.nim b/tests/compile/tobjcov.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tobject2.nim b/tests/compile/tobject2.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tobjects.nim b/tests/compile/tobjects.nim old mode 100755 new mode 100644 diff --git a/tests/compile/toop.nim b/tests/compile/toop.nim old mode 100755 new mode 100644 diff --git a/tests/compile/toptions.nim b/tests/compile/toptions.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tos.nim b/tests/compile/tos.nim old mode 100755 new mode 100644 diff --git a/tests/compile/toverprc.nim b/tests/compile/toverprc.nim old mode 100755 new mode 100644 index f3aa66b80..467dd36c3 --- a/tests/compile/toverprc.nim +++ b/tests/compile/toverprc.nim @@ -25,3 +25,11 @@ var x = stdin.readline.split.map(parseInt).max echo x, " is the maximum!" echo "another number: ", takeParseInt(parseInt) + +type + TFoo[a,b] = object + lorem: a + ipsum: b + +proc bar[a,b](f: TFoo[a,b], x: a) = echo(x, " ", f.lorem, f.ipsum) +proc bar[a,b](f: TFoo[a,b], x: b) = echo(x, " ", f.lorem, f.ipsum) diff --git a/tests/compile/tparedef.nim b/tests/compile/tparedef.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tparscfg.nim b/tests/compile/tparscfg.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tparsefloat.nim b/tests/compile/tparsefloat.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tparsopt.nim b/tests/compile/tparsopt.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tposix.nim b/tests/compile/tposix.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tprep.nim b/tests/compile/tprep.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tpush.nim b/tests/compile/tpush.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tquicksort.nim b/tests/compile/tquicksort.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tquit.nim b/tests/compile/tquit.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tradix.nim b/tests/compile/tradix.nim old mode 100755 new mode 100644 diff --git a/tests/compile/treadln.nim b/tests/compile/treadln.nim old mode 100755 new mode 100644 diff --git a/tests/compile/treadx.nim b/tests/compile/treadx.nim old mode 100755 new mode 100644 diff --git a/tests/compile/trecmod.nim b/tests/compile/trecmod.nim old mode 100755 new mode 100644 diff --git a/tests/compile/trecmod2.nim b/tests/compile/trecmod2.nim old mode 100755 new mode 100644 diff --git a/tests/compile/trectuple.nim b/tests/compile/trectuple.nim old mode 100755 new mode 100644 diff --git a/tests/compile/trepr.nim b/tests/compile/trepr.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tseq2.nim b/tests/compile/tseq2.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tseqcon2.nim b/tests/compile/tseqcon2.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tshadow_magic_type.nim b/tests/compile/tshadow_magic_type.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tsizeof.nim b/tests/compile/tsizeof.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tsockets.nim b/tests/compile/tsockets.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tspecialised_is_equivalent.nim b/tests/compile/tspecialised_is_equivalent.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tstrace.nim b/tests/compile/tstrace.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tstrdesc.nim b/tests/compile/tstrdesc.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tstrdist.nim b/tests/compile/tstrdist.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tstreams.nim b/tests/compile/tstreams.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tstrset.nim b/tests/compile/tstrset.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tstrtabs.nim b/tests/compile/tstrtabs.nim old mode 100755 new mode 100644 diff --git a/tests/compile/ttableconstr.nim b/tests/compile/ttableconstr.nim old mode 100755 new mode 100644 diff --git a/tests/compile/ttempl.nim b/tests/compile/ttempl.nim old mode 100755 new mode 100644 diff --git a/tests/compile/ttempl3.nim b/tests/compile/ttempl3.nim old mode 100755 new mode 100644 diff --git a/tests/compile/ttempl4.nim b/tests/compile/ttempl4.nim old mode 100755 new mode 100644 diff --git a/tests/compile/ttemplreturntype.nim b/tests/compile/ttemplreturntype.nim old mode 100755 new mode 100644 diff --git a/tests/compile/ttime.nim b/tests/compile/ttime.nim old mode 100755 new mode 100644 diff --git a/tests/compile/ttuple1.nim b/tests/compile/ttuple1.nim old mode 100755 new mode 100644 diff --git a/tests/compile/ttypeconverter1.nim b/tests/compile/ttypeconverter1.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tuserpragma.nim b/tests/compile/tuserpragma.nim old mode 100755 new mode 100644 diff --git a/tests/compile/tvarious.nim b/tests/compile/tvarious.nim old mode 100755 new mode 100644 diff --git a/tests/compile/twalker.nim b/tests/compile/twalker.nim old mode 100755 new mode 100644 diff --git a/tests/compile/typalias.nim b/tests/compile/typalias.nim old mode 100755 new mode 100644 diff --git a/tests/gc/closureleak.nim b/tests/gc/closureleak.nim new file mode 100644 index 000000000..669e05227 --- /dev/null +++ b/tests/gc/closureleak.nim @@ -0,0 +1,33 @@ +discard """ + outputsub: "true" +""" + +from strutils import join + +type + TFoo * = object + id: int + func: proc(){.closure.} +var foo_counter = 0 +var alive_foos = newseq[int](0) + +proc free*(some: ref TFoo) = + #echo "Tfoo #", some.id, " freed" + alive_foos.del alive_foos.find(some.id) +proc newFoo*(): ref TFoo = + new result, free + + result.id = foo_counter + alive_foos.add result.id + inc foo_counter + +for i in 0 .. <10: + discard newFoo() + +for i in 0 .. <10: + let f = newFoo() + f.func = proc = + echo f.id + +gc_fullcollect() +echo alive_foos.len <= 3 diff --git a/tests/gc/gcbench.nim b/tests/gc/gcbench.nim old mode 100755 new mode 100644 diff --git a/tests/gc/gcleak.nim b/tests/gc/gcleak.nim old mode 100755 new mode 100644 diff --git a/tests/gc/gcleak2.nim b/tests/gc/gcleak2.nim old mode 100755 new mode 100644 diff --git a/tests/gc/gcleak3.nim b/tests/gc/gcleak3.nim old mode 100755 new mode 100644 diff --git a/tests/gc/gctest.nim b/tests/gc/gctest.nim old mode 100755 new mode 100644 diff --git a/tests/ecmas.html b/tests/js.html old mode 100755 new mode 100644 similarity index 80% rename from tests/ecmas.html rename to tests/js.html index 2004387a1..81baef784 --- a/tests/ecmas.html +++ b/tests/js.html @@ -5,14 +5,14 @@ -Nimrod ECMAScript Generator Test +Nimrod JavaScript Generator Test - + -
+
diff --git a/tests/ecmas.nim b/tests/js.nim old mode 100755 new mode 100644 similarity index 92% rename from tests/ecmas.nim rename to tests/js.nim index 12ed71309..f31bb10d9 --- a/tests/ecmas.nim +++ b/tests/js.nim @@ -2,7 +2,7 @@ discard """ cmd: "nimrod js --hints:on $# $#" """ -# This file tests the ECMAScript generator +# This file tests the JavaScript generator import dom, strutils diff --git a/tests/js/test1.nim b/tests/js/test1.nim index 012b98833..3f3d5f02c 100644 --- a/tests/js/test1.nim +++ b/tests/js/test1.nim @@ -3,7 +3,7 @@ discard """ output: "1261129" """ -# This file tests the ECMAScript generator +# This file tests the JavaScript generator import dom, strutils diff --git a/tests/js/test2.nim b/tests/js/test2.nim new file mode 100644 index 000000000..1342ed15d --- /dev/null +++ b/tests/js/test2.nim @@ -0,0 +1,23 @@ +discard """ + cmd: "nimrod js --hints:on -r $# $#" + output: '''foo +js 3.14''' +""" + +# This file tests the JavaScript generator + +# #335 +proc foo() = + var bar = "foo" + proc baz() = + echo bar + baz() +foo() + +# #376 +when not defined(JS): + proc foo(val: float): string = "no js " & $val +else: + proc foo(val: float): string = "js " & $val + +echo foo(3.14) diff --git a/tests/js/tunittests.nim b/tests/js/tunittests.nim new file mode 100644 index 000000000..af38cd9b9 --- /dev/null +++ b/tests/js/tunittests.nim @@ -0,0 +1,5 @@ +import unittest + +suite "Bacon": + test ">:)": + check(true == true) diff --git a/tests/manyloc/keineschweine/README.md b/tests/manyloc/keineschweine/README.md new file mode 100644 index 000000000..2a6b78a8e --- /dev/null +++ b/tests/manyloc/keineschweine/README.md @@ -0,0 +1,26 @@ +keineSchweine +======================== +Just a dumb little game + +### Dependencies + +* Nimrod 0.8.15, Until this version is released I'm working off Nimrod HEAD: https://github.com/Araq/Nimrod +* SFML 2.0 (git), https://github.com/LaurentGomila/SFML +* CSFML 2.0 (git), https://github.com/LaurentGomila/CSFML +* Chipmunk 6.1.1 http://chipmunk-physics.net/downloads.php + +### How to build? + +* `git clone --recursive git://github.com/fowlmouth/keineSchweine.git somedir` +* `cd somedir` +* `nimrod c -r nakefile test` or `nimrod c -r keineschweine && ./keineschweine` + +### Download the game data + +You need to download the game data before you can play: +http://dl.dropbox.com/u/37533467/data-08-01-2012.7z + +Unpack it to the root directory. You can use the nakefile to do this easily: + +* `nimrod c -r nakefile` +* `./nakefile download` diff --git a/tests/manyloc/keineschweine/TODO.md b/tests/manyloc/keineschweine/TODO.md new file mode 100644 index 000000000..1145949aa --- /dev/null +++ b/tests/manyloc/keineschweine/TODO.md @@ -0,0 +1,21 @@ +##Main State +* Add GUI: + * Player list + * Chat box + * Escape menu + +##Lobby +* Add GUI: + * options menu + * key configuration + +## Animations +* Need one-shot explosion animations + * Thrusters (maybe a particle system instead) + +## Networking +* zone server should verify users through the dir server or handle its own users +* directory server should handle asset patching + +## Genpacket +* add support for branching types with case diff --git a/tests/manyloc/keineschweine/client_settings.json b/tests/manyloc/keineschweine/client_settings.json new file mode 100644 index 000000000..59facf1ad --- /dev/null +++ b/tests/manyloc/keineschweine/client_settings.json @@ -0,0 +1,10 @@ +{ + "resolution": [800,600,32], + "default-file": "alphazone.json", + "alias": "foo", + "directory-server":{ + "host":"localhost", + "port":8024 + }, + "website":"https://github.com/fowlmouth/keineSchweine" +} diff --git a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim new file mode 100644 index 000000000..8226b0b04 --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim @@ -0,0 +1,1516 @@ +# Copyright (c) 2007 Scott Lembcke +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +when defined(Linux): + const Lib = "libchipmunk.so.6.1.1" +else: + {.error: "Platform unsupported".} +when defined(MoreNimrod): + {.hint: "MoreNimrod defined; some Chipmunk functions replaced in Nimrod".} +{.deadCodeElim: on.} +from math import sqrt, sin, cos, arctan2 +when defined(CpUseFloat): + {.hint: "CpUseFloat defined; using float32 as float".} + type CpFloat* = cfloat +else: + type CpFloat* = cdouble +const + CP_BUFFER_BYTES* = (32 * 1024) + CP_MAX_CONTACTS_PER_ARBITER* = 4 + CpInfinity*: CpFloat = 1.0/0 +{.pragma: pf, pure, final.} +type + Bool32* = cint #replace one day with cint-compatible bool + CpDataPointer* = pointer + TVector* {.final, pure.} = object + x*, y*: CpFloat + TTimestamp* = cuint + TBodyVelocityFunc* = proc(body: PBody, gravity: TVector, + damping: CpFloat; dt: CpFloat){.cdecl.} + TBodyPositionFunc* = proc(body: PBody; dt: CpFloat){.cdecl.} + TComponentNode*{.pf.} = object + root*: PBody + next*: PBody + idleTime*: CpFloat + + THashValue = cuint # uintptr_t + TCollisionType* = cuint #uintptr_t + TGroup * = cuint #uintptr_t + TLayers* = cuint + PArray = ptr TArray + TArray{.pure,final.} = object + PHashSet = ptr THashSet + THashSet{.pf.} = object + PContact* = ptr TContact + TContact*{.pure,final.} = object + PArbiter* = ptr TArbiter + TArbiter*{.pf.} = object + e*: CpFloat + u*: CpFloat + surface_vr*: TVector + a*: PShape + b*: PShape + body_a*: PBody + body_b*: PBody + thread_a*: TArbiterThread + thread_b*: TArbiterThread + numContacts*: cint + contacts*: PContact + stamp*: TTimestamp + handler*: PCollisionHandler + swappedColl*: bool32 + state*: TArbiterState + PCollisionHandler* = ptr TCollisionHandler + TCollisionHandler*{.pf.} = object + a*: TCollisionType + b*: TCollisionType + begin*: TCollisionBeginFunc + preSolve*: TCollisionPreSolveFunc + postSolve*: TCollisionPostSolveFunc + separate*: TCollisionSeparateFunc + data*: pointer + TArbiterState*{.size: sizeof(cint).} = enum + ArbiterStateFirstColl, # Arbiter is active and its not the first collision. + ArbiterStateNormal, # Collision has been explicitly ignored. + # Either by returning false from a begin collision handler or calling cpArbiterIgnore(). + ArbiterStateIgnore, # Collison is no longer active. A space will cache an arbiter for up to cpSpace.collisionPersistence more steps. + ArbiterStateCached + TArbiterThread*{.pf.} = object + next*: PArbiter # Links to next and previous arbiters in the contact graph. + prev*: PArbiter + + TContactPoint*{.pf.} = object + point*: TVector #/ The position of the contact point. + normal*: TVector #/ The normal of the contact point. + dist*: CpFloat #/ The depth of the contact point. + #/ A struct that wraps up the important collision data for an arbiter. + PContactPointSet* = ptr TContactPointSet + TContactPointSet*{.pf.} = object + count*: cint #/ The number of contact points in the set. + points*: array[0..CP_MAX_CONTACTS_PER_ARBITER - 1, TContactPoint] #/ The array of contact points. + + #/ Collision begin event function callback type. + #/ Returning false from a begin callback causes the collision to be ignored until + #/ the the separate callback is called when the objects stop colliding. + TCollisionBeginFunc* = proc (arb: PArbiter; space: PSpace; data: pointer): Bool{. + cdecl.} + #/ Collision pre-solve event function callback type. + #/ Returning false from a pre-step callback causes the collision to be ignored until the next step. + TCollisionPreSolveFunc* = proc (arb: PArbiter; space: PSpace; + data: pointer): bool {.cdecl.} + #/ Collision post-solve event function callback type. + TCollisionPostSolveFunc* = proc (arb: PArbiter; space: PSpace; + data: pointer){.cdecl.} + #/ Collision separate event function callback type. + TCollisionSeparateFunc* = proc (arb: PArbiter; space: PSpace; + data: pointer){.cdecl.} + + #/ Chipmunk's axis-aligned 2D bounding box type. (left, bottom, right, top) + PBB* = ptr TBB + TBB* {.pf.} = object + l*, b*, r*, t*: CpFloat + + #/ Spatial index bounding box callback function type. + #/ The spatial index calls this function and passes you a pointer to an object you added + #/ when it needs to get the bounding box associated with that object. + TSpatialIndexBBFunc* = proc (obj: pointer): TBB{.cdecl.} + #/ Spatial index/object iterator callback function type. + TSpatialIndexIteratorFunc* = proc (obj: pointer; data: pointer){.cdecl.} + #/ Spatial query callback function type. + TSpatialIndexQueryFunc* = proc (obj1: pointer; obj2: pointer; data: pointer){. + cdecl.} + #/ Spatial segment query callback function type. + TSpatialIndexSegmentQueryFunc* = proc (obj1: pointer; obj2: pointer; + data: pointer): CpFloat {.cdecl.} + #/ private + PSpatialIndex = ptr TSpatialIndex + TSpatialIndex{.pf.} = object + klass: PSpatialIndexClass + bbfunc: TSpatialIndexBBFunc + staticIndex: PSpatialIndex + dynamicIndex: PSpatialIndex + + TSpatialIndexDestroyImpl* = proc (index: PSpatialIndex){.cdecl.} + TSpatialIndexCountImpl* = proc (index: PSpatialIndex): cint{.cdecl.} + TSpatialIndexEachImpl* = proc (index: PSpatialIndex; + func: TSpatialIndexIteratorFunc; data: pointer){. + cdecl.} + TSpatialIndexContainsImpl* = proc (index: PSpatialIndex; obj: pointer; + hashid: THashValue): Bool32 {.cdecl.} + TSpatialIndexInsertImpl* = proc (index: PSpatialIndex; obj: pointer; + hashid: THashValue){.cdecl.} + TSpatialIndexRemoveImpl* = proc (index: PSpatialIndex; obj: pointer; + hashid: THashValue){.cdecl.} + TSpatialIndexReindexImpl* = proc (index: PSpatialIndex){.cdecl.} + TSpatialIndexReindexObjectImpl* = proc (index: PSpatialIndex; + obj: pointer; hashid: THashValue){.cdecl.} + TSpatialIndexReindexQueryImpl* = proc (index: PSpatialIndex; + func: TSpatialIndexQueryFunc; data: pointer){.cdecl.} + TSpatialIndexPointQueryImpl* = proc (index: PSpatialIndex; point: TVector; + func: TSpatialIndexQueryFunc; + data: pointer){.cdecl.} + TSpatialIndexSegmentQueryImpl* = proc (index: PSpatialIndex; obj: pointer; + a: TVector; b: TVector; t_exit: CpFloat; func: TSpatialIndexSegmentQueryFunc; + data: pointer){.cdecl.} + TSpatialIndexQueryImpl* = proc (index: PSpatialIndex; obj: pointer; + bb: TBB; func: TSpatialIndexQueryFunc; + data: pointer){.cdecl.} + PSpatialIndexClass* = ptr TSpatialIndexClass + TSpatialIndexClass*{.pf.} = object + destroy*: TSpatialIndexDestroyImpl + count*: TSpatialIndexCountImpl + each*: TSpatialIndexEachImpl + contains*: TSpatialIndexContainsImpl + insert*: TSpatialIndexInsertImpl + remove*: TSpatialIndexRemoveImpl + reindex*: TSpatialIndexReindexImpl + reindexObject*: TSpatialIndexReindexObjectImpl + reindexQuery*: TSpatialIndexReindexQueryImpl + pointQuery*: TSpatialIndexPointQueryImpl + segmentQuery*: TSpatialIndexSegmentQueryImpl + query*: TSpatialIndexQueryImpl + + PSpaceHash* = ptr TSpaceHash + TSpaceHash* {.pf.} = object + PBBTree* = ptr TBBTree + TBBTree* {.pf.} = object + PSweep1D* = ptr TSweep1D + TSweep1D* {.pf.} = object + + #/ Bounding box tree velocity callback function. + #/ This function should return an estimate for the object's velocity. + TBBTreeVelocityFunc* = proc (obj: pointer): TVector {.cdecl.} + + PContactBufferHeader* = ptr TContentBufferHeader + TContentBufferHeader* {.pf.} = object + TSpaceArbiterApplyImpulseFunc* = proc (arb: PArbiter){.cdecl.} + + PSpace* = ptr TSpace + TSpace* {.pf.} = object + iterations*: cint + gravity*: TVector + damping*: CpFloat + idleSpeedThreshold*: CpFloat + sleepTimeThreshold*: CpFloat + collisionSlop*: CpFloat + collisionBias*: CpFloat + collisionPersistence*: TTimestamp + enableContactGraph*: cint ##BOOL + data*: pointer + staticBody*: PBody + stamp: TTimestamp + currDT: CpFloat + bodies: PArray + rousedBodies: PArray + sleepingComponents: PArray + staticShapes: PSpatialIndex + activeShapes: PSpatialIndex + arbiters: PArray + contactBuffersHead: PContactBufferHeader + cachedArbiters: PHashSet + pooledArbiters: PArray + constraints: PArray + allocatedBuffers: PArray + locked: cint + collisionHandlers: PHashSet + defaultHandler: TCollisionHandler + postStepCallbacks: PHashSet + arbiterApplyImpulse: TSpaceArbiterApplyImpulseFunc + staticBody2: TBody #_staticBody + PBody* = ptr TBody + TBody*{.pf.} = object + velocityFunc*: TBodyVelocityFunc + positionFunc*: TBodyPositionFunc + m*: CpFloat + mInv*: CpFloat + i*: CpFloat + iInv*: CpFloat + p*: TVector + v*: TVector + f*: TVector + a*: CpFloat + w*: CpFloat + t*: CpFloat + rot*: TVector + data*: pointer + vLimit*: CpFloat + wLimit*: CpFloat + vBias*: TVector + wBias*: CpFloat + space*: PSpace + shapeList*: PShape + arbiterList*: PArbiter + constraintList*: PConstraint + node*: TComponentNode + #/ Body/shape iterator callback function type. + TBodyShapeIteratorFunc* = proc (body: PBody; shape: PShape; + data: pointer) {.cdecl.} + #/ Body/constraint iterator callback function type. + TBodyConstraintIteratorFunc* = proc (body: PBody; + constraint: PConstraint; + data: pointer) {.cdecl.} + #/ Body/arbiter iterator callback function type. + TBodyArbiterIteratorFunc* = proc (body: PBody; arbiter: PArbiter; + data: pointer) {.cdecl.} + + PNearestPointQueryInfo* = ptr TNearestPointQueryInfo + #/ Nearest point query info struct. + TNearestPointQueryInfo*{.pf.} = object + shape: PShape #/ The nearest shape, NULL if no shape was within range. + p: TVector #/ The closest point on the shape's surface. (in world space coordinates) + d: CpFloat #/ The distance to the point. The distance is negative if the point is inside the shape. + + PSegmentQueryInfo* = ptr TSegmentQueryInfo + #/ Segment query info struct. + TSegmentQueryInfo*{.pf.} = object + shape*: PShape #/ The shape that was hit, NULL if no collision occured. + t*: CpFloat #/ The normalized distance along the query segment in the range [0, 1]. + n*: TVector #/ The normal of the surface hit. + TShapeType*{.size: sizeof(cint).} = enum + CP_CIRCLE_SHAPE, CP_SEGMENT_SHAPE, CP_POLY_SHAPE, CP_NUM_SHAPES + TShapeCacheDataImpl* = proc (shape: PShape; p: TVector; rot: TVector): TBB{.cdecl.} + TShapeDestroyImpl* = proc (shape: PShape){.cdecl.} + TShapePointQueryImpl* = proc (shape: PShape; p: TVector): bool32 {.cdecl.} + TShapeSegmentQueryImpl* = proc (shape: PShape; a: TVector; b: TVector; + info: PSegmentQueryInfo){.cdecl.} + PShapeClass* = ptr TShapeClass + TShapeClass*{.pf.} = object + kind*: TShapeType + cacheData*: TShapeCacheDataImpl + destroy*: TShapeDestroyImpl + pointQuery*: TShapePointQueryImpl + segmentQuery*: TShapeSegmentQueryImpl + PShape* = ptr TShape + TShape*{.pf.} = object + klass: PShapeClass #/ PRIVATE + body*: PBody #/ The rigid body this collision shape is attached to. + bb*: TBB #/ The current bounding box of the shape. + sensor*: Bool32 #/ Sensor flag. + #/ Sensor shapes call collision callbacks but don't produce collisions. + e*: CpFloat #/ Coefficient of restitution. (elasticity) + u*: CpFloat #/ Coefficient of friction. + surface_v*: TVector #/ Surface velocity used when solving for friction. + data*: pointer #/ User definable data pointer. Generally this points to your the game object class so you can access it when given a cpShape reference in a callback. + collision_type*: TCollisionType #/ Collision type of this shape used when picking collision handlers. + group*: TGroup #/ Group of this shape. Shapes in the same group don't collide. + layers*: TLayers #/ Layer bitmask for this shape. Shapes only collide if the bitwise and of their layers is non-zero. + space: PSpace #PRIVATE + next: PShape #PRIVATE + prev: PShape #PRIVATE + hashid: THashValue #PRIVATE + PCircleShape* = ptr TCircleShape + TCircleShape*{.pf.} = object + shape: PShape + c, tc: TVector + r: CpFloat + PPolyShape* = ptr TPolyShape + TPolyShape*{.pf.} = object + shape: PShape + numVerts: cint + verts, tVerts: TVector + planes, tPlanes: PSplittingPlane + PSegmentShape* = ptr TSegmentShape + TSegmentShape*{.pf.} = object + shape: PShape + a, b, n: TVector + ta, tb, tn: TVector + r: CpFloat + aTangent, bTangent: TVector + PSplittingPlane* = ptr TSplittingPlane + TSplittingPlane*{.pf.} = object + n: TVector + d: CpFloat + + #/ Post Step callback function type. + TPostStepFunc* = proc (space: PSpace; obj: pointer; data: pointer){.cdecl.} + #/ Point query callback function type. + TSpacePointQueryFunc* = proc (shape: PShape; data: pointer){.cdecl.} + #/ Segment query callback function type. + TSpaceSegmentQueryFunc* = proc (shape: PShape; t: CpFloat; n: TVector; + data: pointer){.cdecl.} + #/ Rectangle Query callback function type. + TSpaceBBQueryFunc* = proc (shape: PShape; data: pointer){.cdecl.} + #/ Shape query callback function type. + TSpaceShapeQueryFunc* = proc (shape: PShape; points: PContactPointSet; + data: pointer){.cdecl.} + #/ Space/body iterator callback function type. + TSpaceBodyIteratorFunc* = proc (body: PBody; data: pointer){.cdecl.} + #/ Space/body iterator callback function type. + TSpaceShapeIteratorFunc* = proc (shape: PShape; data: pointer){.cdecl.} + #/ Space/constraint iterator callback function type. + TSpaceConstraintIteratorFunc* = proc (constraint: PConstraint; + data: pointer){.cdecl.} + #/ Opaque cpConstraint struct. + PConstraint* = ptr TConstraint + TConstraint*{.pf.} = object + klass: PConstraintClass #/PRIVATE + a*: PBody #/ The first body connected to this constraint. + b*: PBody #/ The second body connected to this constraint. + space: PSpace #/PRIVATE + next_a: PConstraint #/PRIVATE + next_b: PConstraint #/PRIVATE + maxForce*: CpFloat #/ The maximum force that this constraint is allowed to use. Defaults to infinity. + errorBias*: CpFloat #/ The rate at which joint error is corrected. Defaults to pow(1.0 - 0.1, 60.0) meaning that it will correct 10% of the error every 1/60th of a second. + maxBias*: CpFloat #/ The maximum rate at which joint error is corrected. Defaults to infinity. + preSolve*: TConstraintPreSolveFunc #/ Function called before the solver runs. Animate your joint anchors, update your motor torque, etc. + postSolve*: TConstraintPostSolveFunc #/ Function called after the solver runs. Use the applied impulse to perform effects like breakable joints. + data*: CpDataPointer # User definable data pointer. Generally this points to your the game object class so you can access it when given a cpConstraint reference in a callback. + TConstraintPreStepImpl = proc (constraint: PConstraint; dt: CpFloat){.cdecl.} + TConstraintApplyCachedImpulseImpl = proc (constraint: PConstraint; dt_coef: CpFloat){.cdecl.} + TConstraintApplyImpulseImpl = proc (constraint: PConstraint){.cdecl.} + TConstraintGetImpulseImpl = proc (constraint: PConstraint): CpFloat{.cdecl.} + PConstraintClass = ptr TConstraintClass + TConstraintClass{.pf.} = object + preStep*: TConstraintPreStepImpl + applyCachedImpulse*: TConstraintApplyCachedImpulseImpl + applyImpulse*: TConstraintApplyImpulseImpl + getImpulse*: TConstraintGetImpulseImpl + #/ Callback function type that gets called before solving a joint. + TConstraintPreSolveFunc* = proc (constraint: PConstraint; space: PSpace){. + cdecl.} + #/ Callback function type that gets called after solving a joint. + TConstraintPostSolveFunc* = proc (constraint: PConstraint; space: PSpace){. + cdecl.} + +##cp property emulators +template defGetter(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} = + proc `get procName`*(obj: otype): memberType {.cdecl.} = + return obj.memberName +template defSetter(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} = + proc `set procName`*(obj: otype, value: memberType) {.cdecl.} = + obj.memberName = value +template defProp(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} = + defGetter(otype, memberType, memberName, procName) + defSetter(otype, memberType, memberName, procName) + + +##cpspace.h +proc allocSpace*(): PSpace {. + importc: "cpSpaceAlloc", dynlib: Lib.} +proc Init*(space: PSpace): PSpace {. + importc: "cpSpaceInit", dynlib: Lib.} +proc newSpace*(): PSpace {. + importc: "cpSpaceNew", dynlib: Lib.} +proc destroy*(space: PSpace) {. + importc: "cpSpaceDestroy", dynlib: Lib.} +proc free*(space: PSpace) {. + importc: "cpSpaceFree", dynlib: Lib.} + +defProp(PSpace, cint, iterations, Iterations) +defProp(PSpace, TVector, gravity, Gravity) +defProp(PSpace, CpFloat, damping, Damping) +defProp(PSpace, CpFloat, idleSpeedThreshold, IdleSpeedThreshold) +defProp(PSpace, CpFloat, sleepTimeThreshold, SleepTimeThreshold) +defProp(PSpace, CpFloat, collisionSlop, CollisionSlop) +defProp(PSpace, CpFloat, collisionBias, CollisionBias) +defProp(PSpace, TTimestamp, collisionPersistence, CollisionPersistence) +defProp(PSpace, Bool32, enableContactGraph, EnableContactGraph) +defProp(PSpace, pointer, data, UserData) +defGetter(PSpace, PBody, staticBody, StaticBody) +defGetter(PSpace, CpFloat, currDt, CurrentTimeStep) + + +#/ returns true from inside a callback and objects cannot be added/removed. +proc isLocked*(space: PSpace): Bool{.inline.} = + result = space.locked.bool + +#/ Set a default collision handler for this space. +#/ The default collision handler is invoked for each colliding pair of shapes +#/ that isn't explicitly handled by a specific collision handler. +#/ You can pass NULL for any function you don't want to implement. +proc setDefaultCollisionHandler*(space: PSpace; begin: TCollisionBeginFunc; + preSolve: TCollisionPreSolveFunc; + postSolve: TCollisionPostSolveFunc; + separate: TCollisionSeparateFunc; + data: pointer){. + cdecl, importc: "cpSpaceSetDefaultCollisionHandler", dynlib: Lib.} +#/ Set a collision handler to be used whenever the two shapes with the given collision types collide. +#/ You can pass NULL for any function you don't want to implement. +proc addCollisionHandler*(space: PSpace; a, b: TCollisionType; + begin: TCollisionBeginFunc; + preSolve: TCollisionPreSolveFunc; + postSolve: TCollisionPostSolveFunc; + separate: TCollisionSeparateFunc; data: pointer){. + cdecl, importc: "cpSpaceAddCollisionHandler", dynlib: Lib.} +#/ Unset a collision handler. +proc removeCollisionHandler*(space: PSpace; a: TCollisionType; + b: TCollisionType){. + cdecl, importc: "cpSpaceRemoveCollisionHandler", dynlib: Lib.} +#/ Add a collision shape to the simulation. +#/ If the shape is attached to a static body, it will be added as a static shape. +proc addShape*(space: PSpace; shape: PShape): PShape{. + cdecl, importc: "cpSpaceAddShape", dynlib: Lib.} +#/ Explicity add a shape as a static shape to the simulation. +proc addStaticShape*(space: PSpace; shape: PShape): PShape{. + cdecl, importc: "cpSpaceAddStaticShape", dynlib: Lib.} +#/ Add a rigid body to the simulation. +proc addBody*(space: PSpace; body: PBody): PBody{. + cdecl, importc: "cpSpaceAddBody", dynlib: Lib.} +#/ Add a constraint to the simulation. +proc addConstraint*(space: PSpace; constraint: PConstraint): PConstraint{. + cdecl, importc: "cpSpaceAddConstraint", dynlib: Lib.} +#/ Remove a collision shape from the simulation. +proc removeShape*(space: PSpace; shape: PShape){. + cdecl, importc: "cpSpaceRemoveShape", dynlib: Lib.} +#/ Remove a collision shape added using cpSpaceAddStaticShape() from the simulation. +proc removeStaticShape*(space: PSpace; shape: PShape){. + cdecl, importc: "cpSpaceRemoveStaticShape", dynlib: Lib.} +#/ Remove a rigid body from the simulation. +proc removeBody*(space: PSpace; body: PBody){. + cdecl, importc: "cpSpaceRemoveBody", dynlib: Lib.} +#/ Remove a constraint from the simulation. +proc RemoveConstraint*(space: PSpace; constraint: PConstraint){. + cdecl, importc: "cpSpaceRemoveConstraint", dynlib: Lib.} +#/ Test if a collision shape has been added to the space. +proc containsShape*(space: PSpace; shape: PShape): Bool{. + cdecl, importc: "cpSpaceContainsShape", dynlib: Lib.} +#/ Test if a rigid body has been added to the space. +proc containsBody*(space: PSpace; body: PBody): Bool{. + cdecl, importc: "cpSpaceContainsBody", dynlib: Lib.} +#/ Test if a constraint has been added to the space. + +proc containsConstraint*(space: PSpace; constraint: PConstraint): Bool{. + cdecl, importc: "cpSpaceContainsConstraint", dynlib: Lib.} +#/ Schedule a post-step callback to be called when cpSpaceStep() finishes. +#/ @c obj is used a key, you can only register one callback per unique value for @c obj +proc addPostStepCallback*(space: PSpace; func: TPostStepFunc; + obj: pointer; data: pointer){. + cdecl, importc: "cpSpaceAddPostStepCallback", dynlib: Lib.} + +#/ Query the space at a point and call @c func for each shape found. +proc pointQuery*(space: PSpace; point: TVector; layers: TLayers; + group: TGroup; func: TSpacePointQueryFunc; data: pointer){. + cdecl, importc: "cpSpacePointQuery", dynlib: Lib.} + +#/ Query the space at a point and return the first shape found. Returns NULL if no shapes were found. +proc pointQueryFirst*(space: PSpace; point: TVector; layers: TLayers; + group: TGroup): PShape{. + cdecl, importc: "cpSpacePointQueryFirst", dynlib: Lib.} + +#/ Perform a directed line segment query (like a raycast) against the space calling @c func for each shape intersected. +proc segmentQuery*(space: PSpace; start: TVector; to: TVector; + layers: TLayers; group: TGroup; + func: TSpaceSegmentQueryFunc; data: pointer){. + cdecl, importc: "cpSpaceSegmentQuery", dynlib: Lib.} +#/ Perform a directed line segment query (like a raycast) against the space and return the first shape hit. Returns NULL if no shapes were hit. +proc segmentQueryFirst*(space: PSpace; start: TVector; to: TVector; + layers: TLayers; group: TGroup; + res: PSegmentQueryInfo): PShape{. + cdecl, importc: "cpSpaceSegmentQueryFirst", dynlib: Lib.} + +#/ Perform a fast rectangle query on the space calling @c func for each shape found. +#/ Only the shape's bounding boxes are checked for overlap, not their full shape. +proc BBQuery*(space: PSpace; bb: TBB; layers: TLayers; group: TGroup; + func: TSpaceBBQueryFunc; data: pointer){. + cdecl, importc: "cpSpaceBBQuery", dynlib: Lib.} + +#/ Query a space for any shapes overlapping the given shape and call @c func for each shape found. +proc shapeQuery*(space: PSpace; shape: PShape; func: TSpaceShapeQueryFunc; data: pointer): Bool {. + cdecl, importc: "cpSpaceShapeQuery", dynlib: Lib.} +#/ Call cpBodyActivate() for any shape that is overlaps the given shape. +proc activateShapesTouchingShape*(space: PSpace; shape: PShape){. + cdecl, importc: "cpSpaceActivateShapesTouchingShape", dynlib: Lib.} + +#/ Call @c func for each body in the space. +proc eachBody*(space: PSpace; func: TSpaceBodyIteratorFunc; data: pointer){. + cdecl, importc: "cpSpaceEachBody", dynlib: Lib.} + +#/ Call @c func for each shape in the space. +proc eachShape*(space: PSpace; func: TSpaceShapeIteratorFunc; + data: pointer){. + cdecl, importc: "cpSpaceEachShape", dynlib: Lib.} +#/ Call @c func for each shape in the space. +proc eachConstraint*(space: PSpace; func: TSpaceConstraintIteratorFunc; + data: pointer){. + cdecl, importc: "cpSpaceEachConstraint", dynlib: Lib.} +#/ Update the collision detection info for the static shapes in the space. +proc reindexStatic*(space: PSpace){. + cdecl, importc: "cpSpaceReindexStatic", dynlib: Lib.} +#/ Update the collision detection data for a specific shape in the space. +proc reindexShape*(space: PSpace; shape: PShape){. + cdecl, importc: "cpSpaceReindexShape", dynlib: Lib.} +#/ Update the collision detection data for all shapes attached to a body. +proc reindexShapesForBody*(space: PSpace; body: PBody){. + cdecl, importc: "cpSpaceReindexShapesForBody", dynlib: Lib.} +#/ Switch the space to use a spatial has as it's spatial index. +proc SpaceUseSpatialHash*(space: PSpace; dim: CpFloat; count: cint){. + cdecl, importc: "cpSpaceUseSpatialHash", dynlib: Lib.} +#/ Step the space forward in time by @c dt. +proc step*(space: PSpace; dt: CpFloat) {. + cdecl, importc: "cpSpaceStep", dynlib: Lib.} + + +#/ Convenience constructor for cpVect structs. +proc vector*(x, y: CpFloat): TVector {.inline.} = + result.x = x + result.y = y +proc newVector*(x, y: CpFloat): TVector {.inline.} = + return vector(x, y) +#let VectorZero* = newVector(0.0, 0.0) +var VectorZero* = newVector(0.0, 0.0) + +#/ Vector dot product. +proc dot*(v1, v2: TVector): CpFloat {.inline.} = + result = v1.x * v2.x + v1.y * v2.y + +#/ Returns the length of v. +#proc len*(v: TVector): CpFloat {. +# cdecl, importc: "cpvlength", dynlib: Lib.} +proc len*(v: TVector): CpFloat {.inline.} = + result = v.dot(v).sqrt +#/ Spherical linearly interpolate between v1 and v2. +proc slerp*(v1, v2: TVector; t: CpFloat): TVector {. + cdecl, importc: "cpvslerp", dynlib: Lib.} +#/ Spherical linearly interpolate between v1 towards v2 by no more than angle a radians +proc slerpconst*(v1, v2: TVector; a: CpFloat): TVector {. + cdecl, importc: "cpvslerpconst", dynlib: Lib.} +#/ Returns the unit length vector for the given angle (in radians). +#proc vectorForAngle*(a: CpFloat): TVector {. +# cdecl, importc: "cpvforangle", dynlib: Lib.} +proc vectorForAngle*(a: CpFloat): TVector {.inline.} = + result = newVector(math.cos(a), math.sin(a)) +#/ Returns the angular direction v is pointing in (in radians). +proc toAngle*(v: TVector): CpFloat {.inline.} = + result = math.arctan2(v.y, v.x) +#/ Returns a string representation of v. Intended mostly for debugging purposes and not production use. +#/ @attention The string points to a static local and is reset every time the function is called. +#/ If you want to print more than one vector you will have to split up your printing onto separate lines. +proc `$`*(v: TVector): cstring {.cdecl, importc: "cpvstr", dynlib: Lib.} + + +#/ Check if two vectors are equal. (Be careful when comparing floating point numbers!) +proc `==`*(v1, v2: TVector): bool {.inline.} = + result = v1.x == v2.x and v1.y == v2.y + +#/ Add two vectors +proc `+`*(v1, v2: TVector): TVector {.inline.} = + result = newVector(v1.x + v2.x, v1.y + v2.y) +proc `+=`*(v1: var TVector; v2: TVector) = + v1.x = v1.x + v2.x + v1.y = v1.y + v2.y + +#/ Subtract two vectors. +proc `-`*(v1, v2: TVector): TVector {.inline.} = + result = newVector(v1.x - v2.x, v1.y - v2.y) +proc `-=`*(v1: var TVector; v2: TVector) = + v1.x = v1.x - v2.x + v1.y = v1.y - v2.y + +#/ Negate a vector. +proc `-`*(v: TVector): TVector {.inline.} = + result = newVector(- v.x, - v.y) + +#/ Scalar multiplication. +proc `*`*(v: TVector, s: CpFloat): TVector {.inline.} = + result.x = v.x * s + result.y = v.y * s +proc `*=`*(v: var TVector; s: CpFloat) = + v.x = v.x * s + v.y = v.y * s + +#/ 2D vector cross product analog. +#/ The cross product of 2D vectors results in a 3D vector with only a z component. +#/ This function returns the magnitude of the z value. +proc cross*(v1, v2: TVector): CpFloat {.inline.} = + result = v1.x * v2.y - v1.y * v2.x + +#/ Returns a perpendicular vector. (90 degree rotation) +proc perp*(v: TVector): TVector {.inline.} = + result = newVector(- v.y, v.x) + +#/ Returns a perpendicular vector. (-90 degree rotation) +proc rperp*(v: TVector): TVector {.inline.} = + result = newVector(v.y, - v.x) + +#/ Returns the vector projection of v1 onto v2. +proc project*(v1,v2: TVector): TVector {.inline.} = + result = v2 * (v1.dot(v2) / v2.dot(v2)) + +#/ Uses complex number multiplication to rotate v1 by v2. Scaling will occur if v1 is not a unit vector. + +proc rotate*(v1, v2: TVector): TVector {.inline.} = + result = newVector(v1.x * v2.x - v1.y * v2.y, v1.x * v2.y + v1.y * v2.x) +#/ Inverse of cpvrotate(). +proc unrotate*(v1, v2: TVector): TVector {.inline.} = + result = newVector(v1.x * v2.x + v1.y * v2.y, v1.y * v2.x - v1.x * v2.y) +#/ Returns the squared length of v. Faster than cpvlength() when you only need to compare lengths. +proc lenSq*(v: TVector): CpFloat {.inline.} = + result = v.dot(v) +#/ Linearly interpolate between v1 and v2. +proc lerp*(v1, v2: TVector; t: CpFloat): TVector {.inline.} = + result = (v1 * (1.0 - t)) + (v2 * t) +#/ Returns a normalized copy of v. +proc normalize*(v: TVector): TVector {.inline.} = + result = v * (1.0 / v.len) +#/ Returns a normalized copy of v or cpvzero if v was already cpvzero. Protects against divide by zero errors. +proc normalizeSafe*(v: TVector): TVector {.inline.} = + result = if v.x == 0.0 and v.y == 0.0: VectorZero else: v.normalize +#/ Clamp v to length len. +proc clamp*(v: TVector; len: CpFloat): TVector {.inline.} = + result = if v.dot(v) > len * len: v.normalize * len else: v +#/ Linearly interpolate between v1 towards v2 by distance d. +proc lerpconst*(v1, v2: TVector; d: CpFloat): TVector {.inline.} = + result = v1 + clamp(v2 - v1, d) #vadd(v1 + vclamp(vsub(v2, v1), d)) +#/ Returns the distance between v1 and v2. +proc dist*(v1, v2: TVector): CpFloat {.inline.} = + result = (v1 - v2).len #vlength(vsub(v1, v2)) +#/ Returns the squared distance between v1 and v2. Faster than cpvdist() when you only need to compare distances. +proc distsq*(v1, v2: TVector): CpFloat {.inline.} = + result = (v1 - v2).lenSq #vlengthsq(vsub(v1, v2)) +#/ Returns true if the distance between v1 and v2 is less than dist. +proc near*(v1, v2: TVector; dist: CpFloat): Bool{.inline.} = + result = v1.distSq(v2) < dist * dist + + + +##cpBody.h +proc allocBody*(): PBody {.importc: "cpBodyAlloc", dynlib: Lib.} +proc init*(body: PBody; m: CpFloat; i: CpFloat): PBody {. + importc: "cpBodyInit", dynlib: Lib.} +proc newBody*(m: CpFloat; i: CpFloat): PBody {. + importc: "cpBodyNew", dynlib: Lib.} + +proc initStaticBody*(body: PBody): PBody{. + importc: "cpBodyInitStatic", dynlib: Lib.} +#/ Allocate and initialize a static cpBody. +proc newStatic*(): PBody{.importc: "cpBodyNewStatic", dynlib: Lib.} +#/ Destroy a cpBody. +proc destroy*(body: PBody){.importc: "cpBodyDestroy", dynlib: Lib.} +#/ Destroy and free a cpBody. +proc free*(body: PBody){.importc: "cpBodyFree", dynlib: Lib.} + +#/ Wake up a sleeping or idle body. +proc activate*(body: PBody){.importc: "cpBodyActivate", dynlib: Lib.} +#/ Wake up any sleeping or idle bodies touching a static body. +proc activateStatic*(body: PBody; filter: PShape){. + importc: "cpBodyActivateStatic", dynlib: Lib.} +#/ Force a body to fall asleep immediately. +proc Sleep*(body: PBody){.importc: "cpBodySleep", dynlib: Lib.} +#/ Force a body to fall asleep immediately along with other bodies in a group. +proc SleepWithGroup*(body: PBody; group: PBody){. + importc: "cpBodySleepWithGroup", dynlib: Lib.} +#/ Returns true if the body is sleeping. +proc isSleeping*(body: PBody): Bool {.inline.} = + return body.node.root != nil +#/ Returns true if the body is static. +proc isStatic*(body: PBody): bool {.inline.} = + return body.node.idleTime == CpInfinity +#/ Returns true if the body has not been added to a space. +proc isRogue*(body: PBody): Bool {.inline.} = + return body.space == nil + +# #define CP_DefineBodyStructGetter(type, member, name) \ +# static inline type cpBodyGet##name(const cpBody *body){return body->member;} +# #define CP_DefineBodyStructSetter(type, member, name) \ +# static inline void cpBodySet##name(cpBody *body, const type value){ \ +# cpBodyActivate(body); \ +# cpBodyAssertSane(body); \ +# body->member = value; \ +# } +# #define CP_DefineBodyStructProperty(type, member, name) \ +# CP_DefineBodyStructGetter(type, member, name) \ +# CP_DefineBodyStructSetter(type, member, name) + +defGetter(PBody, CpFloat, m, Mass) +#/ Set the mass of a body. +when defined(MoreNimrod): + defSetter(PBody, CpFloat, m, Mass) +else: + proc setMass*(body: PBody; m: CpFloat){. + cdecl, importc: "cpBodySetMass", dynlib: Lib.} + +#/ Get the moment of a body. +defGetter(PBody, CpFloat, i, Moment) +#/ Set the moment of a body. +when defined(MoreNimrod): + defSetter(PBody, CpFloat, i, Moment) +else: + proc SetMoment*(body: PBody; i: CpFloat) {. + cdecl, importc: "cpBodySetMoment", dynlib: Lib.} + +#/ Get the position of a body. +defGetter(PBody, TVector, p, Pos) +#/ Set the position of a body. +when defined(MoreNimrod): + defSetter(PBody, TVector, p, Pos) +else: + proc setPos*(body: PBody; pos: TVector) {. + cdecl, importc: "cpBodySetPos", dynlib: Lib.} + +defProp(PBody, TVector, v, Vel) +defProp(PBody, TVector, f, Force) + +#/ Get the angle of a body. +defGetter(PBody, CpFloat, a, Angle) +#/ Set the angle of a body. +proc setAngle*(body: PBody; a: CpFloat){. + cdecl, importc: "cpBodySetAngle", dynlib: Lib.} + +defProp(PBody, CpFloat, w, AngVel) +defProp(PBody, CpFloat, t, Torque) +defGetter(PBody, TVector, rot, Rot) +defProp(PBody, CpFloat, v_limit, VelLimit) +defProp(PBody, CpFloat, w_limit, AngVelLimit) +defProp(PBody, pointer, data, UserData) + +#/ Default Integration functions. +proc UpdateVelocity*(body: PBody; gravity: TVector; damping: CpFloat; dt: CpFloat){. + cdecl, importc: "cpBodyUpdateVelocity", dynlib: Lib.} +proc UpdatePosition*(body: PBody; dt: CpFloat){. + cdecl, importc: "cpBodyUpdatePosition", dynlib: Lib.} +#/ Convert body relative/local coordinates to absolute/world coordinates. +proc Local2World*(body: PBody; v: TVector): TVector{.inline.} = + result = body.p + v.rotate(body.rot) ##return cpvadd(body.p, cpvrotate(v, body.rot)) +#/ Convert body absolute/world coordinates to relative/local coordinates. +proc world2Local*(body: PBody; v: TVector): TVector{.inline.} = + result = (v - body.p).unrotate(body.rot) +#/ Set the forces and torque or a body to zero. +proc resetForces*(body: PBody){. + cdecl, importc: "cpBodyResetForces", dynlib: Lib.} +#/ Apply an force (in world coordinates) to the body at a point relative to the center of gravity (also in world coordinates). +proc applyForce*(body: PBody; f, r: TVector){. + cdecl, importc: "cpBodyApplyForce", dynlib: Lib.} +#/ Apply an impulse (in world coordinates) to the body at a point relative to the center of gravity (also in world coordinates). +proc applyImpulse*(body: PBody; j, r: TVector){. + cdecl, importc: "cpBodyApplyImpulse", dynlib: Lib.} +#/ Get the velocity on a body (in world units) at a point on the body in world coordinates. + +proc getVelAtWorldPoint*(body: PBody; point: TVector): TVector{. + cdecl, importc: "cpBodyGetVelAtWorldPoint", dynlib: Lib.} +#/ Get the velocity on a body (in world units) at a point on the body in local coordinates. +proc getVelAtLocalPoint*(body: PBody; point: TVector): TVector{. + cdecl, importc: "cpBodyGetVelAtLocalPoint", dynlib: Lib.} +#/ Get the kinetic energy of a body. +# static inline CpFloat cpBodyKineticEnergy(const cpBody *body) +# { +# // Need to do some fudging to avoid NaNs +# cpFloat vsq = cpvdot(body->v, body->v); +# cpFloat wsq = body->w*body->w; +# return (vsq ? vsq*body->m : 0.0f) + (wsq ? wsq*body->i : 0.0f); +# } +proc kineticEnergy*(body: PBOdy): CpFloat = + result = (body.v.dot(body.v) * body.m) + (body.w * body.w * body.i) + +#/ Call @c func once for each shape attached to @c body and added to the space. +proc eachShape*(body: PBody; func: TBodyShapeIteratorFunc; + data: pointer){. + cdecl, importc: "cpBodyEachShape", dynlib: Lib.} +#/ Call @c func once for each constraint attached to @c body and added to the space. +proc eachConstraint*(body: PBody; func: TBodyConstraintIteratorFunc; + data: pointer) {. + cdecl, importc: "cpBodyEachConstraint", dynlib: Lib.} +#/ Call @c func once for each arbiter that is currently active on the body. +proc eachArbiter*(body: PBody; func: TBodyArbiterIteratorFunc; + data: pointer){. + cdecl, importc: "cpBodyEachArbiter", dynlib: Lib.} +#/ Allocate a spatial hash. +proc SpaceHashAlloc*(): PSpaceHash{. + cdecl, importc: "cpSpaceHashAlloc", dynlib: Lib.} +#/ Initialize a spatial hash. +proc SpaceHashInit*(hash: PSpaceHash; celldim: CpFloat; numcells: cint; + bbfunc: TSpatialIndexBBFunc; staticIndex: PSpatialIndex): PSpatialIndex{. + cdecl, importc: "cpSpaceHashInit", dynlib: Lib.} +#/ Allocate and initialize a spatial hash. +proc SpaceHashNew*(celldim: CpFloat; cells: cint; bbfunc: TSpatialIndexBBFunc; + staticIndex: PSpatialIndex): PSpatialIndex{. + cdecl, importc: "cpSpaceHashNew", dynlib: Lib.} +#/ Change the cell dimensions and table size of the spatial hash to tune it. +#/ The cell dimensions should roughly match the average size of your objects +#/ and the table size should be ~10 larger than the number of objects inserted. +#/ Some trial and error is required to find the optimum numbers for efficiency. +proc SpaceHashResize*(hash: PSpaceHash; celldim: CpFloat; numcells: cint){. + cdecl, importc: "cpSpaceHashResize", dynlib: Lib.} +#MARK: AABB Tree + + +#/ Allocate a bounding box tree. +proc BBTreeAlloc*(): PBBTree{.cdecl, importc: "cpBBTreeAlloc", dynlib: Lib.} +#/ Initialize a bounding box tree. +proc BBTreeInit*(tree: PBBTree; bbfunc: TSpatialIndexBBFunc; + staticIndex: ptr TSpatialIndex): ptr TSpatialIndex{.cdecl, + importc: "cpBBTreeInit", dynlib: Lib.} +#/ Allocate and initialize a bounding box tree. +proc BBTreeNew*(bbfunc: TSpatialIndexBBFunc; staticIndex: PSpatialIndex): PSpatialIndex{. + cdecl, importc: "cpBBTreeNew", dynlib: Lib.} +#/ Perform a static top down optimization of the tree. +proc BBTreeOptimize*(index: PSpatialIndex){. + cdecl, importc: "cpBBTreeOptimize", dynlib: Lib.} +#/ Set the velocity function for the bounding box tree to enable temporal coherence. + +proc BBTreeSetVelocityFunc*(index: PSpatialIndex; func: TBBTreeVelocityFunc){. + cdecl, importc: "cpBBTreeSetVelocityFunc", dynlib: Lib.} +#MARK: Single Axis Sweep + + +#/ Allocate a 1D sort and sweep broadphase. + +proc Sweep1DAlloc*(): ptr TSweep1D{.cdecl, importc: "cpSweep1DAlloc", + dynlib: Lib.} +#/ Initialize a 1D sort and sweep broadphase. + +proc Sweep1DInit*(sweep: ptr TSweep1D; bbfunc: TSpatialIndexBBFunc; + staticIndex: ptr TSpatialIndex): ptr TSpatialIndex{.cdecl, + importc: "cpSweep1DInit", dynlib: Lib.} +#/ Allocate and initialize a 1D sort and sweep broadphase. + +proc Sweep1DNew*(bbfunc: TSpatialIndexBBFunc; staticIndex: ptr TSpatialIndex): ptr TSpatialIndex{. + cdecl, importc: "cpSweep1DNew", dynlib: Lib.} + + + +defProp(PArbiter, CpFloat, e, Elasticity) +defProp(PArbiter, CpFloat, u, Friction) +defProp(PArbiter, TVector, surface_vr, SurfaceVelocity) + +#/ Calculate the total impulse that was applied by this +#/ This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback. +proc totalImpulse*(obj: PArbiter): TVector {.cdecl, importc: "cpArbiterTotalImpulse", dynlib: Lib.} + +#/ Calculate the total impulse including the friction that was applied by this arbiter. +#/ This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback. +proc totalImpulseWithFriction*(obj: PArbiter): TVector {.cdecl, importc: "cpArbiterTotalImpulseWithFriction", dynlib: Lib.} + +#/ Calculate the amount of energy lost in a collision including static, but not dynamic friction. +#/ This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback. +proc totalKE*(obj: PArbiter): CpFloat {.cdecl, importc: "cpArbiterTotalKE", dynlib: Lib.} + + +#/ Causes a collision pair to be ignored as if you returned false from a begin callback. +#/ If called from a pre-step callback, you will still need to return false +#/ if you want it to be ignored in the current step. +proc ignore*(arb: PArbiter) {.cdecl, importc: "cpArbiterIgnore", dynlib: Lib.} + +#/ Return the colliding shapes involved for this arbiter. +#/ The order of their cpSpace.collision_type values will match +#/ the order set when the collision handler was registered. +proc getShapes*(arb: PArbiter, a, b: var PShape) {.inline.} = + if arb.swappedColl.bool: + a = arb.b + b = arb.a + else: + a = arb.a + b = arb.b + +#/ A macro shortcut for defining and retrieving the shapes from an arbiter. +#define CP_ARBITER_GET_SHAPES(arb, a, b) cpShape *a, *b; cpArbiterGetShapes(arb, &a, &b); +template getShapes*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} = + var name1, name2: PShape + getShapes(arb, name1, name2) + + +#/ Return the colliding bodies involved for this arbiter. +#/ The order of the cpSpace.collision_type the bodies are associated with values will match +#/ the order set when the collision handler was registered. +#proc getBodies*(arb: PArbiter, a, b: var PBody) {.inline.} = +# getShapes(arb, shape1, shape2) +# a = shape1.body +# b = shape2.body + +#/ A macro shortcut for defining and retrieving the bodies from an arbiter. +#define CP_ARBITER_GET_BODIES(arb, a, b) cpBody *a, *b; cpArbiterGetBodies(arb, &a, &b); +template getBodies*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} = + var name1, name2: PBOdy + getBodies(arb, name1, name2) + +proc isFirstContact*(arb: PArbiter): bool {.inline.} = + result = arb.state == ArbiterStateFirstColl + +proc getCount*(arb: PArbiter): cint {.inline.} = + result = arb.numContacts + +#/ Return a contact set from an arbiter. +proc getContactPointSet*(arb: PArbiter): TContactPointSet {. + cdecl, importc: "cpArbiterGetContactPointSet", dynlib: Lib.} +#/ Get the normal of the @c ith contact point. +proc getNormal*(arb: PArbiter; i: cint): TVector {. + cdecl, importc: "cpArbiterGetNormal", dynlib: Lib.} +#/ Get the position of the @c ith contact point. +proc getPoint*(arb: PArbiter; i: cint): TVector {. + cdecl, importc: "cpArbiterGetPoint", dynlib: Lib.} +#/ Get the depth of the @c ith contact point. +proc getDepth*(arb: PArbiter; i: cint): CpFloat {. + cdecl, importc: "cpArbiterGetDepth", dynlib: Lib.} + +##Shapes +template defShapeSetter(memberType: typedesc, memberName: expr, procName: expr, activates: bool): stmt {.immediate.} = + proc `set procName`*(obj: PShape, value: memberType) {.cdecl.} = + if activates and obj.body != nil: obj.body.activate() + obj.memberName = value +template defShapeProp(memberType: typedesc, memberName: expr, procName: expr, activates: bool): stmt {.immediate.} = + defGetter(PShape, memberType, memberName, procName) + defShapeSetter(memberType, memberName, procName, activates) + +#/ Destroy a shape. +proc destroy*(shape: PShape) {. + cdecl, importc: "cpShapeDestroy", dynlib: Lib.} +#/ Destroy and Free a shape. +proc free*(shape: PShape){. + cdecl, importc: "cpShapeFree", dynlib: Lib.} +#/ Update, cache and return the bounding box of a shape based on the body it's attached to. +proc cacheBB*(shape: PShape): TBB{. + cdecl, importc: "cpShapeCacheBB", dynlib: Lib.} +#/ Update, cache and return the bounding box of a shape with an explicit transformation. +proc update*(shape: PShape; pos: TVector; rot: TVector): TBB {. + cdecl, importc: "cpShapeUpdate", dynlib: Lib.} +#/ Test if a point lies within a shape. +proc pointQuery*(shape: PShape; p: TVector): Bool32 {. + cdecl, importc: "cpShapePointQuery", dynlib: Lib.} + +#/ Perform a nearest point query. It finds the closest point on the surface of shape to a specific point. +#/ The value returned is the distance between the points. A negative distance means the point is inside the shape. +proc nearestPointQuery*(shape: PShape; p: TVector; res: PNearestPointQueryInfo): CpFloat {. + cdecl, importc: "cpShapeNearestPointQuery", dynlib: Lib.} +#/ Perform a segment query against a shape. @c info must be a pointer to a valid cpSegmentQueryInfo structure. +proc segmentQuery*(shape: PShape, a, b: TVector, info: PSegmentQueryInfo): bool {. + cdecl, importc: "cpShapeSegmentQuery", dynlib: Lib.} + +#/ Get the hit point for a segment query. +## Possibly change; info to PSegmentQueryInfo +proc queryHitPoint*(start, to: TVector, info: TSegmentQueryInfo): TVector {.inline.} = + result = start.lerp(to, info.t) + +#/ Get the hit distance for a segment query. +proc queryHitDist*(start, to: TVector, info: TSegmentQueryInfo): CpFloat {.inline.} = + result = start.dist(to) * info.t + +defGetter(PShape, PSpace, space, Space) + +defGetter(PShape, PBody, body, Body) +proc setBody*(shape: PShape, value: PBody) {. + cdecl, importc: "cpShapeSetBody", dynlib: Lib.} + + +defGetter(PShape, TBB, bb, BB) +defShapeProp(Bool32, sensor, Sensor, true) +defShapeProp(CpFloat, e, Elasticity, false) +defShapeProp(CpFloat, u, Friction, true) +defShapeProp(TVector, surface_v, SurfaceVelocity, true) +defShapeProp(pointer, data, UserData, false) +defShapeProp(TCollisionType, collision_type, CollisionType, true) +defShapeProp(TGroup, group, Group, true) +defShapeProp(TLayers, layers, Layers, true) + +#/ When initializing a shape, it's hash value comes from a counter. +#/ Because the hash value may affect iteration order, you can reset the shape ID counter +#/ when recreating a space. This will make the simulation be deterministic. +proc resetShapeIdCounter*(): void {.cdecl, importc: "cpResetShapeIdCounter", dynlib: Lib.} +#/ Allocate a circle shape. +proc CircleShapeAlloc*(): PCircleShape {.cdecl, importc: "cpCircleShapeAlloc", dynlib: Lib.} +#/ Initialize a circle shape. +proc init*(circle: PCircleShape, body: PBody, radius: CpFloat, offset: TVector): PCircleShape {. + cdecl, importc: "cpCircleShapeInit", dynlib: Lib.} +#/ Allocate and initialize a circle shape. +proc newCircleShape*(body: PBody, radius: CpFloat, offset: TVector): PShape {. + cdecl, importc: "cpCircleShapeNew", dynlib: Lib.} + +proc getCircleOffset*(shape: PShape): TVector {. + cdecl, importc: "cpCircleShapeGetOffset", dynlib: Lib.} +proc getCircleRadius*(shape: PShape): CpFloat {. + cdecl, importc: "cpCircleShapeGetRadius", dynlib: Lib.} + + +#/ Allocate a polygon shape. +proc allocPolyShape*(): PPolyShape {. + cdecl, importc: "cpPolyShapeAlloc", dynlib: Lib.} +#/ Initialize a polygon shape. +#/ A convex hull will be created from the vertexes. +proc init*(poly: PPolyShape; body: PBody, numVerts: cint; + verts: ptr TVector; offset: TVector): PPolyShape {. + cdecl, importc: "cpPolyShapeInit", dynlib: Lib.} +#/ Allocate and initialize a polygon shape. +#/ A convex hull will be created from the vertexes. +proc newPolyShape*(body: PBody; numVerts: cint; verts: ptr TVector; + offset: TVector): PShape {. + cdecl, importc: "cpPolyShapeNew", dynlib: Lib.} +#/ Initialize a box shaped polygon shape. +proc init*(poly: PPolyShape; body: PBody; width, height: CpFloat): PPolyShape {. + cdecl, importc: "cpBoxShapeInit", dynlib: Lib.} +#/ Initialize an offset box shaped polygon shape. +proc init*(poly: PPolyShape; body: PBody; box: TBB): PPolyShape {. + cdecl, importc: "cpBoxShapeInit2", dynlib: Lib.} +#/ Allocate and initialize a box shaped polygon shape. +proc newBoxShape*(body: PBody; width, height: CpFloat): PShape {. + cdecl, importc: "cpBoxShapeNew", dynlib: Lib.} +#/ Allocate and initialize an offset box shaped polygon shape. +proc newBoxShape*(body: PBody; box: TBB): PShape {. + cdecl, importc: "cpBoxShapeNew2", dynlib: Lib.} + +#/ Check that a set of vertexes is convex and has a clockwise winding. +#/ NOTE: Due to floating point precision issues, hulls created with cpQuickHull() are not guaranteed to validate! +proc validatePoly*(verts: ptr TVector; numVerts: cint): bool {. + cdecl, importc: "cpPolyValidate", dynlib: Lib.} +#/ Get the number of verts in a polygon shape. +proc getNumVerts*(shape: PShape): cint {. + cdecl, importc: "cpPolyShapeGetNumVerts", dynlib: Lib.} +#/ Get the @c ith vertex of a polygon shape. +proc getVert*(shape: PShape; index: cint): TVector {. + cdecl, importc: "cpPolyShapeGetVert", dynlib: Lib.} + +#/ Allocate a segment shape. +proc allocSegmentShape*(): PSegmentShape {. + cdecl, importc: "cpSegmentShapeAlloc", dynlib: Lib.} +#/ Initialize a segment shape. +proc init*(seg: PSegmentShape, body: PBody, a, b: TVector, radius: CpFloat): PSegmentShape {. + cdecl, importc: "cpSegmentShapeInit", dynlib: Lib.} +#/ Allocate and initialize a segment shape. +proc newSegmentShape*(body: PBody, a, b: TVector, radius: CpFloat): PShape {. + cdecl, importc: "cpSegmentShapeNew", dynlib: Lib.} + +proc setSegmentNeighbors*(shape: PShape, prev, next: TVector) {. + cdecl, importc: "cpSegmentShapeSetNeighbors", dynlib: Lib.} +proc getSegmentA*(shape: PShape): TVector {. + cdecl, importc: "cpSegmentShapeGetA", dynlib: Lib.} +proc getSegmentB*(shape: PShape): TVector {. + cdecl, importc: "cpSegmentShapeGetB", dynlib: Lib.} +proc getSegmentNormal*(shape: PShape): TVector {. + cdecl, importc: "cpSegmentShapeGetNormal", dynlib: Lib.} +proc getSegmentRadius*(shape: PShape): CpFloat {. + cdecl, importc: "cpSegmentShapeGetRadius", dynlib: Lib.} + + +#/ Version string. +#var VersionString*{.importc: "cpVersionString", dynlib: Lib.}: cstring +#/ Calculate the moment of inertia for a circle. +#/ @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0. +when defined(MoreNimrod): + proc momentForCircle*(m, r1, r2: CpFloat; offset: TVector): CpFloat {.cdecl.} = + result = m * (0.5 * (r1 * r1 + r2 * r2) + lenSq(offset)) +else: + proc momentForCircle*(m, r1, r2: CpFloat; offset: TVector): CpFloat {. + cdecl, importc: "cpMomentForCircle", dynlib: Lib.} + +#/ Calculate area of a hollow circle. +#/ @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0. +proc AreaForCircle*(r1: CpFloat; r2: CpFloat): CpFloat {. + cdecl, importc: "cpAreaForCircle", dynlib: Lib.} +#/ Calculate the moment of inertia for a line segment. +#/ Beveling radius is not supported. +proc MomentForSegment*(m: CpFloat; a, b: TVector): CpFloat {. + cdecl, importc: "cpMomentForSegment", dynlib: Lib.} +#/ Calculate the area of a fattened (capsule shaped) line segment. +proc AreaForSegment*(a, b: TVector; r: CpFloat): CpFloat {. + cdecl, importc: "cpAreaForSegment", dynlib: Lib.} +#/ Calculate the moment of inertia for a solid polygon shape assuming it's center of gravity is at it's centroid. The offset is added to each vertex. +proc MomentForPoly*(m: CpFloat; numVerts: cint; verts: ptr TVector; offset: TVector): CpFloat {. + cdecl, importc: "cpMomentForPoly", dynlib: Lib.} +#/ Calculate the signed area of a polygon. A Clockwise winding gives positive area. +#/ This is probably backwards from what you expect, but matches Chipmunk's the winding for poly shapes. +proc AreaForPoly*(numVerts: cint; verts: ptr TVector): CpFloat {. + cdecl, importc: "cpAreaForPoly", dynlib: Lib.} +#/ Calculate the natural centroid of a polygon. +proc CentroidForPoly*(numVerts: cint; verts: ptr TVector): TVector {. + cdecl, importc: "cpCentroidForPoly", dynlib: Lib.} +#/ Center the polygon on the origin. (Subtracts the centroid of the polygon from each vertex) +proc RecenterPoly*(numVerts: cint; verts: ptr TVector) {. + cdecl, importc: "cpRecenterPoly", dynlib: Lib.} +#/ Calculate the moment of inertia for a solid box. +proc MomentForBox*(m, width, height: CpFloat): CpFloat {. + cdecl, importc: "cpMomentForBox", dynlib: Lib.} +#/ Calculate the moment of inertia for a solid box. +proc MomentForBox2*(m: CpFloat; box: TBB): CpFloat {. + cdecl, importc: "cpMomentForBox2", dynlib: Lib.} + + + +##constraints +type + #TODO: all these are private + #TODO: defConstraintProp() + PPinJoint = ptr TPinJoint + TPinJoint{.pf.} = object + constraint: PConstraint + anchr1: TVector + anchr2: TVector + dist: CpFloat + r1: TVector + r2: TVector + n: TVector + nMass: CpFloat + jnAcc: CpFloat + jnMax: CpFloat + bias: CpFloat + PSlideJoint = ptr TSlideJoint + TSlideJoint{.pf.} = object + constraint: PConstraint + anchr1: TVector + anchr2: TVector + min: CpFloat + max: CpFloat + r1: TVector + r2: TVector + n: TVector + nMass: CpFloat + jnAcc: CpFloat + jnMax: CpFloat + bias: CpFloat + PPivotJoint = ptr TPivotJoint + TPivotJoint{.pf.} = object + constraint: PConstraint + anchr1: TVector + anchr2: TVector + r1: TVector + r2: TVector + k1: TVector + k2: TVector + jAcc: TVector + jMaxLen: CpFloat + bias: TVector + PGrooveJoint = ptr TGrooveJoint + TGrooveJoint{.pf.} = object + constraint: PConstraint + grv_n: TVector + grv_a: TVector + grv_b: TVector + anchr2: TVector + grv_tn: TVector + clamp: CpFloat + r1: TVector + r2: TVector + k1: TVector + k2: TVector + jAcc: TVector + jMaxLen: CpFloat + bias: TVector + PDampedSpring = ptr TDampedSpring + TDampedSpring{.pf.} = object + constraint: PConstraint + anchr1: TVector + anchr2: TVector + restLength: CpFloat + stiffness: CpFloat + damping: CpFloat + springForceFunc: TDampedSpringForceFunc + target_vrn: CpFloat + v_coef: CpFloat + r1: TVector + r2: TVector + nMass: CpFloat + n: TVector + PDampedRotarySpring = ptr TDampedRotarySpring + TDampedRotarySpring{.pf.} = object + constraint: PConstraint + restAngle: CpFloat + stiffness: CpFloat + damping: CpFloat + springTorqueFunc: TDampedRotarySpringTorqueFunc + target_wrn: CpFloat + w_coef: CpFloat + iSum: CpFloat + PRotaryLimitJoint = ptr TRotaryLimitJoint + TRotaryLimitJoint{.pf.} = object + constraint: PConstraint + min: CpFloat + max: CpFloat + iSum: CpFloat + bias: CpFloat + jAcc: CpFloat + jMax: CpFloat + PRatchetJoint = ptr TRatchetJoint + TRatchetJoint{.pf.} = object + constraint: PConstraint + angle: CpFloat + phase: CpFloat + ratchet: CpFloat + iSum: CpFloat + bias: CpFloat + jAcc: CpFloat + jMax: CpFloat + PGearJoint = ptr TGearJoint + TGearJoint{.pf.} = object + constraint: PConstraint + phase: CpFloat + ratio: CpFloat + ratio_inv: CpFloat + iSum: CpFloat + bias: CpFloat + jAcc: CpFloat + jMax: CpFloat + PSimpleMotor = ptr TSimpleMotor + TSimpleMotor{.pf.} = object + constraint: PConstraint + rate: CpFloat + iSum: CpFloat + jAcc: CpFloat + jMax: CpFloat + TDampedSpringForceFunc* = proc (spring: PConstraint; dist: CpFloat): CpFloat{. + cdecl.} + TDampedRotarySpringTorqueFunc* = proc (spring: PConstraint; + relativeAngle: CpFloat): CpFloat {.cdecl.} +#/ Destroy a constraint. +proc destroy*(constraint: PConstraint){. + cdecl, importc: "cpConstraintDestroy", dynlib: Lib.} +#/ Destroy and free a constraint.111 +proc free*(constraint: PConstraint){. + cdecl, importc: "cpConstraintFree", dynlib: Lib.} + +#/ @private +proc activateBodies(constraint: PConstraint) {.inline.} = + if not constraint.a.isNil: constraint.a.activate() + if not constraint.b.isNil: constraint.b.activate() + +# /// @private +# #define CP_DefineConstraintStructGetter(type, member, name) \ +# static inline type cpConstraint##Get##name(const cpConstraint *constraint){return constraint->member;} +# /// @private +# #define CP_DefineConstraintStructSetter(type, member, name) \ +# static inline void cpConstraint##Set##name(cpConstraint *constraint, type value){ \ +# cpConstraintActivateBodies(constraint); \ +# constraint->member = value; \ +# } +template defConstraintSetter(memberType: typedesc, member: expr, name: expr): stmt {.immediate.} = + proc `set name`*(constraint: PConstraint, value: memberType) {.cdecl.} = + activateBodies(constraint) + constraint.member = value +template defConstraintProp(memberType: typedesc, member: expr, name: expr): stmt {.immediate.} = + defGetter(PConstraint, memberType, member, name) + defConstraintSetter(memberType, member, name) +# CP_DefineConstraintStructGetter(cpSpace*, CP_PRIVATE(space), Space) +defGetter(PConstraint, PSpace, space, Space) +defGetter(PConstraint, PBody, a, A) +defGetter(PConstraint, PBody, a, B) +defGetter(PConstraint, CpFloat, maxForce, MaxForce) +defGetter(PConstraint, CpFloat, errorBias, ErrorBias) +defGetter(PConstraint, CpFloat, maxBias, MaxBias) +defGetter(PConstraint, TConstraintPreSolveFunc, preSolve, PreSolveFunc) +defGetter(PConstraint, TConstraintPostSolveFunc, postSolve, PostSolveFunc) +defGetter(PConstraint, CpDataPointer, data, UserData) +# Get the last impulse applied by this constraint. +proc getImpulse*(constraint: PConstraint): CpFloat {.inline.} = + return constraint.klass.getImpulse(constraint) + +# #define cpConstraintCheckCast(constraint, struct) \ +# cpAssertHard(constraint->CP_PRIVATE(klass) == struct##GetClass(), "Constraint is not a "#struct) +# #define CP_DefineConstraintGetter(struct, type, member, name) \ +# static inline type struct##Get##name(const cpConstraint *constraint){ \ +# cpConstraintCheckCast(constraint, struct); \ +# return ((struct *)constraint)->member; \ +# } +# #define CP_DefineConstraintSetter(struct, type, member, name) \ +# static inline void struct##Set##name(cpConstraint *constraint, type value){ \ +# cpConstraintCheckCast(constraint, struct); \ +# cpConstraintActivateBodies(constraint); \ +# ((struct *)constraint)->member = value; \ +# } +template constraintCheckCast(constraint: PConstraint, ctype: expr): stmt {.immediate.} = + assert(constraint.klass == `ctype getClass`(), "Constraint is the wrong class") +template defCGetter(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} = + proc `get ctype name`*(constraint: PConstraint): memberType {.cdecl.} = + constraintCheckCast(constraint, ctype) + result = cast[`P ctype`](constraint).member +template defCSetter(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} = + proc `set ctype name`*(constraint: PConstraint, value: memberType) {.cdecl.} = + constraintCheckCast(constraint, ctype) + activateBodies(constraint) + cast[`P ctype`](constraint).member = value +template defCProp(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} = + defCGetter(ctype, memberType, member, name) + defCSetter(ctype, memberType, member, name) + +proc PinJointGetClass*(): PConstraintClass{. + cdecl, importc: "cpPinJointGetClass", dynlib: Lib.} +#/ @private + +#/ Allocate a pin joint. +proc AllocPinJoint*(): PPinJoint{. + cdecl, importc: "cpPinJointAlloc", dynlib: Lib.} +#/ Initialize a pin joint. +proc PinJointInit*(joint: PPinJoint; a: PBody; b: PBody; anchr1: TVector; + anchr2: TVector): PPinJoint{. + cdecl, importc: "cpPinJointInit", dynlib: Lib.} +#/ Allocate and initialize a pin joint. +proc newPinJoint*(a: PBody; b: PBody; anchr1: TVector; anchr2: TVector): PConstraint{. + cdecl, importc: "cpPinJointNew", dynlib: Lib.} +# CP_DefineConstraintProperty(cpPinJoint, cpVect, anchr1, Anchr1) +defCProp(PinJoint, TVector, anchr1, Anchr1) +defCProp(PinJoint, TVector, anchr2, Anchr2) +defCProp(PinJoint, CpFloat, dist, Dist) + +proc SlideJointGetClass*(): PConstraintClass{. + cdecl, importc: "cpSlideJointGetClass", dynlib: Lib.} +#/ Allocate a slide joint. +proc AllocSlideJoint*(): PSlideJoint{. + cdecl, importc: "cpSlideJointAlloc", dynlib: Lib.} +#/ Initialize a slide joint. +proc init*(joint: PSlideJoint; a, b: PBody; anchr1, anchr2: TVector; + min, max: CpFloat): PSlideJoint{. + cdecl, importc: "cpSlideJointInit", dynlib: Lib.} +#/ Allocate and initialize a slide joint. +proc newSlideJoint*(a, b: PBody; anchr1, anchr2: TVector; min, max: CpFloat): PConstraint{. + cdecl, importc: "cpSlideJointNew", dynlib: Lib.} + +defCProp(SlideJoint, TVector, anchr1, Anchr1) +defCProp(SlideJoint, TVector, anchr2, Anchr2) +defCProp(SlideJoint, CpFloat, min, Min) +defCProp(SlideJoint, CpFloat, max, Max) + +proc pivotJointGetClass*(): PConstraintClass {. + cdecl, importc: "cpPivotJointGetClass", dynlib: Lib.} + +#/ Allocate a pivot joint +proc allocPivotJoint*(): PPivotJoint{. + cdecl, importc: "cpPivotJointAlloc", dynlib: Lib.} +#/ Initialize a pivot joint. +proc init*(joint: PPivotJoint; a, b: PBody; anchr1, anchr2: TVector): PPivotJoint{. + cdecl, importc: "cpPivotJointInit", dynlib: Lib.} +#/ Allocate and initialize a pivot joint. +proc newPivotJoint*(a, b: PBody; pivot: TVector): PConstraint{. + cdecl, importc: "cpPivotJointNew", dynlib: Lib.} +#/ Allocate and initialize a pivot joint with specific anchors. +proc newPivotJoint*(a, b: PBody; anchr1, anchr2: TVector): PConstraint{. + cdecl, importc: "cpPivotJointNew2", dynlib: Lib.} + +defCProp(PivotJoint, TVector, anchr1, Anchr1) +defCProp(PivotJoint, TVector, anchr2, Anchr2) + + +proc GrooveJointGetClass*(): PConstraintClass{. + cdecl, importc: "cpGrooveJointGetClass", dynlib: Lib.} +#/ Allocate a groove joint. +proc GrooveJointAlloc*(): ptr TGrooveJoint{. + cdecl, importc: "cpGrooveJointAlloc", dynlib: Lib.} +#/ Initialize a groove joint. +proc Init*(joint: PGrooveJoint; a, b: PBody; groove_a, groove_b, anchr2: TVector): PGrooveJoint{. + cdecl, importc: "cpGrooveJointInit", dynlib: Lib.} +#/ Allocate and initialize a groove joint. +proc newGrooveJoint*(a, b: PBody; groove_a, groove_b, anchr2: TVector): PConstraint{. + cdecl, importc: "cpGrooveJointNew", dynlib: Lib.} + +defCGetter(GrooveJoint, TVector, grv_a, GrooveA) +defCGetter(GrooveJoint, TVector, grv_b, GrooveB) +# /// Set endpoint a of a groove joint's groove +proc SetGrooveA*(constraint: PConstraint, value: TVector) {. + cdecl, importc: "cpGrooveJointSetGrooveA", dynlib: Lib.} +# /// Set endpoint b of a groove joint's groove +proc SetGrooveB*(constraint: PConstraint, value: TVector) {. + cdecl, importc: "cpGrooveJointSetGrooveB", dynlib: Lib.} +defCProp(GrooveJoint, TVector, anchr2, Anchr2) + +proc DampedSpringGetClass*(): PConstraintClass{. + cdecl, importc: "cpDampedSpringGetClass", dynlib: Lib.} +#/ Allocate a damped spring. +proc AllocDampedSpring*(): PDampedSpring{. + cdecl, importc: "cpDampedSpringAlloc", dynlib: Lib.} +#/ Initialize a damped spring. +proc init*(joint: PDampedSpring; a, b: PBody; anchr1, anchr2: TVector; + restLength, stiffness, damping: CpFloat): PDampedSpring{. + cdecl, importc: "cpDampedSpringInit", dynlib: Lib.} +#/ Allocate and initialize a damped spring. +proc newDampedSpring*(a, b: PBody; anchr1, anchr2: TVector; + restLength, stiffness, damping: CpFloat): PConstraint{. + cdecl, importc: "cpDampedSpringNew", dynlib: Lib.} + +# CP_DefineConstraintProperty(cpDampedSpring, cpVect, anchr1, Anchr1) +defCProp(DampedSpring, TVector, anchr1, Anchr1) +defCProp(DampedSpring, TVector, anchr2, Anchr2) +defCProp(DampedSpring, CpFloat, restLength, RestLength) +defCProp(DampedSpring, CpFloat, stiffness, Stiffness) +defCProp(DampedSpring, CpFloat, damping, Damping) +defCProp(DampedSpring, TDampedSpringForceFunc, springForceFunc, SpringForceFunc) + + +proc DampedRotarySpringGetClass*(): PConstraintClass{. + cdecl, importc: "cpDampedRotarySpringGetClass", dynlib: Lib.} + +#/ Allocate a damped rotary spring. +proc DampedRotarySpringAlloc*(): PDampedRotarySpring{. + cdecl, importc: "cpDampedRotarySpringAlloc", dynlib: Lib.} +#/ Initialize a damped rotary spring. +proc init*(joint: PDampedRotarySpring; a, b: PBody; + restAngle, stiffness, damping: CpFloat): PDampedRotarySpring{. + cdecl, importc: "cpDampedRotarySpringInit", dynlib: Lib.} +#/ Allocate and initialize a damped rotary spring. +proc DampedRotarySpringNew*(a, b: PBody; restAngle, stiffness, damping: CpFloat): PConstraint{. + cdecl, importc: "cpDampedRotarySpringNew", dynlib: Lib.} + +defCProp(DampedRotarySpring, CpFloat, restAngle, RestAngle) +defCProp(DampedRotarySpring, CpFloat, stiffness, Stiffness) +defCProp(DampedRotarySpring, CpFloat, damping, Damping) +defCProp(DampedRotarySpring, TDampedRotarySpringTorqueFunc, springTorqueFunc, SpringTorqueFunc) + + +proc RotaryLimitJointGetClass*(): PConstraintClass{. + cdecl, importc: "cpRotaryLimitJointGetClass", dynlib: Lib.} +#/ Allocate a damped rotary limit joint. +proc allocRotaryLimitJoint*(): PRotaryLimitJoint{. + cdecl, importc: "cpRotaryLimitJointAlloc", dynlib: Lib.} +#/ Initialize a damped rotary limit joint. +proc init*(joint: PRotaryLimitJoint; a, b: PBody; min, max: CpFloat): PRotaryLimitJoint{. + cdecl, importc: "cpRotaryLimitJointInit", dynlib: Lib.} +#/ Allocate and initialize a damped rotary limit joint. +proc newRotaryLimitJoint*(a, b: PBody; min, max: CpFloat): PConstraint{. + cdecl, importc: "cpRotaryLimitJointNew", dynlib: Lib.} + +defCProp(RotaryLimitJoint, CpFloat, min, Min) +defCProp(RotaryLimitJoint, CpFloat, max, Max) + + +proc RatchetJointGetClass*(): PConstraintClass{. + cdecl, importc: "cpRatchetJointGetClass", dynlib: Lib.} +#/ Allocate a ratchet joint. +proc AllocRatchetJoint*(): PRatchetJoint{. + cdecl, importc: "cpRatchetJointAlloc", dynlib: Lib.} +#/ Initialize a ratched joint. +proc init*(joint: PRatchetJoint; a, b: PBody; phase, ratchet: CpFloat): PRatchetJoint{. + cdecl, importc: "cpRatchetJointInit", dynlib: Lib.} +#/ Allocate and initialize a ratchet joint. +proc NewRatchetJoint*(a, b: PBody; phase, ratchet: CpFloat): PConstraint{. + cdecl, importc: "cpRatchetJointNew", dynlib: Lib.} + +defCProp(RatchetJoint, CpFloat, angle, Angle) +defCProp(RatchetJoint, CpFloat, phase, Phase) +defCProp(RatchetJoint, CpFloat, ratchet, Ratchet) + + +proc GearJointGetClass*(): PConstraintClass{.cdecl, + importc: "cpGearJointGetClass", dynlib: Lib.} +#/ Allocate a gear joint. +proc AllocGearJoint*(): PGearJoint{. + cdecl, importc: "cpGearJointAlloc", dynlib: Lib.} +#/ Initialize a gear joint. +proc init*(joint: PGearJoint; a, b: PBody, phase, ratio: CpFloat): PGearJoint{. + cdecl, importc: "cpGearJointInit", dynlib: Lib.} +#/ Allocate and initialize a gear joint. +proc NewGearJoint*(a, b: PBody; phase, ratio: CpFloat): PConstraint{. + cdecl, importc: "cpGearJointNew", dynlib: Lib.} + +defCProp(GearJoint, CpFloat, phase, Phase) +defCGetter(GearJoint, CpFloat, ratio, Ratio) +#/ Set the ratio of a gear joint. +proc GearJointSetRatio*(constraint: PConstraint; value: CpFloat){. + cdecl, importc: "cpGearJointSetRatio", dynlib: Lib.} + + +proc SimpleMotorGetClass*(): PConstraintClass{. + cdecl, importc: "cpSimpleMotorGetClass", dynlib: Lib.} +#/ Allocate a simple motor. +proc AllocSimpleMotor*(): PSimpleMotor{. + cdecl, importc: "cpSimpleMotorAlloc", dynlib: Lib.} +#/ initialize a simple motor. +proc init*(joint: PSimpleMotor; a, b: PBody; + rate: CpFloat): PSimpleMotor{. + cdecl, importc: "cpSimpleMotorInit", dynlib: Lib.} +#/ Allocate and initialize a simple motor. +proc newSimpleMotor*(a, b: PBody; rate: CpFloat): PConstraint{. + cdecl, importc: "cpSimpleMotorNew", dynlib: Lib.} + +defCProp(SimpleMotor, CpFloat, rate, Rate) + + + diff --git a/tests/manyloc/keineschweine/dependencies/enet/enet.nim b/tests/manyloc/keineschweine/dependencies/enet/enet.nim new file mode 100644 index 000000000..ad43c69b7 --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/enet/enet.nim @@ -0,0 +1,614 @@ +discard """Copyright (c) 2002-2012 Lee Salzman + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +when defined(Linux): + const Lib = "libenet.so.1(|.0.3)" +else: + {.error: "Your platform has not been accounted for."} +{.deadCodeElim: ON.} +const + ENET_VERSION_MAJOR* = 1 + ENET_VERSION_MINOR* = 3 + ENET_VERSION_PATCH* = 3 +template ENET_VERSION_CREATE(major, minor, patch: expr): expr = + (((major) shl 16) or ((minor) shl 8) or (patch)) + +const + ENET_VERSION* = ENET_VERSION_CREATE(ENET_VERSION_MAJOR, ENET_VERSION_MINOR, + ENET_VERSION_PATCH) +type + TVersion* = cuint + TSocketType*{.size: sizeof(cint).} = enum + ENET_SOCKET_TYPE_STREAM = 1, ENET_SOCKET_TYPE_DATAGRAM = 2 + TSocketWait*{.size: sizeof(cint).} = enum + ENET_SOCKET_WAIT_NONE = 0, ENET_SOCKET_WAIT_SEND = (1 shl 0), + ENET_SOCKET_WAIT_RECEIVE = (1 shl 1) + TSocketOption*{.size: sizeof(cint).} = enum + ENET_SOCKOPT_NONBLOCK = 1, ENET_SOCKOPT_BROADCAST = 2, + ENET_SOCKOPT_RCVBUF = 3, ENET_SOCKOPT_SNDBUF = 4, + ENET_SOCKOPT_REUSEADDR = 5 +const + ENET_HOST_ANY* = 0 + ENET_HOST_BROADCAST* = 0xFFFFFFFF + ENET_PORT_ANY* = 0 + + ENET_PROTOCOL_MINIMUM_MTU* = 576 + ENET_PROTOCOL_MAXIMUM_MTU* = 4096 + ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS* = 32 + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE* = 4096 + ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE* = 32768 + ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT* = 1 + ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT* = 255 + ENET_PROTOCOL_MAXIMUM_PEER_ID* = 0x00000FFF +type + PAddress* = ptr TAddress + TAddress*{.pure, final.} = object + host*: cuint + port*: cushort + + TPacketFlag*{.size: sizeof(cint).} = enum + FlagReliable = (1 shl 0), + FlagUnsequenced = (1 shl 1), + NoAllocate = (1 shl 2), + UnreliableFragment = (1 shl 3) + + TENetListNode*{.pure, final.} = object + next*: ptr T_ENetListNode + previous*: ptr T_ENetListNode + + PENetListIterator* = ptr TENetListNode + TENetList*{.pure, final.} = object + sentinel*: TENetListNode + + T_ENetPacket*{.pure, final.} = object + TPacketFreeCallback* = proc (a2: ptr T_ENetPacket){.cdecl.} + + PPacket* = ptr TPacket + TPacket*{.pure, final.} = object + referenceCount: csize + flags*: cint + data*: cstring#ptr cuchar + dataLength*: csize + freeCallback*: TPacketFreeCallback + + PAcknowledgement* = ptr TAcknowledgement + TAcknowledgement*{.pure, final.} = object + acknowledgementList*: TEnetListNode + sentTime*: cuint + command*: TEnetProtocol + + POutgoingCommand* = ptr TOutgoingCommand + TOutgoingCommand*{.pure, final.} = object + outgoingCommandList*: TEnetListNode + reliableSequenceNumber*: cushort + unreliableSequenceNumber*: cushort + sentTime*: cuint + roundTripTimeout*: cuint + roundTripTimeoutLimit*: cuint + fragmentOffset*: cuint + fragmentLength*: cushort + sendAttempts*: cushort + command*: TEnetProtocol + packet*: PPacket + + PIncomingCommand* = ptr TIncomingCommand + TIncomingCommand*{.pure, final.} = object + incomingCommandList*: TEnetListNode + reliableSequenceNumber*: cushort + unreliableSequenceNumber*: cushort + command*: TEnetProtocol + fragmentCount*: cuint + fragmentsRemaining*: cuint + fragments*: ptr cuint + packet*: ptr TPacket + + TPeerState*{.size: sizeof(cint).} = enum + ENET_PEER_STATE_DISCONNECTED = 0, ENET_PEER_STATE_CONNECTING = 1, + ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2, + ENET_PEER_STATE_CONNECTION_PENDING = 3, + ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4, ENET_PEER_STATE_CONNECTED = 5, + ENET_PEER_STATE_DISCONNECT_LATER = 6, ENET_PEER_STATE_DISCONNECTING = 7, + ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT = 8, ENET_PEER_STATE_ZOMBIE = 9 + + TENetProtocolCommand*{.size: sizeof(cint).} = enum + ENET_PROTOCOL_COMMAND_NONE = 0, ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1, + ENET_PROTOCOL_COMMAND_CONNECT = 2, + ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3, + ENET_PROTOCOL_COMMAND_DISCONNECT = 4, ENET_PROTOCOL_COMMAND_PING = 5, + ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6, + ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7, + ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8, + ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9, + ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10, + ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11, + ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12, + ENET_PROTOCOL_COMMAND_COUNT = 13, ENET_PROTOCOL_COMMAND_MASK = 0x0000000F + TENetProtocolFlag*{.size: sizeof(cint).} = enum + ENET_PROTOCOL_HEADER_SESSION_SHIFT = 12, + ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 shl 6), + ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 shl 7), + ENET_PROTOCOL_HEADER_SESSION_MASK = (3 shl 12), + ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 shl 14), + ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 shl 15), + ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED.cint or + ENET_PROTOCOL_HEADER_FLAG_SENT_TIME.cint + + TENetProtocolHeader*{.pure, final.} = object + peerID*: cushort + sentTime*: cushort + + TENetProtocolCommandHeader*{.pure, final.} = object + command*: cuchar + channelID*: cuchar + reliableSequenceNumber*: cushort + + TENetProtocolAcknowledge*{.pure, final.} = object + header*: TENetProtocolCommandHeader + receivedReliableSequenceNumber*: cushort + receivedSentTime*: cushort + + TENetProtocolConnect*{.pure, final.} = object + header*: TENetProtocolCommandHeader + outgoingPeerID*: cushort + incomingSessionID*: cuchar + outgoingSessionID*: cuchar + mtu*: cuint + windowSize*: cuint + channelCount*: cuint + incomingBandwidth*: cuint + outgoingBandwidth*: cuint + packetThrottleInterval*: cuint + packetThrottleAcceleration*: cuint + packetThrottleDeceleration*: cuint + connectID*: cuint + data*: cuint + + TENetProtocolVerifyConnect*{.pure, final.} = object + header*: TENetProtocolCommandHeader + outgoingPeerID*: cushort + incomingSessionID*: cuchar + outgoingSessionID*: cuchar + mtu*: cuint + windowSize*: cuint + channelCount*: cuint + incomingBandwidth*: cuint + outgoingBandwidth*: cuint + packetThrottleInterval*: cuint + packetThrottleAcceleration*: cuint + packetThrottleDeceleration*: cuint + connectID*: cuint + + TENetProtocolBandwidthLimit*{.pure, final.} = object + header*: TENetProtocolCommandHeader + incomingBandwidth*: cuint + outgoingBandwidth*: cuint + + TENetProtocolThrottleConfigure*{.pure, final.} = object + header*: TENetProtocolCommandHeader + packetThrottleInterval*: cuint + packetThrottleAcceleration*: cuint + packetThrottleDeceleration*: cuint + + TENetProtocolDisconnect*{.pure, final.} = object + header*: TENetProtocolCommandHeader + data*: cuint + + TENetProtocolPing*{.pure, final.} = object + header*: TENetProtocolCommandHeader + + TENetProtocolSendReliable*{.pure, final.} = object + header*: TENetProtocolCommandHeader + dataLength*: cushort + + TENetProtocolSendUnreliable*{.pure, final.} = object + header*: TENetProtocolCommandHeader + unreliableSequenceNumber*: cushort + dataLength*: cushort + + TENetProtocolSendUnsequenced*{.pure, final.} = object + header*: TENetProtocolCommandHeader + unsequencedGroup*: cushort + dataLength*: cushort + + TENetProtocolSendFragment*{.pure, final.} = object + header*: TENetProtocolCommandHeader + startSequenceNumber*: cushort + dataLength*: cushort + fragmentCount*: cuint + fragmentNumber*: cuint + totalLength*: cuint + fragmentOffset*: cuint + + ## this is incomplete; need helper templates or something + ## ENetProtocol + TENetProtocol*{.pure, final.} = object + header*: TENetProtocolCommandHeader +const + ENET_BUFFER_MAXIMUM* = (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS) + ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024 + ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024 + ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000 + ENET_HOST_DEFAULT_MTU = 1400 + + ENET_PEER_DEFAULT_ROUND_TRIP_TIME = 500 + ENET_PEER_DEFAULT_PACKET_THROTTLE = 32 + ENET_PEER_PACKET_THROTTLE_SCALE = 32 + ENET_PEER_PACKET_THROTTLE_COUNTER = 7 + ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2 + ENET_PEER_PACKET_THROTTLE_DECELERATION = 2 + ENET_PEER_PACKET_THROTTLE_INTERVAL = 5000 + ENET_PEER_PACKET_LOSS_SCALE = (1 shl 16) + ENET_PEER_PACKET_LOSS_INTERVAL = 10000 + ENET_PEER_WINDOW_SIZE_SCALE = 64 * 1024 + ENET_PEER_TIMEOUT_LIMIT = 32 + ENET_PEER_TIMEOUT_MINIMUM = 5000 + ENET_PEER_TIMEOUT_MAXIMUM = 30000 + ENET_PEER_PING_INTERVAL = 500 + ENET_PEER_UNSEQUENCED_WINDOWS = 64 + ENET_PEER_UNSEQUENCED_WINDOW_SIZE = 1024 + ENET_PEER_FREE_UNSEQUENCED_WINDOWS = 32 + ENET_PEER_RELIABLE_WINDOWS = 16 + ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000 + ENET_PEER_FREE_RELIABLE_WINDOWS = 8 + +when defined(Linux): + import posix + const + ENET_SOCKET_NULL*: cint = -1 + type + TENetSocket* = cint + PEnetBuffer* = ptr object + TENetBuffer*{.pure, final.} = object + data*: pointer + dataLength*: csize + TENetSocketSet* = Tfd_set + ## see if these are different on win32, if not then get rid of these + template ENET_HOST_TO_NET_16*(value: expr): expr = + (htons(value)) + template ENET_HOST_TO_NET_32*(value: expr): expr = + (htonl(value)) + template ENET_NET_TO_HOST_16*(value: expr): expr = + (ntohs(value)) + template ENET_NET_TO_HOST_32*(value: expr): expr = + (ntohl(value)) + + template ENET_SOCKETSET_EMPTY*(sockset: expr): expr = + FD_ZERO(addr((sockset))) + template ENET_SOCKETSET_ADD*(sockset, socket: expr): expr = + FD_SET(socket, addr((sockset))) + template ENET_SOCKETSET_REMOVE*(sockset, socket: expr): expr = + FD_CLEAR(socket, addr((sockset))) + template ENET_SOCKETSET_CHECK*(sockset, socket: expr): expr = + FD_ISSET(socket, addr((sockset))) + +when defined(Windows): + ## put the content of win32.h in here + + +type + PChannel* = ptr TChannel + TChannel*{.pure, final.} = object + outgoingReliableSequenceNumber*: cushort + outgoingUnreliableSequenceNumber*: cushort + usedReliableWindows*: cushort + reliableWindows*: array[0..ENET_PEER_RELIABLE_WINDOWS - 1, cushort] + incomingReliableSequenceNumber*: cushort + incomingUnreliableSequenceNumber*: cushort + incomingReliableCommands*: TENetList + incomingUnreliableCommands*: TENetList + + PPeer* = ptr TPeer + TPeer*{.pure, final.} = object + dispatchList*: TEnetListNode + host*: ptr THost + outgoingPeerID*: cushort + incomingPeerID*: cushort + connectID*: cuint + outgoingSessionID*: cuchar + incomingSessionID*: cuchar + address*: TAddress + data*: pointer + state*: TPeerState + channels*: PChannel + channelCount*: csize + incomingBandwidth*: cuint + outgoingBandwidth*: cuint + incomingBandwidthThrottleEpoch*: cuint + outgoingBandwidthThrottleEpoch*: cuint + incomingDataTotal*: cuint + outgoingDataTotal*: cuint + lastSendTime*: cuint + lastReceiveTime*: cuint + nextTimeout*: cuint + earliestTimeout*: cuint + packetLossEpoch*: cuint + packetsSent*: cuint + packetsLost*: cuint + packetLoss*: cuint + packetLossVariance*: cuint + packetThrottle*: cuint + packetThrottleLimit*: cuint + packetThrottleCounter*: cuint + packetThrottleEpoch*: cuint + packetThrottleAcceleration*: cuint + packetThrottleDeceleration*: cuint + packetThrottleInterval*: cuint + lastRoundTripTime*: cuint + lowestRoundTripTime*: cuint + lastRoundTripTimeVariance*: cuint + highestRoundTripTimeVariance*: cuint + roundTripTime*: cuint + roundTripTimeVariance*: cuint + mtu*: cuint + windowSize*: cuint + reliableDataInTransit*: cuint + outgoingReliableSequenceNumber*: cushort + acknowledgements*: TENetList + sentReliableCommands*: TENetList + sentUnreliableCommands*: TENetList + outgoingReliableCommands*: TENetList + outgoingUnreliableCommands*: TENetList + dispatchedCommands*: TENetList + needsDispatch*: cint + incomingUnsequencedGroup*: cushort + outgoingUnsequencedGroup*: cushort + unsequencedWindow*: array[0..ENET_PEER_UNSEQUENCED_WINDOW_SIZE div 32 - 1, + cuint] + eventData*: cuint + + PCompressor* = ptr TCompressor + TCompressor*{.pure, final.} = object + context*: pointer + compress*: proc (context: pointer; inBuffers: ptr TEnetBuffer; + inBufferCount: csize; inLimit: csize; + outData: ptr cuchar; outLimit: csize): csize{.cdecl.} + decompress*: proc (context: pointer; inData: ptr cuchar; inLimit: csize; + outData: ptr cuchar; outLimit: csize): csize{.cdecl.} + destroy*: proc (context: pointer){.cdecl.} + + TChecksumCallback* = proc (buffers: ptr TEnetBuffer; bufferCount: csize): cuint{. + cdecl.} + + PHost* = ptr THost + THost*{.pure, final.} = object + socket*: TEnetSocket + address*: TAddress + incomingBandwidth*: cuint + outgoingBandwidth*: cuint + bandwidthThrottleEpoch*: cuint + mtu*: cuint + randomSeed*: cuint + recalculateBandwidthLimits*: cint + peers*: ptr TPeer + peerCount*: csize + channelLimit*: csize + serviceTime*: cuint + dispatchQueue*: TEnetList + continueSending*: cint + packetSize*: csize + headerFlags*: cushort + commands*: array[0..ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS - 1, + TEnetProtocol] + commandCount*: csize + buffers*: array[0..ENET_BUFFER_MAXIMUM - 1, TEnetBuffer] + bufferCount*: csize + checksum*: TChecksumCallback + compressor*: TCompressor + packetData*: array[0..ENET_PROTOCOL_MAXIMUM_MTU - 1, + array[0..2 - 1, cuchar]] + receivedAddress*: TAddress + receivedData*: ptr cuchar + receivedDataLength*: csize + totalSentData*: cuint + totalSentPackets*: cuint + totalReceivedData*: cuint + totalReceivedPackets*: cuint + + TEventType*{.size: sizeof(cint).} = enum + EvtNone = 0, EvtConnect = 1, + EvtDisconnect = 2, EvtReceive = 3 + PEvent* = ptr TEvent + TEvent*{.pure, final.} = object + kind*: TEventType + peer*: ptr TPeer + channelID*: cuchar + data*: cuint + packet*: ptr TPacket + + TENetCallbacks*{.pure, final.} = object + malloc*: proc (size: csize): pointer{.cdecl.} + free*: proc (memory: pointer){.cdecl.} + no_memory*: proc (){.cdecl.} + +{.push callConv:cdecl.} +proc enet_malloc*(a2: csize): pointer{. + importc: "enet_malloc", dynlib: Lib.} +proc enet_free*(a2: pointer){. + importc: "enet_free", dynlib: Lib.} + +proc enetInit*(): cint{. + importc: "enet_initialize", dynlib: Lib.} +proc enetInit*(version: TVersion; inits: ptr TENetCallbacks): cint{. + importc: "enet_initialize_with_callbacks", dynlib: Lib.} +proc enetDeinit*(){. + importc: "enet_deinitialize", dynlib: Lib.} +proc enet_time_get*(): cuint{. + importc: "enet_time_get", dynlib: Lib.} +proc enet_time_set*(a2: cuint){. + importc: "enet_time_set", dynlib: Lib.} + +#enet docs are pretty lacking, i'm not sure what the names of these arguments should be +proc createSocket*(kind: TSocketType): TEnetSocket{. + importc: "enet_socket_create", dynlib: Lib.} +proc bindTo*(socket: TEnetSocket; address: var TAddress): cint{. + importc: "enet_socket_bind", dynlib: Lib.} +proc bindTo*(socket: TEnetSocket; address: ptr TAddress): cint{. + importc: "enet_socket_bind", dynlib: Lib.} +proc listen*(socket: TEnetSocket; a3: cint): cint{. + importc: "enet_socket_listen", dynlib: Lib.} +proc accept*(socket: TEnetSocket; address: var TAddress): TEnetSocket{. + importc: "enet_socket_accept", dynlib: Lib.} +proc accept*(socket: TEnetSocket; address: ptr TAddress): TEnetSocket{. + importc: "enet_socket_accept", dynlib: Lib.} +proc connect*(socket: TEnetSocket; address: var TAddress): cint{. + importc: "enet_socket_connect", dynlib: Lib.} +proc connect*(socket: TEnetSocket; address: ptr TAddress): cint{. + importc: "enet_socket_connect", dynlib: Lib.} +proc send*(socket: TEnetSocket; address: var TAddress; buffer: ptr TEnetBuffer; size: csize): cint{. + importc: "enet_socket_send", dynlib: Lib.} +proc send*(socket: TEnetSocket; address: ptr TAddress; buffer: ptr TEnetBuffer; size: csize): cint{. + importc: "enet_socket_send", dynlib: Lib.} +proc receive*(socket: TEnetSocket; address: var TAddress; + buffer: ptr TEnetBuffer; size: csize): cint{. + importc: "enet_socket_receive", dynlib: Lib.} +proc receive*(socket: TEnetSocket; address: ptr TAddress; + buffer: ptr TEnetBuffer; size: csize): cint{. + importc: "enet_socket_receive", dynlib: Lib.} +proc wait*(socket: TEnetSocket; a3: ptr cuint; a4: cuint): cint{. + importc: "enet_socket_wait", dynlib: Lib.} +proc setOption*(socket: TEnetSocket; a3: TSocketOption; a4: cint): cint{. + importc: "enet_socket_set_option", dynlib: Lib.} +proc destroy*(socket: TEnetSocket){. + importc: "enet_socket_destroy", dynlib: Lib.} +proc select*(socket: TEnetSocket; a3: ptr TENetSocketSet; + a4: ptr TENetSocketSet; a5: cuint): cint{. + importc: "enet_socketset_select", dynlib: Lib.} + +proc setHost*(address: PAddress; hostName: cstring): cint{. + importc: "enet_address_set_host", dynlib: Lib.} +proc setHost*(address: var TAddress; hostName: cstring): cint{. + importc: "enet_address_set_host", dynlib: Lib.} +proc getHostIP*(address: var TAddress; hostName: cstring; nameLength: csize): cint{. + importc: "enet_address_get_host_ip", dynlib: Lib.} +proc getHost*(address: var TAddress; hostName: cstring; nameLength: csize): cint{. + importc: "enet_address_get_host", dynlib: Lib.} + +## Call the above two funcs but trim the result string +proc getHostIP*(address: var TAddress; hostName: var string; nameLength: csize): cint{.inline.} = + hostName.setLen nameLength + result = getHostIP(address, cstring(hostName), nameLength) + if result == 0: + hostName.setLen(len(cstring(hostName))) +proc getHost*(address: var TAddress; hostName: var string; nameLength: csize): cint{.inline.} = + hostName.setLen nameLength + result = getHost(address, cstring(hostName), nameLength) + if result == 0: + hostName.setLen(len(cstring(hostName))) + +proc createPacket*(data: pointer; len: csize; flag: TPacketFlag): PPacket{. + importc: "enet_packet_create", dynlib: Lib.} +proc destroy*(packet: PPacket){. + importc: "enet_packet_destroy", dynlib: Lib.} +proc resize*(packet: PPacket; dataLength: csize): cint{. + importc: "enet_packet_resize", dynlib: Lib.} + +proc crc32*(buffers: ptr TEnetBuffer; bufferCount: csize): cuint{. + importc: "enet_crc32", dynlib: Lib.} + +proc createHost*(address: ptr TAddress; maxConnections, maxChannels: csize; downSpeed, upSpeed: cuint): PHost{. + importc: "enet_host_create", dynlib: Lib.} +proc createHost*(address: var TAddress; maxConnections, maxChannels: csize; downSpeed, upSpeed: cuint): PHost{. + importc: "enet_host_create", dynlib: Lib.} +proc destroy*(host: PHost){. + importc: "enet_host_destroy", dynlib: Lib.} +proc connect*(host: PHost; address: ptr TAddress; channelCount: csize; data: cuint): PPeer{. + importc: "enet_host_connect", dynlib: Lib.} +proc connect*(host: PHost; address: var TAddress; channelCount: csize; data: cuint): PPeer{. + importc: "enet_host_connect", dynlib: Lib.} + +proc checkEvents*(host: PHost; event: var TEvent): cint{. + importc: "enet_host_check_events", dynlib: Lib.} +proc checkEvents*(host: PHost; event: ptr TEvent): cint{. + importc: "enet_host_check_events", dynlib: Lib.} +proc hostService*(host: PHost; event: var TEvent; timeout: cuint): cint{. + importc: "enet_host_service", dynlib: Lib.} +proc hostService*(host: PHost; event: ptr TEvent; timeout: cuint): cint{. + importc: "enet_host_service", dynlib: Lib.} +proc flush*(host: PHost){. + importc: "enet_host_flush", dynlib: Lib.} +proc broadcast*(host: PHost; channelID: cuchar; packet: PPacket){. + importc: "enet_host_broadcast", dynlib: Lib.} +proc compress*(host: PHost; compressor: PCompressor){. + importc: "enet_host_compress", dynlib: Lib.} +proc compressWithRangeCoder*(host: PHost): cint{. + importc: "enet_host_compress_with_range_coder", dynlib: Lib.} +proc channelLimit*(host: PHost; channelLimit: csize){. + importc: "enet_host_channel_limit", dynlib: Lib.} +proc bandwidthLimit*(host: PHost; incoming, outgoing: cuint){. + importc: "enet_host_bandwidth_limit", dynlib: Lib.} +proc bandwidthThrottle*(host: PHost){. + importc: "enet_host_bandwidth_throttle", dynlib: Lib.} + + +proc send*(peer: PPeer; channel: cuchar; packet: PPacket): cint{. + importc: "enet_peer_send", dynlib: Lib.} +proc receive*(peer: PPeer; channelID: ptr cuchar): PPacket{. + importc: "enet_peer_receive", dynlib: Lib.} +proc ping*(peer: PPeer){. + importc: "enet_peer_ping", dynlib: Lib.} +proc reset*(peer: PPeer){. + importc: "enet_peer_reset", dynlib: Lib.} +proc disconnect*(peer: PPeer; a3: cuint){. + importc: "enet_peer_disconnect", dynlib: Lib.} +proc disconnectNow*(peer: PPeer; a3: cuint){. + importc: "enet_peer_disconnect_now", dynlib: Lib.} +proc disconnectLater*(peer: PPeer; a3: cuint){. + importc: "enet_peer_disconnect_later", dynlib: Lib.} +proc throttleConfigure*(peer: PPeer; interval, acceleration, deceleration: cuint){. + importc: "enet_peer_throttle_configure", dynlib: Lib.} +proc throttle*(peer: PPeer; rtt: cuint): cint{. + importc: "enet_peer_throttle", dynlib: Lib.} +proc resetQueues*(peer: PPeer){. + importc: "enet_peer_reset_queues", dynlib: Lib.} +proc setupOutgoingCommand*(peer: PPeer; outgoingCommand: POutgoingCommand){. + importc: "enet_peer_setup_outgoing_command", dynlib: Lib.} + +proc queueOutgoingCommand*(peer: PPeer; command: ptr TEnetProtocol; + packet: PPacket; offset: cuint; length: cushort): POutgoingCommand{. + importc: "enet_peer_queue_outgoing_command", dynlib: Lib.} +proc queueIncomingCommand*(peer: PPeer; command: ptr TEnetProtocol; + packet: PPacket; fragmentCount: cuint): PIncomingCommand{. + importc: "enet_peer_queue_incoming_command", dynlib: Lib.} +proc queueAcknowledgement*(peer: PPeer; command: ptr TEnetProtocol; + sentTime: cushort): PAcknowledgement{. + importc: "enet_peer_queue_acknowledgement", dynlib: Lib.} +proc dispatchIncomingUnreliableCommands*(peer: PPeer; channel: PChannel){. + importc: "enet_peer_dispatch_incoming_unreliable_commands", dynlib: Lib.} +proc dispatchIncomingReliableCommands*(peer: PPeer; channel: PChannel){. + importc: "enet_peer_dispatch_incoming_reliable_commands", dynlib: Lib.} + +proc createRangeCoder*(): pointer{. + importc: "enet_range_coder_create", dynlib: Lib.} +proc rangeCoderDestroy*(context: pointer){. + importc: "enet_range_coder_destroy", dynlib: Lib.} +proc rangeCoderCompress*(context: pointer; inBuffers: PEnetBuffer; inLimit, + bufferCount: csize; outData: cstring; outLimit: csize): csize{. + importc: "enet_range_coder_compress", dynlib: Lib.} +proc rangeCoderDecompress*(context: pointer; inData: cstring; inLimit: csize; + outData: cstring; outLimit: csize): csize{. + importc: "enet_range_coder_decompress", dynlib: Lib.} +proc protocolCommandSize*(commandNumber: cuchar): csize{. + importc: "enet_protocol_command_size", dynlib: Lib.} + +{.pop.} + +from hashes import `!$`, `!&`, THash, hash +proc hash*(x: TAddress): THash {.nimcall, noSideEffect.} = + result = !$(hash(x.host.int32) !& hash(x.port.int16)) + diff --git a/tests/manyloc/keineschweine/dependencies/enet/testclient.nim b/tests/manyloc/keineschweine/dependencies/enet/testclient.nim new file mode 100644 index 000000000..2447a1fb5 --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/enet/testclient.nim @@ -0,0 +1,49 @@ +import enet, strutils + +if enetInit() != 0: + quit "Could not initialize ENet" +var + address: enet.TAddress + event: TEvent + peer: PPeer + client: PHost + +client = createHost(nil, 1, 2, 0, 0) +if client == nil: + quit "Could not create client!" + +if setHost(addr address, "localhost") != 0: + quit "Could not set host" +address.port = 8024 + +peer = client.connect(addr address, 2, 0) +if peer == nil: + quit "No available peers" + +block: + var bConnected = false + while not bConnected: + if client.hostService(event, 5000) > 0 and event.kind == EvtConnect: + echo "Connected" + bConnected = true + else: + echo "Connection failed" + quit 0 + +var runServer = true +while client.hostService(event, 1000) >= 0 and runServer: + case event.kind + of EvtReceive: + echo "Recvd ($1) $2 ".format( + event.packet.dataLength, + event.packet.data) + of EvtDisconnect: + echo "Disconnected" + event.peer.data = nil + runServer = false + of EvtNone: discard + else: + echo repr(event) + + +client.destroy() diff --git a/tests/manyloc/keineschweine/dependencies/enet/testserver.nim b/tests/manyloc/keineschweine/dependencies/enet/testserver.nim new file mode 100644 index 000000000..28a6bd1f7 --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/enet/testserver.nim @@ -0,0 +1,45 @@ +import enet, strutils +var + address: enet.TAddress + server: PHost + event: TEvent + +if enetInit() != 0: + quit "Could not initialize ENet" + +address.host = EnetHostAny +address.port = 8024 + +server = enet.createHost( + addr address, 32, 2, 0, 0) +if server == nil: + quit "Could not create the server!" + +while server.hostService(addr event, 2500) >= 0: + case event.kind + of EvtConnect: + echo "New client from $1:$2".format(event.peer.address.host, event.peer.address.port) + + var + msg = "hello" + resp = createPacket(cstring(msg), msg.len + 1, FlagReliable) + + if event.peer.send(0.cuchar, resp) < 0: + echo "FAILED" + else: + echo "Replied" + of EvtReceive: + echo "Recvd ($1) $2 ".format( + event.packet.dataLength, + event.packet.data) + + destroy(event.packet) + + of EvtDisconnect: + echo "Disconnected" + event.peer.data = nil + else: + discard + +server.destroy() +enetDeinit() \ No newline at end of file diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket.nim b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket.nim new file mode 100644 index 000000000..ae9dfb39f --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket.nim @@ -0,0 +1,295 @@ +import macros, macro_dsl, streams, streams_enh +from strutils import format + +template newLenName(): stmt {.immediate.} = + let lenName {.inject.} = ^("len"& $lenNames) + inc(lenNames) + +template defPacketImports*(): stmt {.immediate, dirty.} = + import macros, macro_dsl, streams, streams_enh + from strutils import format + +proc `$`*[T](x: seq[T]): string = + result = "[seq len=" + result.add($x.len) + result.add ':' + for i in 0.. `(a: string, b: string): PNimrodNode {.compileTime.} = + result = newNimNode(nnkIdentDefs).und(^a, ^b, newNimNode(nnkEmpty)) +proc `->`(a: string, b: PNimrodNode): PNimrodNode {.compileTime.} = + result = newNimNode(nnkIdentDefs).und(^a, b, newNimNode(nnkEmpty)) +proc `->`(a, b: PNimrodNode): PNimrodNode {.compileTime.} = + a[2] = b + result = a + +proc newProc*(name: string, params: varargs[PNimrodNode], resultType: PNimrodNode): PNimrodNode {.compileTime.} = + result = newNimNode(nnkProcDef).und( + ^name, + emptyNode(), + emptyNode(), + newNimNode(nnkFormalParams).und(resultType), + emptyNode(), + emptyNode(), + newNimNode(nnkStmtList)) + result[3].add(params) +macro forwardPacket*(typeName: expr, underlyingType: typedesc): stmt {.immediate.} = + result = newNimNode(nnkStmtList).und( + newProc( + "read"& $typeName.ident, + ["s" -> "PStream" -> newNimNode(nnkNilLit)], + typeName), + newProc( + "pack", + [ "p" -> newNimNode(nnkVarTy).und(typeName), + "s" -> "PStream" -> newNimNode(nnkNilLit)], + emptyNode())) + result[0][6].add(newNimNode(nnkDiscardStmt).und( + newCall( + "readData", ^"s", newNimNode(nnkAddr).und(^"result"), newCall("sizeof", ^"result") + ))) + result[1][6].add( + newCall( + "writeData", ^"s", newNimNode(nnkAddr).und(^"p"), newCall( + "sizeof", ^"p"))) + when defined(GenPacketShowOutput): + echo(repr(result)) + +template forwardPacketT*(typeName: expr): stmt {.dirty, immediate.} = + proc `read typeName`*(s: PStream): typeName = + discard readData(s, addr result, sizeof(result)) + proc `pack typeName`*(p: var typeName; s: PStream) = + writeData(s, addr p, sizeof(p)) + +when isMainModule: + type + SomeEnum = enum + A = 0'i8, + B, C + forwardPacket(SomeEnum, int8) + + + defPacket(Foo, tuple[x: array[0..4, int8]]) + var f = newFoo([4'i8, 3'i8, 2'i8, 1'i8, 0'i8]) + var s2 = newStringStream("") + f.pack(s2) + assert s2.data == "\4\3\2\1\0" + + var s = newStringStream() + s.flushImpl = proc(s: PStream) = + var z = PStringStream(s) + z.setPosition(0) + z.data.setLen(0) + + + s.setPosition(0) + s.data.setLen(0) + var o = B + o.pack(s) + o = A + o.pack(s) + o = C + o.pack(s) + assert s.data == "\1\0\2" + s.flush + + defPacket(Y, tuple[z: int8]) + proc `$`(z: Y): string = result = "Y("& $z.z &")" + defPacket(TestPkt, tuple[x: seq[Y]]) + var test = newTestPkt() + test.x.add([newY(5), newY(4), newY(3), newY(2), newY(1)]) + for itm in test.x: + echo(itm) + test.pack(s) + echo(repr(s.data)) \ No newline at end of file diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim new file mode 100644 index 000000000..44d00db53 --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/genpacket/genpacket_enet.nim @@ -0,0 +1,296 @@ +import macros, macro_dsl, estreams +from strutils import format + +template newLenName(): stmt {.immediate.} = + let lenName {.inject.} = ^("len"& $lenNames) + inc(lenNames) + +template defPacketImports*(): stmt {.immediate, dirty.} = + import macros, macro_dsl, estreams + from strutils import format + +proc `$`*[T](x: seq[T]): string = + result = "[seq len=" + result.add($x.len) + result.add ':' + for i in 0.. 5 + 10 + result = newNimNode(nnkInfix).und(b, a, c) +proc infix*(a: PNimrodNode; b: string; c: PNimrodNode): PNimrodNode {.compileTime.} = + ## Infix operation: infix(5, "+", 10) ## => + result = newNimNode(nnkInfix).und(newIdentNode(b), a, c) + +proc quoted2ident*(a: PNimrodNode): PNimrodNode {.compileTime.} = + if a.kind != nnkAccQuoted: + return a + var pname = "" + for piece in 0..a.len - 1: + pname.add($a[piece].ident) + result = ^pname + + +macro `?`(a: expr): expr = + ## Character literal ?A #=> 'A' + result = ($a[1].ident)[0].lit +## echo(?F,?a,?t,?t,?y) + +when isMainModule: + macro foo(x: stmt): stmt = + result = newNimNode(nnkStmtList) + result.add(newNimNode(nnkCall).und(!!"echo", "Hello thar".lit)) + result.add(newCall("echo", lit("3 * 45 = "), (3.lit.infix("*", 45.lit)))) + let stmtlist = x[1] + for i in countdown(len(stmtlist)-1, 0): + result.add(stmtlist[i]) + foo: + echo y, " * 2 = ", y * 2 + let y = 320 + diff --git a/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim b/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim new file mode 100644 index 000000000..a9759687f --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/genpacket/streams_enh.nim @@ -0,0 +1,47 @@ +import streams +from strutils import repeatChar + +proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString = + var lastChr = length + result = s.readStr(length) + while lastChr >= 0 and result[lastChr - 1] == padChar: dec(lastChr) + result.setLen(lastChr) + +proc writePaddedStr*(s: PStream, str: string, length: int, padChar = '\0') = + if str.len < length: + s.write(str) + s.write(repeatChar(length - str.len, padChar)) + elif str.len > length: + s.write(str.substr(0, length - 1)) + else: + s.write(str) + +proc readLEStr*(s: PStream): TaintedString = + var len = s.readInt16() + result = s.readStr(len) + +proc writeLEStr*(s: PStream, str: string) = + s.write(str.len.int16) + s.write(str) + +when isMainModule: + var testStream = newStringStream() + + testStream.writeLEStr("Hello") + doAssert testStream.data == "\5\0Hello" + + testStream.setPosition 0 + var res = testStream.readLEStr() + doAssert res == "Hello" + + testStream.setPosition 0 + testStream.writePaddedStr("Sup", 10) + echo(repr(testStream), testStream.data.len) + doAssert testStream.data == "Sup"&repeatChar(7, '\0') + + testStream.setPosition 0 + res = testStream.readPaddedStr(10) + doAssert res == "Sup" + + testStream.close() + diff --git a/tests/manyloc/keineschweine/dependencies/nake/nake.nim b/tests/manyloc/keineschweine/dependencies/nake/nake.nim new file mode 100644 index 000000000..eade28c70 --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/nake/nake.nim @@ -0,0 +1,83 @@ +discard """ +DO AS THOU WILST PUBLIC LICENSE + +Whoever should stumble upon this document is henceforth and forever +entitled to DO AS THOU WILST with aforementioned document and the +contents thereof. + +As said in the Olde Country, `Keepe it Gangster'.""" + +import strutils, parseopt, tables, os + +type + PTask* = ref object + desc*: string + action*: TTaskFunction + TTaskFunction* = proc() {.closure.} +var + tasks* = initTable[string, PTask](16) + +proc newTask*(desc: string; action: TTaskFunction): PTask +proc runTask*(name: string) {.inline.} +proc shell*(cmd: varargs[string, `$`]): int {.discardable.} +proc cd*(dir: string) {.inline.} + +template nakeImports*(): stmt {.immediate.} = + import tables, parseopt, strutils, os + +template task*(name: string; description: string; body: stmt): stmt {.dirty, immediate.} = + block: + var t = newTask(description, proc() {.closure.} = + body) + tasks[name] = t + +proc newTask*(desc: string; action: TTaskFunction): PTask = + new(result) + result.desc = desc + result.action = action +proc runTask*(name: string) = tasks[name].action() + +proc shell*(cmd: varargs[string, `$`]): int = + result = execShellCmd(cmd.join(" ")) +proc cd*(dir: string) = setCurrentDir(dir) +template withDir*(dir: string; body: stmt): stmt = + ## temporary cd + ## withDir "foo": + ## # inside foo + ## #back to last dir + var curDir = getCurrentDir() + cd(dir) + body + cd(curDir) + +when isMainModule: + if not existsFile("nakefile.nim"): + echo "No nakefile.nim found. Current working dir is ", getCurrentDir() + quit 1 + var args = "" + for i in 1..paramCount(): + args.add paramStr(i) + args.add " " + quit(shell("nimrod", "c", "-r", "nakefile.nim", args)) +else: + addQuitProc(proc() {.noconv.} = + var + task: string + printTaskList: bool + for kind, key, val in getOpt(): + case kind + of cmdLongOption, cmdShortOption: + case key.tolower + of "tasks", "t": + printTaskList = true + else: + echo "Unknown option: ", key, ": ", val + of cmdArgument: + task = key + else: nil + if printTaskList or task.isNil or not(tasks.hasKey(task)): + echo "Available tasks:" + for name, task in pairs(tasks): + echo name, " - ", task.desc + quit 0 + tasks[task].action()) diff --git a/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim b/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim new file mode 100644 index 000000000..24af63d10 --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/nake/nakefile.nim @@ -0,0 +1,23 @@ +import nake +nakeImports + +task "install", "compile and install nake binary": + if shell("nimrod", "c", "nake") == 0: + let path = getEnv("PATH").split(PathSep) + for index, dir in pairs(path): + echo " ", index, ". ", dir + echo "Where to install nake binary? (quit with ^C or quit or exit)" + let ans = stdin.readLine().toLower + var index = 0 + case ans + of "q", "quit", "x", "exit": + quit 0 + else: + index = parseInt(ans) + if index > path.len or index < 0: + echo "Invalid index." + quit 1 + moveFile "nake", path[index]/"nake" + echo "Great success!" + + diff --git a/tests/manyloc/keineschweine/dependencies/sfml/README.md b/tests/manyloc/keineschweine/dependencies/sfml/README.md new file mode 100644 index 000000000..bd9b3d0e7 --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/sfml/README.md @@ -0,0 +1,13 @@ +sfml-nimrod +=========== + +Nimrod binding of SFML 2.0 + +This is only tested for Linux at the moment + +### What is needed for Windows / OS X? + +* The library names need filling in +* TWindowHandle is handled differently on those platforms + +I believe that is it diff --git a/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim b/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim new file mode 100644 index 000000000..27163e271 --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim @@ -0,0 +1,1115 @@ +import + strutils, math +when defined(linux): + const + LibG = "libcsfml-graphics.so.2.0" + LibS = "libcsfml-system.so.2.0" + LibW = "libcsfml-window.so.2.0" +else: + {.error: "Platform unsupported".} +{.deadCodeElim: on.} +{.pragma: pf, pure, final.} +type + PClock* = ptr TClock + TClock* {.pf.} = object + TTime* {.pf.} = object + microseconds*: int64 + TVector2i* {.pf.} = object + x*, y*: cint + TVector2f* {.pf.} = object + x*, y*: cfloat + TVector3f* {.pf.} = object + x*, y*, z*: cfloat + + PInputStream* = ptr TInputStream + TInputStream* {.pf.} = object + read*: TInputStreamReadFunc + seek*: TInputStreamSeekFunc + tell*: TInputStreamTellFunc + getSize*: TInputStreamGetSizeFunc + userData*: pointer + TInputStreamReadFunc* = proc (data: pointer, size: int64, userData: pointer): int64{. + cdecl.} + TInputStreamSeekFunc* = proc (position: int16, userData: pointer): int64{. + cdecl.} + TInputStreamTellFunc* = proc (userData: pointer): int64 {.cdecl.} + TInputStreamGetSizeFunc* = proc (userData: pointer): int64 {.cdecl.} + PWindow* = ptr TWindow + TWindow* {.pf.} = object + PContextSettings* = ptr TContextSettings + TContextSettings*{.pf.} = object + depthBits: cint + stencilBits: cint + antialiasingLevel: cint + majorVersion: cint + minorVersion: cint + TVideoMode* {.pf.} = object + width*: cint + height*: cint + bitsPerPixel*: cint + TEventType*{.size: sizeof(cint).} = enum + EvtClosed, EvtResized, EvtLostFocus, EvtGainedFocus, + EvtTextEntered, EvtKeyPressed, EvtKeyReleased, EvtMouseWheelMoved, + EvtMouseButtonPressed, EvtMouseButtonReleased, EvtMouseMoved, + EvtMouseEntered, EvtMouseLeft, EvtJoystickButtonPressed, + EvtJoystickButtonReleased, EvtJoystickMoved, EvtJoystickConnected, + EvtJoystickDisconnected + TKeyEvent*{.pf.} = object + code*: TKeyCode + alt* : bool + control*: bool + shift* : bool + system* : bool + TJoystickConnectEvent*{.pf.} = object + joystickId*: cint + TJoystickButtonEvent*{.pf.} = object + joystickId*: cint + button*: cint + TJoystickMoveEvent*{.pf.} = object + joystickId*: cint + axis*: TJoystickAxis + position*: cfloat + TMouseWheelEvent*{.pf.} = object + delta*: cint + x*: cint + y*: cint + TMouseButtonEvent*{.pf.} = object + button*: TMouseButton + x*: cint + y*: cint + TMouseMoveEvent*{.pf.} = object + x*: cint + y*: cint + TTextEvent*{.pf.} = object + unicode*: cint + PEvent* = ptr TEvent + TEvent*{.pf.} = object + case kind*: TEventType + of EvtKeyPressed, EvtKeyReleased: + key*: TKeyEvent + of EvtMouseButtonPressed, EvtMouseButtonReleased: + mouseButton*: TMouseButtonEvent + of EvtTextEntered: + text*: TTextEvent + of EvtJoystickConnected, EvtJoystickDisconnected: + joystickConnect*: TJoystickConnectEvent + of EvtJoystickMoved: + joystickMove*: TJoystickMoveEvent + of EvtJoystickButtonPressed, EvtJoystickButtonReleased: + joystickButton*: TJoystickButtonEvent + of EvtResized: + size*: TSizeEvent + of EvtMouseMoved, EvtMouseEntered, EvtMouseLeft: + mouseMove*: TMouseMoveEvent + of EvtMouseWheelMoved: + mouseWheel*: TMouseWheelEvent + else: nil + TJoystickAxis*{.size: sizeof(cint).} = enum + JoystickX, JoystickY, JoystickZ, JoystickR, + JoystickU, JoystickV, JoystickPovX, JoystickPovY + TSizeEvent*{.pf.} = object + width*: cint + height*: cint + TMouseButton*{.size: sizeof(cint).} = enum + MouseLeft, MouseRight, MouseMiddle, + MouseXButton1, MouseXButton2, MouseButtonCount + TKeyCode*{.size: sizeof(cint).} = enum + KeyUnknown = - 1, KeyA, KeyB, KeyC, KeyD, KeyE, + KeyF, KeyG, KeyH, KeyI, KeyJ, KeyK, KeyL, KeyM, #/< The M key + KeyN, KeyO, KeyP, KeyQ, KeyR, KeyS, KeyT, KeyU, #/< The U key + KeyV, KeyW, KeyX, KeyY, KeyZ, KeyNum0, KeyNum1, #/< The 1 key + KeyNum2, KeyNum3, KeyNum4, KeyNum5, KeyNum6, #/< The 6 key + KeyNum7, KeyNum8, KeyNum9, KeyEscape, KeyLControl, #/< The left Control key + KeyLShift, KeyLAlt, KeyLSystem, KeyRControl, #/< The right Control key + KeyRShift, KeyRAlt, KeyRSystem, KeyMenu, #/< The Menu key + KeyLBracket, KeyRBracket, KeySemiColon, KeyComma, #/< The , key + KeyPeriod, KeyQuote, KeySlash, KeyBackSlash, #/< The \ key + KeyTilde, KeyEqual, KeyDash, KeySpace, KeyReturn, #/< The Return key + KeyBack, KeyTab, KeyPageUp, KeyPageDown, KeyEnd, #/< The End key + KeyHome, KeyInsert, KeyDelete, KeyAdd, KeySubtract, #/< - + KeyMultiply, KeyDivide, KeyLeft, KeyRight, KeyUp, #/< Up arrow + KeyDown, KeyNumpad0, KeyNumpad1, KeyNumpad2, #/< The numpad 2 key + KeyNumpad3, #/< The numpad 3 key + KeyNumpad4, #/< The numpad 4 key + KeyNumpad5, #/< The numpad 5 key + KeyNumpad6, #/< The numpad 6 key + KeyNumpad7, #/< The numpad 7 key + KeyNumpad8, #/< The numpad 8 key + KeyNumpad9, #/< The numpad 9 key + KeyF1, #/< The F1 key + KeyF2, #/< The F2 key + KeyF3, #/< The F3 key + KeyF4, #/< The F4 key + KeyF5, #/< The F5 key + KeyF6, #/< The F6 key + KeyF7, #/< The F7 key + KeyF8, #/< The F8 key + KeyF9, #/< The F8 key + KeyF10, #/< The F10 key + KeyF11, #/< The F11 key + KeyF12, #/< The F12 key + KeyF13, #/< The F13 key + KeyF14, #/< The F14 key + KeyF15, #/< The F15 key + KeyPause, #/< The Pause key + KeyCount #/< Keep last -- the total number of keyboard keys +when defined(linux): #or defined(bsd) ?? + type TWindowHandle* = clong +#elif defined(mac): +# type TWindowHandle* = pointer ##typedef void* sfWindowHandle; <- whatever the hell that is +#elif defined(windows): +# type TWindowHandle* = HWND__ ? windows is crazy. ##struct HWND__; typedef struct HWND__* sfWindowHandle; +const + sfNone* = 0 + sfTitlebar* = 1 shl 0 + sfResize* = 1 shl 1 + sfClose* = 1 shl 2 + sfFullscreen* = 1 shl 3 + sfDefaultStyle* = sfTitlebar or sfResize or sfClose +type + PRenderWindow* = ptr TRenderWindow + TRenderWindow* {.pf.} = object + + PFont* = ptr TFont + TFont* {.pf.} = object + PImage* = ptr TImage + TImage* {.pf.} = object + PShader* = ptr TShader + TShader* {.pf.} = object + PSprite* = ptr TSprite + TSprite* {.pf.} = object + PText* = ptr TText + TText* {.pf.} = object + PTexture* = ptr TTexture + TTexture* {.pf.} = object + PVertexArray* = ptr TVertexArray + TVertexArray* {.pf.} = object + PView* = ptr TView + TView* {.pf.} = object + PRenderTexture* = ptr TRenderTexture + TRenderTexture* {.pf.} = object + + PShape* = ptr TShape + TShape* {.pf.} = object + PCircleShape* = ptr TCircleShape + TCircleShape* {.pf.} = object + PRectangleShape* = ptr TRectangleShape + TRectangleShape* {.pf.} = object + PConvexShape* = ptr TConvexShape + TConvexShape* {.pf.} = object + + TTextStyle*{.size: sizeof(cint).} = enum + TextRegular = 0, TextBold = 1 shl 0, TextItalic = 1 shl 1, + TextUnderlined = 1 shl 2 + + TBlendMode*{.size: sizeof(cint).} = enum + BlendAlpha, BlendAdd, BlendMultiply, BlendNone + PRenderStates* = ptr TRenderStates + TRenderStates* {.pf.} = object + blendMode*: TBlendMode + transform*: TTransform + texture*: PTexture + shader*: PShader + + PTransform* = ptr TTransform + TTransform* {.pf.} = object + matrix*: array[0..8, cfloat] + TColor* {.pf.} = object + r*: Uint8 + g*: Uint8 + b*: Uint8 + a*: Uint8 + PFloatRect* = ptr TFloatRect + TFloatRect*{.pf.} = object + left*: cfloat + top*: cfloat + width*: cfloat + height*: cfloat + PIntRect* = ptr TIntRect + TIntRect*{.pf.} = object + left*: cint + top*: cint + width*: cint + height*: cint + TGlyph* {.pf.} = object + advance*: cint + bounds*: TIntRect + textureRect*: TIntRect + PVertex* = ptr TVertex + TVertex* {.pf.} = object + position*: TVector2f + color*: TColor + texCoords*: TVector2f + TPrimitiveType*{.size: sizeof(cint).} = enum + Points, #/< List of individual points + Lines, #/< List of individual lines + LinesStrip, #/< List of connected lines, a point uses the previous point to form a line + Triangles, #/< List of individual triangles + TrianglesStrip, #/< List of connected triangles, a point uses the two previous points to form a triangle + TrianglesFan, #/< List of connected triangles, a point uses the common center and the previous point to form a triangle + Quads + + +proc newWindow*(mode: TVideoMode, title: cstring, style: uint32, settings: PContextSettings = nil): PWindow {. + cdecl, importc: "sfWindow_create", dynlib: LibW.} + +proc close*(window: PWindow) {. + cdecl, importc: "sfWindow_close", dynlib: LibW.} +proc isOpen*(window: PWindow): bool {.cdecl, importc: "sfWindow_isOpen", dynlib: LibW.} + +proc pollEvent*(window: PWindow, event: PEvent): bool {. + cdecl, importc: "sfWindow_pollEvent", dynlib: LibW.} +proc waitEvent*(window: PWindow, event: PEvent): bool {. + cdecl, importc: "sfWindow_waitEvent", dynlib: LibW.} + +proc getDesktopMode*(): TVideoMode {. + cdecl, importc: "sfVideoMode_getDesktopMode", dynlib: LibW.} +proc isKeyPressed*(key: TKeyCode): bool {. + cdecl, importc: "sfKeyboard_isKeyPressed", dynlib: LibW.} + +proc mouseIsButtonPressed*(button: TMouseButton): bool {. + cdecl, importc: "sfMouse_isButtonPressed", dynlib: LibW.} +proc mouseGetPosition*(relativeTo: PWindow): TVector2i {. + cdecl, importc: "sfMouse_getPosition", dynlib: LibW.} +proc mouseSetPosition*(position: TVector2i, relativeTo: PWindow) {. + cdecl, importc: "sfMouse_setPosition", dynlib: LibW.} + +proc joystickIsConnected*(joystick: cint): bool {. + cdecl, importc: "sfJoystick_isConnected", dynlib: LibW.} +proc joystickGetButtonCount*(joystick: cint): cint {. + cdecl, importc: "sfJoystick_getButtonCount", dynlib: LibW.} +proc joystickHasAxis*(joystick: cint, axis: TJoystickAxis): bool {. + cdecl, importc: "sfJoystick_hasAxis", dynlib: LibW.} +proc joystickIsButtonPressed*(joystick: cint, button: cint): bool {. + cdecl, importc: "sfJoystick_isButtonPressed", dynlib: LibW.} +proc joystickGetAxisPosition*(joystick: cint, axis: TJoystickAxis): float {. + cdecl, importc: "sfJoystick_getAxisPosition", dynlib: LibW.} +proc joystickUpdate*(): void {. + cdecl, importc: "sfJoystick_update", dynlib: LibW.} + + +proc newRenderWindow*(handle: TWindowHandle, settings: PContextSettings = nil): PRenderWindow{. + cdecl, importc: "sfRenderWindow_createFromHandle", dynlib: LibG.} +proc newRenderWindow*(mode: TVideoMode, title: cstring, style: int32, settings: PContextSettings = nil): PRenderWindow {. + cdecl, importc: "sfRenderWindow_create", dynlib: LibG.} + +proc destroy*(window: PRenderWindow) {. + cdecl, importc: "sfRenderWindow_destroy", dynlib: LibG.} +proc close*(window: PRenderWindow) {. + cdecl, importc: "sfRenderWindow_close", dynlib: LibG.} +proc isOpen*(window: PRenderWindow): bool {. + cdecl, importc: "sfRenderWindow_isOpen", dynlib: LibG.} + +#void sfRenderWindow_setIcon(sfRenderWindow* renderWindow, unsigned int width, unsigned int height, const sfUint8* pixels); +#proc setIcon*(window: PRenderWindow, width, height: cint, pixels: seq[uint8]) {. +# cdecl, importc: "sfRenderWindow_setIcon", dynlib: LibG.} + +proc getSettings*(window: PRenderWindow): TContextSettings {. + cdecl, importc: "sfRenderWindow_getSettings", dynlib: LibG.} + +proc pollEvent*(window: PRenderWindow, event: PEvent): bool {. + cdecl, importc: "sfRenderWindow_pollEvent", dynlib: LibG.} +proc pollEvent*(window: PRenderWindow; event: var TEvent): bool {. + cdecl, importc: "sfRenderWindow_pollEvent", dynlib: LibG.} +proc waitEvent*(window: PRenderWindow, event: PEvent): bool {. + cdecl, importc: "sfRenderWindow_waitEvent", dynlib: LibG.} +proc waitEvent*(window: PRenderWindow, event: var TEvent): bool {. + cdecl, importc: "sfRenderWindow_waitEvent", dynlib: LibG.} +proc getPosition*(window: PRenderWindow): TVector2i {. + cdecl, importc: "sfRenderWindow_getPosition", dynlib: LibG.} +proc setPosition*(window: PRenderWindow, position: TVector2i) {. + cdecl, importc: "sfRenderWindow_setPosition", dynlib: LibG.} +proc getSize*(window: PRenderWindow): TVector2i {. + cdecl, importc: "sfRenderWindow_getSize", dynlib: LibG.} +proc setSize*(window: PRenderWindow, size: TVector2i): void {. + cdecl, importc: "sfRenderWindow_setSize", dynlib: LibG.} +proc setTitle*(window: PRenderWindow, title: cstring): void {. + cdecl, importc: "sfRenderWindow_setTitle", dynlib: LibG.} + +proc setVisible*(window: PRenderWindow, visible: bool) {. + cdecl, importc: "sfRenderWindow_setVisible", dynlib: LibG.} +proc setMouseCursorVisible*(window: PRenderWindow, show: bool) {. + cdecl, importc: "sfRenderWindow_setMouseCursorVisible", dynlib: LibG.} +proc setVerticalSyncEnabled*(window: PRenderWindow, enabled: bool) {. + cdecl, importc: "sfRenderWindow_setVerticalSyncEnabled", dynlib: LibG.} +proc setKeyRepeatEnabled*(window: PRenderWindow, enabled: bool) {. + cdecl, importc: "sfRenderWindow_setKeyRepeatEnabled", dynlib: LibG.} +proc setActive*(window: PRenderWindow, active: bool): bool {. + cdecl, importc: "sfRenderWindow_setActive", dynlib: LibG.} +proc display*(window: PRenderWindow) {. + cdecl, importc: "sfRenderWindow_display", dynlib: LibG.} +proc setFramerateLimit*(window: PRenderWindow, limit: uint) {. + cdecl, importc: "sfRenderWindow_setFramerateLimit", dynlib: LibG.} +proc setJoystickThreshold*(window: PRenderWindow, threshold: float) {. + cdecl, importc: "sfRenderWindow_setJoystickThreshold", dynlib: LibG.} +proc getSystemHandle*(window: PRenderWindow): TWindowHandle {. + cdecl, importc: "sfRenderWindow_getSystemHandle", dynlib: LibG.} + +proc clear*(window: PRenderWindow, color: TColor) {. + cdecl, importc: "sfRenderWindow_clear", dynlib: LibG.} + +proc setView*(window: PRenderWindow, view: PView) {. + cdecl, importc: "sfRenderWindow_setView", dynlib: LibG.} +proc getView*(window: PRenderWindow): PView {. + cdecl, importc: "sfRenderWindow_getView", dynlib: LibG.} +proc getDefaultView*(window: PRenderWindow): PView {. + cdecl, importc: "sfRenderWindow_getDefaultView", dynlib: LibG.} +proc getViewport*(window: PRenderWindow, view: PView): TIntRect {. + cdecl, importc: "sfRenderWindow_getViewport", dynlib: LibG.} + +proc convertCoords*(window: PRenderWindow, point: TVector2i, targetView: PView): TVector2f {. + cdecl, importc: "sfRenderWindow_convertCoords", dynlib: LibG.} + +proc draw*(window: PRenderWindow, sprite: PSprite, states: PRenderStates = nil) {. + cdecl, importc: "sfRenderWindow_drawSprite", dynlib: LibG.} +proc draw*(window: PRenderWindow, text: PText, states: PRenderStates = nil) {. + cdecl, importc: "sfRenderWindow_drawText", dynlib: LibG.} +proc draw*(window: PRenderWindow, shape: PShape, states: PRenderStates = nil) {. + cdecl, importc: "sfRenderWindow_drawShape", dynlib: LibG.} +proc draw*(window: PRenderWindow, shape: PCircleShape, states: PRenderStates = nil) {. + cdecl, importc: "sfRenderWindow_drawCircleShape", dynlib: LibG.} +proc draw*(window: PRenderWindow, shape: PRectangleShape, states: PRenderStates = nil) {. + cdecl, importc: "sfRenderWindow_drawRectangleShape", dynlib: LibG.} + +proc draw*(window: PRenderWindow, shape: PConvexShape, states: PRenderStates = nil) {. + cdecl, importc: "sfRenderWindow_drawConvexShape", dynlib: LibG.} +proc draw*(window: PRenderWindow, shape: PVertexArray, states: PRenderStates = nil) {. + cdecl, importc: "sfRenderWindow_drawVertexArray", dynlib: LibG.} +proc draw*(window: PRenderWindow, vertices: PVertex, vertexCount: cint, + vertexType: TPrimitiveType, states: PRenderStates = nil) {. + cdecl, importc: "sfRenderWindow_drawPrimitives", dynlib: LibG.} + +proc pushGlStates*(window: PRenderWindow) {. + cdecl, importc: "sfRenderWindow_pushGLStates", dynlib: LibG.} +proc popGlStates*(window: PRenderWindow) {. + cdecl, importc: "sfRenderWindow_popGLStates", dynlib: LibG.} +proc resetGlStates*(window: PRenderWindow) {. + cdecl, importc: "sfRenderWindow_resetGLStates", dynlib: LibG.} +proc capture*(window: PRenderWindow): PImage {. + cdecl, importc: "sfRenderWindow_capture", dynlib: LibG.} + +#Construct a new render texture +proc newRenderTexture*(width, height: cint; depthBuffer: Bool): PRenderTexture {. + cdecl, importc: "sfRenderTexture_create", dynlib: LibG.} +#Destroy an existing render texture +proc destroy*(renderTexture: PRenderTexture){. + cdecl, importc: "sfRenderTexture_destroy", dynlib: LibG.} +#Get the size of the rendering region of a render texture +proc getSize*(renderTexture: PRenderTexture): TVector2i {. + cdecl, importc: "sfRenderTexture_getSize", dynlib: LibG.} +#Activate or deactivate a render texture as the current target for rendering +proc setActive*(renderTexture: PRenderTexture; active: bool): bool{. + cdecl, importc: "sfRenderTexture_setActive", dynlib: LibG.} +#Update the contents of the target texture +proc display*(renderTexture: PRenderTexture){. + cdecl, importc: "sfRenderTexture_display", dynlib: LibG.} +#Clear the rendertexture with the given color +proc clear*(renderTexture: PRenderTexture; color: TColor){. + cdecl, importc: "sfRenderTexture_clear", dynlib: LibG.} +#Change the current active view of a render texture +proc setView*(renderTexture: PRenderTexture; view: PView){. + cdecl, importc: "sfRenderTexture_setView", dynlib: LibG.} +#Get the current active view of a render texture +proc getView*(renderTexture: PRenderTexture): PView{. + cdecl, importc: "sfRenderTexture_getView", dynlib: LibG.} +#Get the default view of a render texture +proc getDefaultView*(renderTexture: PRenderTexture): PView{. + cdecl, importc: "sfRenderTexture_getDefaultView", dynlib: LibG.} +#Get the viewport of a view applied to this target +proc getViewport*(renderTexture: PRenderTexture; view: PView): TIntRect{. + cdecl, importc: "sfRenderTexture_getViewport", dynlib: LibG.} +#Convert a point in texture coordinates into view coordinates +proc convertCoords*(renderTexture: PRenderTexture; point: TVector2i; targetView: PView): TVector2f{. + cdecl, importc: "sfRenderTexture_convertCoords", dynlib: LibG.} +#Draw a drawable object to the render-target +proc draw*(renderTexture: PRenderTexture; sprite: PSprite; states: PRenderStates){. + cdecl, importc: "sfRenderTexture_drawSprite", dynlib: LibG.} +proc draw*(renderTexture: PRenderTexture; text: PText; states: PRenderStates){. + cdecl, importc: "sfRenderTexture_drawText", dynlib: LibG.} +proc draw*(renderTexture: PRenderTexture; shape: PShape; states: PRenderStates){. + cdecl, importc: "sfRenderTexture_drawShape", dynlib: LibG.} +proc draw*(renderTexture: PRenderTexture; shape: PCircleShape; + states: PRenderStates){. + cdecl, importc: "sfRenderTexture_drawCircleShape", dynlib: LibG.} +proc draw*(renderTexture: PRenderTexture; shape: PConvexShape; + states: PRenderStates){. + cdecl, importc: "sfRenderTexture_drawConvexShape", dynlib: LibG.} +proc draw*(renderTexture: PRenderTexture; shape: PRectangleShape; + states: PRenderStates){. + cdecl, importc: "sfRenderTexture_drawRectangleShape", dynlib: LibG.} +proc draw*(renderTexture: PRenderTexture; va: PVertexArray; + states: PRenderStates){. + cdecl, importc: "sfRenderTexture_drawVertexArray", dynlib: LibG.} +#Draw primitives defined by an array of vertices to a render texture +proc draw*(renderTexture: PRenderTexture; vertices: PVertex; vertexCount: cint; + primitiveType: TPrimitiveType; states: PRenderStates){. + cdecl, importc: "sfRenderTexture_drawPrimitives", dynlib: LibG.} +#Save the current OpenGL render states and matrices +#/ +#/ This function can be used when you mix SFML drawing +#/ and direct OpenGL rendering. Combined with popGLStates, +#/ it ensures that: +#/ * SFML's internal states are not messed up by your OpenGL code +#/ * your OpenGL states are not modified by a call to a SFML function +#/ +#/ Note that this function is quite expensive: it saves all the +#/ possible OpenGL states and matrices, even the ones you +#/ don't care about. Therefore it should be used wisely. +#/ It is provided for convenience, but the best results will +#/ be achieved if you handle OpenGL states yourself (because +#/ you know which states have really changed, and need to be +#/ saved and restored). Take a look at the resetGLStates +#/ function if you do so. +proc pushGLStates*(renderTexture: PRenderTexture){. + cdecl, importc: "sfRenderTexture_pushGLStates", dynlib: LibG.} +#Restore the previously saved OpenGL render states and matrices +#/ +#/ See the description of pushGLStates to get a detailed +#/ description of these functions. +proc popGLStates*(renderTexture: PRenderTexture){. + cdecl, importc: "sfRenderTexture_popGLStates", dynlib: LibG.} +#Reset the internal OpenGL states so that the target is ready for drawing +#/ +#/ This function can be used when you mix SFML drawing +#/ and direct OpenGL rendering, if you choose not to use +#/ pushGLStates/popGLStates. It makes sure that all OpenGL +#/ states needed by SFML are set, so that subsequent sfRenderTexture_draw*() +#/ calls will work as expected. +proc resetGLStates*(renderTexture: PRenderTexture){. + cdecl, importc: "sfRenderTexture_resetGLStates", dynlib: LibG.} +#Get the target texture of a render texture +proc getTexture*(renderTexture: PRenderTexture): PTexture{. + cdecl, importc: "sfRenderTexture_getTexture", dynlib: LibG.} +#Enable or disable the smooth filter on a render texture +proc setSmooth*(renderTexture: PRenderTexture; smooth: bool){. + cdecl, importc: "sfRenderTexture_setSmooth", dynlib: LibG.} +#Tell whether the smooth filter is enabled or not for a render texture +proc isSmooth*(renderTexture: PRenderTexture): bool {. + cdecl, importc: "sfRenderTexture_isSmooth", dynlib: LibG.} + +proc intRect*(left, top, width, height: cint): TIntRect = + result.left = left + result.top = top + result.width = width + result.height = height +proc floatRect*(left, top, width, height: cfloat): TFloatRect = + result.left = left + result.top = top + result.width = width + result.height = height +proc contains*(rect: PFloatRect, x, y: cfloat): bool {. + cdecl, importc: "sfFloatRect_contains", dynlib: LibG.} +proc contains*(rect: PIntRect, x: cint, y: cint): bool{.cdecl, + importc: "sfIntRect_contains", dynlib: LibG.} +proc intersects*(rect1, rect2, intersection: PFloatRect): bool {. + cdecl, importc: "sfFloatRect_intersects", dynlib: LibG.} +proc intersects*(rect1, rect2, intersection: PIntRect): bool {. + cdecl, importc: "sfIntRect_intersects", dynlib: LibG.} + +proc newFont*(filename: cstring): PFont {. + cdecl, importc: "sfFont_createFromFile", dynlib: LibG.} +proc newFont*(data: pointer, sizeInBytes: cint): PFont {. + cdecl, importc: "sfFont_createFromMemory", dynlib: LibG.} +proc newFont*(stream: PInputStream): PFont {. + cdecl, importc: "sfFont_createFromStream", dynlib: LibG.} +proc copy*(font: PFont): PFont {. + cdecl, importc: "sfFont_copy", dynlib: LibG.} +proc destroy*(font: PFont) {. + cdecl, importc: "sfFont_destroy", dynlib: LibG.} +proc getGlyph*(font: PFont, codePoint: Uint32, characterSize: cint, bold: bool): TGlyph{. + cdecl, importc: "sfFont_getGlyph", dynlib: LibG.} +proc getKerning*(font: PFont, first: Uint32, second: Uint32, characterSize: cint): cint {. + cdecl, importc: "sfFont_getKerning", dynlib: LibG.} +proc getLineSpacing*(font: PFont, characterSize: cint): cint {. + cdecl, importc: "sfFont_getLineSpacing", dynlib: LibG.} +proc getTexture*(font: PFont, characterSize: cint): PTexture {. + cdecl, importc: "sfFont_getTexture", dynlib: LibG.} +#getDefaultFont() has been removed from CSFML +proc getDefaultFont*(): PFont {. + error, cdecl, importc: "sfFont_getDefaultFont", dynlib: LibG.} + +proc newCircleShape*(): PCircleShape {. + cdecl, importc: "sfCircleShape_create", dynlib: LibG.} +proc copy*(shape: PCircleShape): PCircleShape {. + cdecl, importc: "sfCircleShape_copy", dynlib: LibG.} +proc destroy*(shape: PCircleShape) {. + cdecl, importc: "sfCircleShape_destroy", dynlib: LibG.} +proc setPosition*(shape: PCircleShape, position: TVector2f) {. + cdecl, importc: "sfCircleShape_setPosition", dynlib: LibG.} +proc setRotation*(shape: PCircleShape, angle: cfloat) {. + cdecl, importc: "sfCircleShape_setRotation", dynlib: LibG.} +proc setScale*(shape: PCircleShape, scale: TVector2f) {. + cdecl, importc: "sfCircleShape_setScale", dynlib: LibG.} +proc setOrigin*(shape: PCircleShape, origin: TVector2f) {. + cdecl, importc: "sfCircleShape_setOrigin", dynlib: LibG.} +proc getPosition*(shape: PCircleShape): TVector2f {. + cdecl, importc: "sfCircleShape_getPosition", dynlib: LibG.} +proc getRotation*(shape: PCircleShape): cfloat {. + cdecl, importc: "sfCircleShape_getRotation", dynlib: LibG.} +proc getScale*(shape: PCircleShape): TVector2f {. + cdecl, importc: "sfCircleShape_getScale", dynlib: LibG.} +proc getOrigin*(shape: PCircleShape): TVector2f {. + cdecl, importc: "sfCircleShape_getOrigin", dynlib: LibG.} +proc move*(shape: PCircleShape, offset: TVector2f) {. + cdecl, importc: "sfCircleShape_move", dynlib: LibG.} +proc rotate*(shape: PCircleShape, angle: cfloat){. + cdecl, importc: "sfCircleShape_rotate", dynlib: LibG.} +proc scale*(shape: PCircleShape, factors: TVector2f) {. + cdecl, importc: "sfCircleShape_scale", dynlib: LibG.} +proc getTransform*(shape: PCircleShape): TTransform {. + cdecl, importc: "sfCircleShape_getTransform", dynlib: LibG.} +proc getInverseTransform*(shape: PCircleShape): TTransform {. + cdecl, importc: "sfCircleShape_getInverseTransform", dynlib: LibG.} +proc setTexture*(shape: PCircleShape, texture: PTexture, resetRect: bool) {. + cdecl, importc: "sfCircleShape_setTexture", dynlib: LibG.} +proc setTextureRect*(shape: PCircleShape, rect: TIntRect) {. + cdecl, importc: "sfCircleShape_setTextureRect", dynlib: LibG.} +proc setFillColor*(shape: PCircleShape, color: TColor) {. + cdecl, importc: "sfCircleShape_setFillColor", dynlib: LibG.} +proc setOutlineColor*(shape: PCircleShape, color: TColor) {. + cdecl, importc: "sfCircleShape_setOutlineColor", dynlib: LibG.} +proc setOutlineThickness*(shape: PCircleShape, thickness: cfloat) {. + cdecl, importc: "sfCircleShape_setOutlineThickness", dynlib: LibG.} +proc getTexture*(shape: PCircleShape): PTexture {. + cdecl, importc: "sfCircleShape_getTexture", dynlib: LibG.} +proc getTextureRect*(shape: PCircleShape): TIntRect {. + cdecl, importc: "sfCircleShape_getTextureRect", dynlib: LibG.} +proc getFillColor*(shape: PCircleShape): TColor {. + cdecl, importc: "sfCircleShape_getFillColor", dynlib: LibG.} +proc getOutlineColor*(shape: PCircleShape): TColor {. + cdecl, importc: "sfCircleShape_getOutlineColor", dynlib: LibG.} +proc getOutlineThickness*(shape: PCircleShape): cfloat {. + cdecl, importc: "sfCircleShape_getOutlineThickness", dynlib: LibG.} +proc getPointCount*(shape: PCircleShape): cint {. + cdecl, importc: "sfCircleShape_getPointCount", dynlib: LibG.} +proc getPoint*(shape: PCircleShape, index: cint): TVector2f {. + cdecl, importc: "sfCircleShape_getPoint", dynlib: LibG.} +proc setRadius*(shape: PCircleShape, radius: cfloat) {. + cdecl, importc: "sfCircleShape_setRadius", dynlib: LibG.} +proc getRadius*(shape: PCircleShape): cfloat {. + cdecl, importc: "sfCircleShape_getRadius", dynlib: LibG.} +proc setPointCount*(shape: PCircleShape, count: cint) {. + cdecl, importc: "sfCircleShape_setPointCount", dynlib: LibG.} +proc getLocalBounds*(shape: PCircleShape): TFloatRect {. + cdecl, importc: "sfCircleShape_getLocalBounds", dynlib: LibG.} +proc getGlobalBounds*(shape: PCircleShape): TFloatRect {. + cdecl, importc: "sfCircleShape_getGlobalBounds", dynlib: LibG.} + +proc newRectangleShape*(): PRectangleShape {. + cdecl, importc: "sfRectangleShape_create", dynlib: LibG.} +proc copy*(shape: PRectangleShape): PRectangleShape {. + cdecl, importc: "sfRectangleShape_copy", dynlib: LibG.} +proc destroy*(shape: PRectangleShape){. + cdecl, importc: "sfRectangleShape_destroy", dynlib: LibG.} +proc setPosition*(shape: PRectangleShape, position: TVector2f) {. + cdecl, importc: "sfRectangleShape_setPosition", dynlib: LibG.} +proc setRotation*(shape: PRectangleShape, angle: cfloat) {. + cdecl, importc: "sfRectangleShape_setRotation", dynlib: LibG.} +proc setScale*(shape: PRectangleShape, scale: TVector2f) {. + cdecl, importc: "sfRectangleShape_setScale", dynlib: LibG.} +proc setOrigin*(shape: PRectangleShape, origin: TVector2f) {. + cdecl, importc: "sfRectangleShape_setOrigin", dynlib: LibG.} +proc getPosition*(shape: PRectangleShape): TVector2f {. + cdecl, importc: "sfRectangleShape_getPosition", dynlib: LibG.} +proc getRotation*(shape: PRectangleShape): cfloat {. + cdecl, importc: "sfRectangleShape_getRotation", dynlib: LibG.} +proc getScale*(shape: PRectangleShape): TVector2f {. + cdecl, importc: "sfRectangleShape_getScale", dynlib: LibG.} +proc getOrigin*(shape: PRectangleShape): TVector2f {. + cdecl, importc: "sfRectangleShape_getOrigin", dynlib: LibG.} +proc move*(shape: PRectangleShape, offset: TVector2f) {. + cdecl, importc: "sfRectangleShape_move", dynlib: LibG.} +proc rotate*(shape: PRectangleShape, angle: cfloat) {. + cdecl, importc: "sfRectangleShape_rotate", dynlib: LibG.} +proc scale*(shape: PRectangleShape, factors: TVector2f) {. + cdecl, importc: "sfRectangleShape_scale", dynlib: LibG.} +proc getTransform*(shape: PRectangleShape): TTransform {. + cdecl, importc: "sfRectangleShape_getTransform", dynlib: LibG.} +proc getInverseTransform*(shape: PRectangleShape): TTransform {. + cdecl, importc: "sfRectangleShape_getInverseTransform", dynlib: LibG.} +proc setTexture*(shape: PRectangleShape, texture: PTexture, resetRect: bool) {. + cdecl, importc: "sfRectangleShape_setTexture", dynlib: LibG.} +proc setTextureRect*(shape: PRectangleShape, rect: TIntRect) {. + cdecl, importc: "sfRectangleShape_setTextureRect", dynlib: LibG.} +proc setFillColor*(shape: PRectangleShape, color: TColor) {. + cdecl, importc: "sfRectangleShape_setFillColor", dynlib: LibG.} +proc setOutlineColor*(shape: PRectangleShape, color: TColor) {. + cdecl, importc: "sfRectangleShape_setOutlineColor", dynlib: LibG.} +proc setOutlineThickness*(shape: PRectangleShape, thickness: cfloat) {. + cdecl, importc: "sfRectangleShape_setOutlineThickness", dynlib: LibG.} +proc getTexture*(shape: PRectangleShape): PTexture {. + cdecl, importc: "sfRectangleShape_getTexture", dynlib: LibG.} +proc getTextureRect*(shape: PRectangleShape): TIntRect {. + cdecl, importc: "sfRectangleShape_getTextureRect", dynlib: LibG.} +proc getFillColor*(shape: PRectangleShape): TColor {. + cdecl, importc: "sfRectangleShape_getFillColor", dynlib: LibG.} +proc getOutlineColor*(shape: PRectangleShape): TColor {. + cdecl, importc: "sfRectangleShape_getOutlineColor", dynlib: LibG.} +proc getOutlineThickness*(shape: PRectangleShape): cfloat {. + cdecl, importc: "sfRectangleShape_getOutlineThickness", dynlib: LibG.} +proc getPointCount*(shape: PRectangleShape): cint {. + cdecl, importc: "sfRectangleShape_getPointCount", dynlib: LibG.} +proc getPoint*(shape: PRectangleShape, index: cint): TVector2f {. + cdecl, importc: "sfRectangleShape_getPoint", dynlib: LibG.} +proc setSize*(shape: PRectangleShape, size: TVector2f) {. + cdecl, importc: "sfRectangleShape_setSize", dynlib: LibG.} +proc getSize*(shape: PRectangleShape): TVector2f {. + cdecl, importc: "sfRectangleShape_getSize", dynlib: LibG.} +proc getLocalBounds*(shape: PRectangleShape): TFloatRect {. + cdecl, importc: "sfRectangleShape_getLocalBounds", dynlib: LibG.} +proc getGlobalBounds*(shape: PRectangleShape): TFloatRect {. + cdecl, importc: "sfRectangleShape_getGlobalBounds", dynlib: LibG.} + + +proc newView*(): PView {. + cdecl, importc: "sfView_create", dynlib: LibG.} +proc viewFromRect*(rectangle: TFloatRect): PView{. + cdecl, importc: "sfView_createFromRect", dynlib: LibG.} +proc copy*(view: PView): PView {. + cdecl, importc: "sfView_copy", dynlib: LibG.} +proc destroy*(view: PView) {. + cdecl, importc: "sfView_destroy", dynlib: LibG.} +proc setCenter*(view: PView, center: TVector2f) {. + cdecl, importc: "sfView_setCenter", dynlib: LibG.} +proc setSize*(view: PView, size: TVector2f) {. + cdecl, importc: "sfView_setSize", dynlib: LibG.} +proc setRotation*(view: PView, angle: cfloat) {. + cdecl, importc: "sfView_setRotation", dynlib: LibG.} +proc setViewport*(view: PView, viewport: TFloatRect) {. + cdecl, importc: "sfView_setViewport", dynlib: LibG.} +proc reset*(view: PView, rectangle: TFloatRect) {. + cdecl, importc: "sfView_reset", dynlib: LibG.} +proc getCenter*(view: PView): TVector2f {. + cdecl, importc: "sfView_getCenter", dynlib: LibG.} +proc getSize*(view: PView): TVector2f {. + cdecl, importc: "sfView_getSize", dynlib: LibG.} +proc getRotation*(view: PView): cfloat {. + cdecl, importc: "sfView_getRotation", dynlib: LibG.} +proc getViewport*(view: PView): TFloatRect {. + cdecl, importc: "sfView_getViewport", dynlib: LibG.} +proc move*(view: PView, offset: TVector2f) {. + cdecl, importc: "sfView_move", dynlib: LibG.} +proc rotate*(view: PView, angle: cfloat) {. + cdecl, importc: "sfView_rotate", dynlib: LibG.} +proc zoom*(view: PView, factor: cfloat) {. + cdecl, importc: "sfView_zoom", dynlib: LibG.} + +proc newImage*(width, height: cint): PImage {. + cdecl, importc: "sfImage_create", dynlib: LibG.} +proc newImage*(width, height: cint, color: TColor): PImage {. + cdecl, importc: "sfImage_createFromColor", dynlib: LibG.} +proc newImage*(width, height: cint, pixels: pointer): PImage {. ##same deal as setIcon() + cdecl, importc: "sfImage_createFromPixels", dynlib: LibG.} +proc newImage*(filename: cstring): PImage {. + cdecl, importc: "sfImage_createFromFile", dynlib: LibG.} +proc newImage*(data: pointer, size: cint): PImage {. + cdecl, importc: "sfImage_createFromMemory", dynlib: LibG.} +proc newImage*(stream: PInputStream): PImage {. + cdecl, importc: "sfImage_createFromStream", dynlib: LibG.} +proc copy*(image: PImage): PImage {. + cdecl, importc: "sfImage_copy", dynlib: LibG.} +proc destroy*(image: PImage) {. + cdecl, importc: "sfImage_destroy", dynlib: LibG.} +proc save*(image: PImage, filename: cstring): bool {. + cdecl, importc: "sfImage_saveToFile", dynlib: LibG.} +proc getSize*(image: PImage): TVector2i {. + cdecl, importc: "sfImage_getSize", dynlib: LibG.} +proc createMask*(image: PImage, color: TColor, alpha: cchar) {. + cdecl, importc: "sfImage_createMaskFromColor", dynlib: LibG.} +proc copy*(destination, source: PImage, destX, destY: cint; + sourceRect: TIntRect, applyAlpha: bool) {. + cdecl, importc: "sfImage_copyImage", dynlib: LibG.} +proc setPixel*(image: PImage, x, y: cint, color: TColor) {. + cdecl, importc: "sfImage_setPixel", dynlib: LibG.} +proc getPixel*(image: PImage, x, y: cint): TColor {. + cdecl, importc: "sfImage_getPixel", dynlib: LibG.} +proc getPixels*(image: PImage): pointer {. + cdecl, importc: "sfImage_getPixelsPtr", dynlib: LibG.} +proc flipHorizontally*(image: PImage) {. + cdecl, importc: "sfImage_flipHorizontally", dynlib: LibG.} +proc flipVertically*(image: PImage) {. + cdecl, importc: "sfImage_flipVertically", dynlib: LibG.} + +proc newSprite*(): PSprite {. + cdecl, importc: "sfSprite_create", dynlib: LibG.} +proc copy*(sprite: PSprite): PSprite {. + cdecl, importc: "sfSprite_copy", dynlib: LibG.} +proc destroy*(sprite: PSprite) {. + cdecl, importc: "sfSprite_destroy", dynlib: LibG.} +proc setPosition*(sprite: PSprite, position: TVector2f) {. + cdecl, importc: "sfSprite_setPosition", dynlib: LibG.} +proc setRotation*(sprite: PSprite, angle: cfloat) {. + cdecl, importc: "sfSprite_setRotation", dynlib: LibG.} +proc setScale*(sprite: PSprite, scale: TVector2f) {. + cdecl, importc: "sfSprite_setScale", dynlib: LibG.} +proc setOrigin*(sprite: PSprite, origin: TVector2f) {. + cdecl, importc: "sfSprite_setOrigin", dynlib: LibG.} +proc getPosition*(sprite: PSprite): TVector2f {. + cdecl, importc: "sfSprite_getPosition", dynlib: LibG.} +proc getRotation*(sprite: PSprite): cfloat {. + cdecl, importc: "sfSprite_getRotation", dynlib: LibG.} +proc getScale*(sprite: PSprite): TVector2f {. + cdecl, importc: "sfSprite_getScale", dynlib: LibG.} +proc getOrigin*(sprite: PSprite): TVector2f {. + cdecl, importc: "sfSprite_getOrigin", dynlib: LibG.} +proc move*(sprite: PSprite, offset: TVector2f) {. + cdecl, importc: "sfSprite_move", dynlib: LibG.} +proc rotate*(sprite: PSprite, angle: cfloat) {. + cdecl, importc: "sfSprite_rotate", dynlib: LibG.} +proc scale*(sprite: PSprite, factor: TVector2f) {. + cdecl, importc: "sfSprite_scale", dynlib: LibG.} +proc getTransform*(sprite: PSprite): TTransform {. + cdecl, importc: "sfSprite_getTransform", dynlib: LibG.} +proc getInverseTransform*(sprite: PSprite): TTransform {. + cdecl, importc: "sfSprite_getInverseTransform", dynlib: LibG.} +proc setTexture*(sprite: PSprite, texture: PTexture, resetRect: bool) {. + cdecl, importc: "sfSprite_setTexture", dynlib: LibG.} +proc setTextureRect*(sprite: PSprite, rectangle: TIntRect) {. + cdecl, importc: "sfSprite_setTextureRect", dynlib: LibG.} +proc setColor*(sprite: PSprite, color: TColor) {. + cdecl, importc: "sfSprite_setColor", dynlib: LibG.} +proc getTexture*(sprite: PSprite): TTexture {. + cdecl, importc: "sfSprite_getTexture", dynlib: LibG.} +proc getTextureRect*(sprite: PSprite): TIntRect {. + cdecl, importc: "sfSprite_getTextureRect", dynlib: LibG.} +proc getColor*(sprite: PSprite): TColor {. + cdecl, importc: "sfSprite_getColor", dynlib: LibG.} +proc getLocalBounds*(sprite: PSprite): TFloatRect {. + cdecl, importc: "sfSprite_getLocalBounds", dynlib: LibG.} +proc getGlobalBounds*(sprite: PSprite): TFloatRect {. + cdecl, importc: "sfSprite_getGlobalBounds", dynlib: LibG.} + +proc newTexture*(width, height: cint): PTexture {. + cdecl, importc: "sfTexture_create", dynlib: LibG.} +proc newTexture*(filename: cstring): PTexture {. + cdecl, importc: "sfTexture_createFromFile", dynlib: LibG.} +proc newTexture*(data: pointer, size: cint, area: PIntRect): PTexture {. + cdecl, importc: "sfTexture_createFromMemory", dynlib: LibG.} +proc newTexture*(stream: PInputStream, area: PIntRect): PTexture {. + cdecl, importc: "sfTexture_createFromStream", dynlib: LibG.} +proc newTexture*(image: PImage, area: PIntRect = nil): PTexture {. + cdecl, importc: "sfTexture_createFromImage", dynlib: LibG.} +proc copy*(texture: PTexture): PTexture {. + cdecl, importc: "sfTexture_copy", dynlib: LibG.} +proc destroy*(texture: PTexture) {. + cdecl, importc: "sfTexture_destroy", dynlib: LibG.} +proc getSize*(texture: PTexture): TVector2i {. + cdecl, importc: "sfTexture_getSize", dynlib: LibG.} +proc copyToImage*(texture: PTexture): PImage {. + cdecl, importc: "sfTexture_copyToImage", dynlib: LibG.} +proc updateFromPixels*(texture: PTexture, pixels: pointer, width, height, x, y: cint) {. + cdecl, importc: "sfTexture_updateFromPixels", dynlib: LibG.} +proc updateFromImage*(texture: PTexture, image: PImage, x, y: cint) {. + cdecl, importc: "sfTexture_updateFromImage", dynlib: LibG.} +proc updateFromWindow*(texture: PTexture, window: PWindow, x, y: cint) {. + cdecl, importc: "sfTexture_updateFromWindow", dynlib: LibG.} +proc updateFromWindow*(texture: PTexture, window: PRenderWindow, x, y: cint) {. + cdecl, importc: "sfTexture_updateFromRenderWindow", dynlib: LibG.} +proc bindGL*(texture: PTexture) {. + cdecl, importc: "sfTexture_bind", dynlib: LibG.} +proc setSmooth*(texture: PTexture, smooth: bool) {. + cdecl, importc: "sfTexture_setSmooth", dynlib: LibG.} +proc isSmooth*(texture: PTexture): bool {. + cdecl, importc: "sfTexture_isSmooth", dynlib: LibG.} +proc setRepeated*(texture: PTexture, repeated: bool) {. + cdecl, importc: "sfTexture_setRepeated", dynlib: LibG.} +proc isRepeated*(texture: PTexture): bool {. + cdecl, importc: "sfTexture_isRepeated", dynlib: LibG.} +proc textureMaxSize*(): cint {. + cdecl, importc: "sfTexture_getMaximumSize", dynlib: LibG.} + +proc newVertexArray*(): PVertexArray {. + cdecl, importc: "sfVertexArray_create", dynlib: LibG.} +proc copy*(vertexArray: PVertexArray): PVertexArray {. + cdecl, importc: "sfVertexArray_copy", dynlib: LibG.} +proc destroy*(va: PVertexArray) {. + cdecl, importc: "sfVertexArray_destroy", dynlib: LibG.} +proc getVertexCount*(va: PVertexArray): cint {. + cdecl, importc: "sfVertexArray_getVertexCount", dynlib: LibG.} +proc getVertex*(va: PVertexArray, index: cint): PVertex {. + cdecl, importc: "sfVertexArray_getVertex", dynlib: LibG.} +proc clear*(va: PVertexArray) {. + cdecl, importc: "sfVertexArray_clear", dynlib: LibG.} +proc resize*(va: PVertexArray, size: cint) {. + cdecl, importc: "sfVertexArray_resize", dynlib: LibG.} +proc append*(va: PVertexArray, vertex: TVertex) {. + cdecl, importc: "sfVertexArray_append", dynlib: LibG.} +proc setPrimitiveType*(va: PVertexArray, primitiveType: TPrimitiveType) {. + cdecl, importc: "sfVertexArray_setPrimitiveType", dynlib: LibG.} +proc getPrimitiveType*(va: PVertexArray): TPrimitiveType {. + cdecl, importc: "sfVertexArray_getPrimitiveType", dynlib: LibG.} +proc getBounds*(va: PVertexArray): TFloatRect {. + cdecl, importc: "sfVertexArray_getBounds", dynlib: LibG.} + + +proc newText*(): PText {. + cdecl, importc: "sfText_create", dynlib: LibG.} +proc copy*(text: PText): PText {. + cdecl, importc: "sfText_copy", dynlib: LibG.} +proc destroy*(text: PText) {. + cdecl, importc: "sfText_destroy", dynlib: LibG.} +proc setPosition*(text: PText, position: TVector2f) {. + cdecl, importc: "sfText_setPosition", dynlib: LibG.} +proc setRotation*(text: PText, angle: cfloat) {. + cdecl, importc: "sfText_setRotation", dynlib: LibG.} +proc setScale*(text: PText, scale: TVector2f) {. + cdecl, importc: "sfText_setScale", dynlib: LibG.} +proc setOrigin*(text: PText, origin: TVector2f) {. + cdecl, importc: "sfText_setOrigin", dynlib: LibG.} +proc getPosition*(text: PText): TVector2f {. + cdecl, importc: "sfText_getPosition", dynlib: LibG.} +proc getRotation*(text: PText): cfloat {. + cdecl, importc: "sfText_getRotation", dynlib: LibG.} +proc getScale*(text: PText): TVector2f {. + cdecl, importc: "sfText_getScale", dynlib: LibG.} +proc getOrigin*(text: PText): TVector2f {. + cdecl, importc: "sfText_getOrigin", dynlib: LibG.} +proc move*(text: PText, offset: TVector2f) {. + cdecl, importc: "sfText_move", dynlib: LibG.} +proc rotate*(text: PText, angle: cfloat) {. + cdecl, importc: "sfText_rotate", dynlib: LibG.} +proc scale*(text: PText, factors: TVector2f) {. + cdecl, importc: "sfText_scale", dynlib: LibG.} +proc getTransform*(text: PText): TTransform {. + cdecl, importc: "sfText_getTransform", dynlib: LibG.} +proc getInverseTransform*(text: PText): TTransform {. + cdecl, importc: "sfText_getInverseTransform", dynlib: LibG.} +proc setString*(text: PText, string: cstring) {. + cdecl, importc: "sfText_setString", dynlib: LibG.} +proc setUnicodeString*(text: PText, string: ptr Uint32) {. + cdecl, importc: "sfText_setUnicodeString", dynlib: LibG.} +proc setFont*(text: PText, font: PFont) {. + cdecl, importc: "sfText_setFont", dynlib: LibG.} +proc setCharacterSize*(text: PText, size: cint) {. + cdecl, importc: "sfText_setCharacterSize", dynlib: LibG.} +proc setStyle*(text: PText, style: TTextStyle) {. + cdecl, importc: "sfText_setStyle", dynlib: LibG.} +proc setColor*(text: PText, color: TColor) {. + cdecl, importc: "sfText_setColor", dynlib: LibG.} +proc getString*(text: PText): cstring {. + cdecl, importc: "sfText_getString", dynlib: LibG.} +proc getUnicodeString*(text: PText): ptr Uint32 {.cdecl, + importc: "sfText_getUnicodeString", dynlib: LibG.} +proc getFont*(text: PText): PFont {. + cdecl, importc: "sfText_getFont", dynlib: LibG.} +proc getCharacterSize*(text: PText): cint {. + cdecl, importc: "sfText_getCharacterSize", dynlib: LibG.} +proc getStyle*(text: PText): Uint32 {. + cdecl, importc: "sfText_getStyle", dynlib: LibG.} +proc getColor*(text: PText): TColor {. + cdecl, importc: "sfText_getColor", dynlib: LibG.} +proc findCharacterPos*(text: PText, index: cint): TVector2f {. + cdecl, importc: "sfText_findCharacterPos", dynlib: LibG.} +proc getLocalBounds*(text: PText): TFloatRect {. + cdecl, importc: "sfText_getLocalBounds", dynlib: LibG.} +proc getGlobalBounds*(text: PText): TFloatRect {. + cdecl, importc: "sfText_getGlobalBounds", dynlib: LibG.} + +proc transformFromMatrix*(a00, a01, a02, a10, a11, a12, a20, a21, a22: cfloat): TTransform {. + cdecl, importc: "sfTransform_fromMatrix", dynlib: LibG.} +proc getMatrix*(transform: PTransform, matrix: ptr cfloat) {. + cdecl, importc: "sfTransform_getMatrix", dynlib: LibG.} +proc getInverse*(transform: PTransform): TTransform {. + cdecl, importc: "sfTransform_getInverse", dynlib: LibG.} +proc transformPoint*(transform: PTransform, point: TVector2f): TVector2f {. + cdecl, importc: "sfTransform_transformPoint", dynlib: LibG.} +proc transformRect*(transform: PTransform, rectangle: TFloatRect): TFloatRect {. + cdecl, importc: "sfTransform_transformRect", dynlib: LibG.} +proc combine*(transform: PTransform, other: PTransform) {. + cdecl, importc: "sfTransform_combine", dynlib: LibG.} +proc translate*(transform: PTransform, x, y: cfloat) {. + cdecl, importc: "sfTransform_translate", dynlib: LibG.} +proc rotate*(transform: PTransform, angle: cfloat) {. + cdecl, importc: "sfTransform_rotate", dynlib: LibG.} +proc rotateWithCenter*(transform: PTransform, angle, centerX, centerY: cfloat){. + cdecl, importc: "sfTransform_rotateWithCenter", dynlib: LibG.} +proc scale*(transform: PTransform, scaleX, scaleY: cfloat) {. + cdecl, importc: "sfTransform_scale", dynlib: LibG.} +proc scaleWithCenter*(transform: PTransform, scaleX, scaleY, centerX, centerY: cfloat) {. + cdecl, importc: "sfTransform_scaleWithCenter", dynlib: LibG.} +let IdentityMatrix*: TTransform = transformFromMatrix(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0) + + +proc newShader*(VSfilename: cstring, fragmentShaderFilename: cstring): PShader {. + cdecl, importc: "sfShader_createFromFile", dynlib: LibG.} +proc newShaderFromStr*(vertexShader: cstring, fragmentShader: cstring): PShader {. + cdecl, importc: "sfShader_createFromMemory", dynlib: LibG.} +proc newShader*(vertexShaderStream: PInputStream, fragmentShaderStream: PInputStream): PShader {. + cdecl, importc: "sfShader_createFromStream", dynlib: LibG.} +proc destroy*(shader: PShader) {. + cdecl, importc: "sfShader_destroy", dynlib: LibG.} +proc setFloatParameter*(shader: PShader, name: cstring, x: cfloat) {. + cdecl, importc: "sfShader_setFloatParameter", dynlib: LibG.} +proc setFloat2Parameter*(shader: PShader, name: cstring, x, y: cfloat) {. + cdecl, importc: "sfShader_setFloat2Parameter", dynlib: LibG.} +proc setFloat3Parameter*(shader: PShader, name: cstring, x, y, z: cfloat) {. + cdecl, importc: "sfShader_setFloat3Parameter", dynlib: LibG.} +proc setFloat4Parameter*(shader: PShader, name: cstring, x, y, z, w: cfloat) {. + cdecl, importc: "sfShader_setFloat4Parameter", dynlib: LibG.} +proc setVector2Parameter*(shader: PShader, name: cstring, vector: TVector2f) {. + cdecl, importc: "sfShader_setVector2Parameter", dynlib: LibG.} +proc setVector3Parameter*(shader: PShader, name: cstring, vector: TVector3f) {. + cdecl, importc: "sfShader_setVector3Parameter", dynlib: LibG.} +proc setColorParameter*(shader: PShader, name: cstring, color: TColor) {. + cdecl, importc: "sfShader_setColorParameter", dynlib: LibG.} +proc setTransformParameter*(shader: PShader, name: cstring, transform: TTransform) {. + cdecl, importc: "sfShader_setTransformParameter", dynlib: LibG.} +proc setTextureParameter*(shader: PShader, name: cstring, texture: PTexture) {. + cdecl, importc: "sfShader_setTextureParameter", dynlib: LibG.} +proc setCurrentTextureParameter*(shader: PShader, name: cstring) {. + cdecl, importc: "sfShader_setCurrentTextureParameter", dynlib: LibG.} +proc bindGL*(shader: PShader) {. + cdecl, importc: "sfShader_bind", dynlib: LibG.} +proc unbindGL*(shader: PShader) {. + cdecl, importc: "sfShader_unbind", dynlib: LibG.} +proc shaderIsAvailable*(): bool {. + cdecl, importc: "sfShader_isAvailable", dynlib: LibG.} + +proc color*(red, green, blue: cchar): TColor {. + cdecl, importc: "sfColor_fromRGB", dynlib: LibG.} +proc color*(red, green, blue: int): TColor {.inline.} = + return color(red.cchar, green.cchar, blue.cchar) +proc color*(red, green, blue, alpha: cchar): TColor {. + cdecl, importc: "sfColor_fromRGBA", dynlib: LibG.} +proc color*(red, green, blue, alpha: int): TColor {.inline.} = + return color(red.cchar, green.cchar, blue.cchar, alpha.cchar) +proc `+`*(color1, color2: TColor): TColor {. + cdecl, importc: "sfColor_add", dynlib: LibG.} +proc `*`*(color1, color2: TColor): TColor {. + cdecl, importc: "sfColor_modulate", dynlib: LibG.} +proc newColor*(r,g,b: int): TColor {.inline.} = + return color(r,g,b) +proc newColor*(r,g,b,a: int): TColor {.inline.} = + return color(r,g,b,a) + +proc newClock*(): PClock {. + cdecl, importc: "sfClock_create", dynlib: LibS.} +proc copy*(clocK: PClock): PClock {. + cdecl, importc: "sfClock_copy", dynlib: LibS.} +proc destroy*(clock: PClock): PClock {. + cdecl, importc: "sfClock_destroy", dynlib: LibS.} +proc getElapsedTime*(clock: PClock): TTime {. + cdecl, importc: "sfClock_getElapsedTime", dynlib: LibS.} +proc restart*(clock: PClock): TTime {. + cdecl, importc: "sfClock_restart", dynlib: LibS, discardable.} +proc asSeconds*(time: TTime): cfloat {. + cdecl, importc: "sfTime_asSeconds", dynlib: LibS.} +proc asMilliseconds*(time: TTime): int32 {. + cdecl, importc: "sfTime_asMilliseconds", dynlib: LibS.} +proc asMicroseconds*(time: TTime): int64 {. + cdecl, importc: "sfTime_asMicroseconds", dynlib: LibS.} +proc seconds*(seconds: cfloat): TTime {. + cdecl, importc: "sfSeconds", dynlib: LibS.} +proc milliseconds*(ms: int32): TTime {. + cdecl, importc: "sfMilliseconds", dynlib: LibS.} +proc microseconds*(us: int64): TTime {. + cdecl, importc: "sfMicroseconds", dynlib: LibS.} + +proc newContextSettings*(depthBits: cint = 0, + stencilBits: cint = 0, + antialiasingLevel: cint = 0, + majorVersion: cint = 0, + minorVersion: cint = 0): TContextSettings = + result.depthBits = depthBits + result.stencilBits = stencilBits + result.antialiasingLevel = antialiasingLevel + result.majorVersion = majorVersion + result.minorVersion = minorVersion + +proc newCircleShape*(radius: cfloat; pointCount: cint = 30): PCircleShape = + result = newCircleShape() + result.setRadius radius + result.setPointCount pointCount +proc newText*(str: string, font: PFont, size: int): PText = + result = newText() + result.setString(str) + result.setFont(font) + result.setCharacterSize(size.cint) +proc newVertexArray*(primitiveType: TPrimitiveType, vertexCount: cint = 0): PVertexArray = + result = newVertexArray() + result.setPrimitiveType(primitiveType) + if vertexCount != 0: + result.resize(vertexCount) +proc videoMode*(width, height, bpp: cint): TVideoMode = + result.width = width + result.height = height + result.bitsPerPixel = bpp + +proc `[]`*(a: PVertexArray, index: int): PVertex = + return getVertex(a, index.cint) + +proc `$` *(a: TContextSettings): string = + return "" % [ + $a.stencilBits, $a.antialiasingLevel, $a.majorVersion, $a.minorVersion, $a.depthBits] +proc `$` *(a: TVideoMode): string = + return "" % [$a.width, $a.height, $a.bitsPerPixel] +proc `$` *(a: TFloatRect): string = + return "" % [$a.left, $a.top, $a.width, $a.height] +proc `$` *(a: PView): string = + return $a.getViewport() +proc `$` *(a: TVector2f): string = + return "" % [$a.x, $a.y] + +proc vec2i*(x, y: int): TVector2i = + result.x = x.cint + result.y = y.cint +proc vec2f*(x, y: float): TVector2f = + result.x = x.cfloat + result.y = y.cfloat + +proc `+`*(a, b: TVector2f): TVector2f {.inline.} = + result.x = a.x + b.x + result.y = a.y + b.y +proc `-`*(a: TVector2f): TVector2f {.inline.} = + result.x = -a.x + result.y = -a.y +proc `-`*(a, b: TVector2f): TVector2f {.inline.}= + result.x = a.x - b.x + result.y = a.y - b.y +proc `*`*(a: TVector2f, b: cfloat): TVector2f {.inline.} = + result.x = a.x * b + result.y = a.y * b +proc `*`*(a, b: TVector2f): TVector2f {.inline.} = + result.x = a.x * b.x + result.y = a.y * b.y +proc `/`*(a: TVector2f, b: cfloat): TVector2f {.inline.} = + result.x = a.x / b + result.y = a.y / b +proc `+=` *(a: var TVector2f, b: TVector2f) {.inline, noSideEffect.} = + a = a + b +proc `-=` *(a: var TVector2f, b: TVector2f) {.inline, noSideEffect.} = + a = a - b +proc `*=` *(a: var TVector2f, b: float) {.inline, noSideEffect.} = + a = a * b +proc `*=` *(a: var TVector2f, b: TVector2f) {.inline, noSideEffect.} = + a = a * b +proc `/=` *(a: var TVector2f, b: float) {.inline, noSideEffect.} = + a = a / b +proc `<` *(a, b: TVector2f): bool {.inline, noSideEffect.} = + return a.x < b.x or (a.x == b.x and a.y < b.y) +proc `<=` *(a, b: TVector2f): bool {.inline, noSideEffect.} = + return a.x <= b.x and a.y <= b.y +proc `==` *(a, b: TVector2f): bool {.inline, noSideEffect.} = + return a.x == b.x and a.y == b.y +proc length*(a: TVector2f): float {.inline.} = + return sqrt(pow(a.x, 2.0) + pow(a.y, 2.0)) +proc lengthSq*(a: TVector2f): float {.inline.} = + return pow(a.x, 2.0) + pow(a.y, 2.0) +proc distanceSq*(a, b: TVector2f): float {.inline.} = + return pow(a.x - b.x, 2.0) + pow(a.y - b.y, 2.0) +proc distance*(a, b: TVector2f): float {.inline.} = + return sqrt(pow(a.x - b.x, 2.0) + pow(a.y - b.y, 2.0)) +proc permul*(a, b: TVector2f): TVector2f = + result.x = a.x * b.x + result.y = a.y * b.y +proc rotate*(a: TVector2f, phi: float): TVector2f = + var c = cos(phi) + var s = sin(phi) + result.x = a.x * c - a.y * s + result.y = a.x * s + a.y * c +proc perpendicular(a: TVector2f): TVector2f = + result.x = -a.x + result.y = a.y +proc cross(a, b: TVector2f): float = + return a.x * b.y - a.y * b.x + diff --git a/tests/manyloc/keineschweine/dependencies/sfml/sfml_audio.nim b/tests/manyloc/keineschweine/dependencies/sfml/sfml_audio.nim new file mode 100644 index 000000000..3cfd33c02 --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/sfml/sfml_audio.nim @@ -0,0 +1,899 @@ +import + sfml +const + Lib = "libcsfml-audio.so.2.0" +type + PMusic* = ptr TMusic + TMusic* {.pure, final.} = object + PSound* = ptr TSound + TSound* {.pure, final.} = object + PSoundBuffer* = ptr TSoundBuffer + TSoundBuffer* {.pure, final.} = object + PSoundBufferRecorder* = ptr TSoundBufferRecorder + TSoundBufferRecorder* {.pure, final.} = object + PSoundRecorder* = ptr TSoundRecorder + TSoundRecorder* {.pure, final.} = object + PSoundStream* = ptr TSoundStream + TSoundStream* {.pure, final.} = object + TSoundStatus* {.size: sizeof(cint).} = enum + Stopped, Paused, Playing + +proc newMusic*(filename: cstring): PMusic {. + cdecl, importc: "sfMusic_createFromFile", dynlib: Lib.} +proc newMusic*(data: pointer, size: cint): PMusic {. + cdecl, importc: "sfMusic_createFromMemory", dynlib: Lib.} +proc newMusic*(stream: PInputStream): PMusic {. + cdecl, importc: "sfMusic_createFromStream", dynlib: Lib.} +proc destroy*(music: PMusic) {. + cdecl, importc: "sfMusic_destroy", dynlib: Lib.} +proc setLoop*(music: PMusic, loop: bool) {. + cdecl, importc: "sfMusic_setLoop", dynlib: Lib.} +proc getLoop*(music: PMusic): bool {. + cdecl, importc: "sfMusic_getLoop", dynlib: Lib.} +proc getDuration*(music: PMusic): TTime {. + cdecl, importc: "sfMusic_getDuration", dynlib: Lib.} +proc play*(music: PMusic) {. + cdecl, importc: "sfMusic_play", dynlib: Lib.} +proc pause*(music: PMusic) {. + cdecl, importc: "sfMusic_pause", dynlib: Lib.} +proc stop*(music: PMusic) {. + cdecl, importc: "sfMusic_stop", dynlib: Lib.} +proc getChannelCount*(music: PMusic): cint {. + cdecl, importc: "sfMusic_getChannelCount", dynlib: Lib.} +proc getSampleRate*(music: PMusic): cint {. + cdecl, importc: "sfMusic_getSampleRate", dynlib: Lib.} +proc getStatus*(music: PMusic): TSoundStatus {. + cdecl, importc: "sfMusic_getStatus", dynlib: Lib.} +proc getPlayingOffset*(music: PMusic): TTime {. + cdecl, importc: "sfMusic_getPlayingOffset", dynlib: Lib.} +proc setPitch*(music: PMusic, pitch: cfloat) {. + cdecl, importc: "sfMusic_setPitch", dynlib: Lib.} +proc setVolume*(music: PMusic, volume: float) {. + cdecl, importc: "sfMusic_setVolume", dynlib: Lib.} +proc setPosition*(music: PMusic, position: TVector3f) {. + cdecl, importc: "sfMusic_setPosition", dynlib: Lib.} +proc setRelativeToListener*(music: PMusic, relative: bool) {. + cdecl, importc: "sfMusic_setRelativeToListener", dynlib: Lib.} +proc setMinDistance*(music: PMusic, distance: cfloat) {. + cdecl, importc: "sfMusic_setMinDistance", dynlib: Lib.} +proc setAttenuation*(music: PMusic, attenuation: cfloat) {. + cdecl, importc: "sfMusic_setAttenuation", dynlib: Lib.} +proc setPlayingOffset*(music: PMusic, time: TTime) {. + cdecl, importc: "sfMusic_setPlayingOffset", dynlib: Lib.} +proc getPitch*(music: PMusic): cfloat {. + cdecl, importc: "sfMusic_getPitch", dynlib: Lib.} +proc getVolume*(music: PMusic): cfloat {. + cdecl, importc: "sfMusic_getVolume", dynlib: Lib.} +proc getPosition*(music: PMusic): TVector3f {. + cdecl, importc: "sfMusic_getPosition", dynlib: Lib.} +proc isRelativeToListener*(music: PMusic): bool {. + cdecl, importc: "sfMusic_isRelativeToListener", dynlib: Lib.} +proc getMinDistance*(music: PMusic): cfloat {. + cdecl, importc: "sfMusic_isRelativeToListener", dynlib: Lib.} +proc getAttenuation*(music: PMusic): cfloat {. + cdecl, importc: "sfMusic_isRelativeToListener", dynlib: Lib.} + +#/ \brief Create a new sound +proc newSound*(): PSound{. + cdecl, importc: "sfSound_create", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Create a new sound by copying an existing one +#/ +#/ \param sound Sound to copy +#/ +#/ \return A new sfSound object which is a copy of \a sound +#/ +#////////////////////////////////////////////////////////// +proc copy*(sound: PSound): PSound{. + cdecl, importc: "sfSound_copy", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Destroy a sound +proc destroy*(sound: PSound){. + cdecl, importc: "sfSound_destroy", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Start or resume playing a sound +#/ +#/ This function starts the sound if it was stopped, resumes +#/ it if it was paused, and restarts it from beginning if it +#/ was it already playing. +#/ This function uses its own thread so that it doesn't block +#/ the rest of the program while the sound is played. +proc play*(sound: PSound){. + cdecl, importc: "sfSound_play", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ This function pauses the sound if it was playing, +#/ otherwise (sound already paused or stopped) it has no effect. +proc pause*(sound: PSound){. + cdecl, importc: "sfSound_pause", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ This function stops the sound if it was playing or paused, +#/ and does nothing if it was already stopped. +#/ It also resets the playing position (unlike sfSound_pause). +proc stop*(sound: PSound){. + cdecl, importc: "sfSound_stop", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ It is important to note that the sound buffer is not copied, +#/ thus the sfSoundBuffer object must remain alive as long +#/ as it is attached to the sound. +proc setBuffer*(sound: PSound; buffer: PSoundBuffer){. + cdecl, importc: "sfSound_setBuffer", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the audio buffer attached to a sound +proc getBuffer*(sound: PSound): PSoundBuffer{. + cdecl, importc: "sfSound_getBuffer", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set whether or not a sound should loop after reaching the end +#/ +#/ If set, the sound will restart from beginning after +#/ reaching the end and so on, until it is stopped or +#/ sfSound_setLoop(sound, sfFalse) is called. +#/ The default looping state for sounds is false. +proc setLoop*(sound: PSound; loop: bool){. + cdecl, importc: "sfSound_setLoop", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Tell whether or not a soud is in loop mode +proc getLoop*(sound: PSound): bool {. + cdecl, importc: "sfSound_getLoop", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the current status of a sound (stopped, paused, playing) +proc getStatus*(sound: PSound): TSoundStatus{. + cdecl, importc: "sfSound_getStatus", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set the pitch of a sound +#/ +#/ The pitch represents the perceived fundamental frequency +#/ of a sound; thus you can make a sound more acute or grave +#/ by changing its pitch. A side effect of changing the pitch +#/ is to modify the playing speed of the sound as well. +#/ The default value for the pitch is 1. +proc setPitch*(sound: PSound; pitch: cfloat){. + cdecl, importc: "sfSound_setPitch", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set the volume of a sound +#/ +#/ The volume is a value between 0 (mute) and 100 (full volume). +#/ The default value for the volume is 100. +proc setVolume*(sound: PSound; volume: cfloat){. + cdecl, importc: "sfSound_setVolume", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set the 3D position of a sound in the audio scene +#/ +#/ Only sounds with one channel (mono sounds) can be +#/ spatialized. +#/ The default position of a sound is (0, 0, 0). +proc setPosition*(sound: PSound; position: TVector3f){. + cdecl, importc: "sfSound_setPosition", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Make the sound's position relative to the listener or absolute +#/ +#/ Making a sound relative to the listener will ensure that it will always +#/ be played the same way regardless the position of the listener. +#/ This can be useful for non-spatialized sounds, sounds that are +#/ produced by the listener, or sounds attached to it. +#/ The default value is false (position is absolute). +proc setRelativeToListener*(sound: PSound; relative: bool){. + cdecl, importc: "sfSound_setRelativeToListener", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set the minimum distance of a sound +#/ +#/ The "minimum distance" of a sound is the maximum +#/ distance at which it is heard at its maximum volume. Further +#/ than the minimum distance, it will start to fade out according +#/ to its attenuation factor. A value of 0 ("inside the head +#/ of the listener") is an invalid value and is forbidden. +#/ The default value of the minimum distance is 1. +proc setMinDistance*(sound: PSound; distance: cfloat){. + cdecl, importc: "sfSound_setMinDistance", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set the attenuation factor of a sound +#/ +#/ The attenuation is a multiplicative factor which makes +#/ the sound more or less loud according to its distance +#/ from the listener. An attenuation of 0 will produce a +#/ non-attenuated sound, i.e. its volume will always be the same +#/ whether it is heard from near or from far. On the other hand, +#/ an attenuation value such as 100 will make the sound fade out +#/ very quickly as it gets further from the listener. +#/ The default value of the attenuation is 1. +proc setAttenuation*(sound: PSound; attenuation: cfloat){. + cdecl, importc: "sfSound_setAttenuation", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Change the current playing position of a sound +#/ +#/ The playing position can be changed when the sound is +#/ either paused or playing. +proc setPlayingOffset*(sound: PSound; timeOffset: sfml.TTime){. + cdecl, importc: "sfSound_setPlayingOffset", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the pitch of a sound +proc getPitch*(sound: PSound): cfloat{. + cdecl, importc: "sfSound_getPitch", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the volume of a sound +proc getVolume*(sound: PSound): cfloat{. + cdecl, importc: "sfSound_getVolume", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the 3D position of a sound in the audio scene +proc getPosition*(sound: PSound): TVector3f{. + cdecl, importc: "sfSound_getPosition", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Tell whether a sound's position is relative to the +#/ listener or is absolute +proc isRelativeToListener*(sound: PSound): bool{. + cdecl, importc: "sfSound_isRelativeToListener", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the minimum distance of a sound +proc getMinDistance*(sound: PSound): cfloat{. + cdecl, importc: "sfSound_getMinDistance", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the attenuation factor of a sound +proc getAttenuation*(sound: PSound): cfloat{. + cdecl, importc: "sfSound_getAttenuation", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the current playing position of a sound +proc getPlayingOffset*(sound: PSound): TTime{. + cdecl, importc: "sfSound_getPlayingOffset", dynlib: Lib.} + +#////////////////////////////////////////////////////////// +# Headers +#////////////////////////////////////////////////////////// +#////////////////////////////////////////////////////////// +#/ \brief Create a new sound buffer and load it from a file +#/ +#/ Here is a complete list of all the supported audio formats: +#/ ogg, wav, flac, aiff, au, raw, paf, svx, nist, voc, ircam, +#/ w64, mat4, mat5 pvf, htk, sds, avr, sd2, caf, wve, mpc2k, rf64. +#/ +#/ \param filename Path of the sound file to load +#/ +#/ \return A new sfSoundBuffer object (NULL if failed) +#/ +#////////////////////////////////////////////////////////// +proc newSoundBuffer*(filename: cstring): PSoundBuffer{. + cdecl, importc: "sfSoundBuffer_createFromFile", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Create a new sound buffer and load it from a file in memory +#/ +#/ Here is a complete list of all the supported audio formats: +#/ ogg, wav, flac, aiff, au, raw, paf, svx, nist, voc, ircam, +#/ w64, mat4, mat5 pvf, htk, sds, avr, sd2, caf, wve, mpc2k, rf64. +#/ +#/ \param data Pointer to the file data in memory +#/ \param sizeInBytes Size of the data to load, in bytes +#/ +#/ \return A new sfSoundBuffer object (NULL if failed) +#/ +#////////////////////////////////////////////////////////// +proc newSoundBuffer*(data: pointer; sizeInBytes: cint): PSoundBuffer{. + cdecl, importc: "sfSoundBuffer_createFromMemory", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Create a new sound buffer and load it from a custom stream +#/ +#/ Here is a complete list of all the supported audio formats: +#/ ogg, wav, flac, aiff, au, raw, paf, svx, nist, voc, ircam, +#/ w64, mat4, mat5 pvf, htk, sds, avr, sd2, caf, wve, mpc2k, rf64. +#/ +#/ \param stream Source stream to read from +#/ +#/ \return A new sfSoundBuffer object (NULL if failed) +#/ +#////////////////////////////////////////////////////////// +proc newSoundBuffer*(stream: PInputStream): PSoundBuffer{. + cdecl, importc: "sfSoundBuffer_createFromStream", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Create a new sound buffer and load it from an array of samples in memory +#/ +#/ The assumed format of the audio samples is 16 bits signed integer +#/ (sfInt16). +#/ +#/ \param samples Pointer to the array of samples in memory +#/ \param sampleCount Number of samples in the array +#/ \param channelCount Number of channels (1 = mono, 2 = stereo, ...) +#/ \param sampleRate Sample rate (number of samples to play per second) +#/ +#/ \return A new sfSoundBuffer object (NULL if failed) +#/ +#////////////////////////////////////////////////////////// +proc createFromSamples*(samples: ptr int16; sampleCount: cuint; + channelCount: cuint; sampleRate: cuint): PSoundBuffer{. + cdecl, importc: "sfSoundBuffer_createFromSamples", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Create a new sound buffer by copying an existing one +#/ +#/ \param soundBuffer Sound buffer to copy +#/ +#/ \return A new sfSoundBuffer object which is a copy of \a soundBuffer +#/ +#////////////////////////////////////////////////////////// +proc copy*(soundBuffer: PSoundBuffer): PSoundBuffer{. + cdecl, importc: "sfSoundBuffer_copy", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Destroy a sound buffer +#/ +#/ \param soundBuffer Sound buffer to destroy +#/ +#////////////////////////////////////////////////////////// +proc destroy*(soundBuffer: PSoundBuffer){. + cdecl, importc: "sfSoundBuffer_destroy", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Save a sound buffer to an audio file +#/ +#/ Here is a complete list of all the supported audio formats: +#/ ogg, wav, flac, aiff, au, raw, paf, svx, nist, voc, ircam, +#/ w64, mat4, mat5 pvf, htk, sds, avr, sd2, caf, wve, mpc2k, rf64. +#/ +#/ \param soundBuffer Sound buffer object +#/ \param filename Path of the sound file to write +#/ +#/ \return sfTrue if saving succeeded, sfFalse if it failed +#/ +#////////////////////////////////////////////////////////// +proc saveToFile*(soundBuffer: PSoundBuffer; filename: cstring): bool {. + cdecl, importc: "sfSoundBuffer_saveToFile", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the array of audio samples stored in a sound buffer +#/ +#/ The format of the returned samples is 16 bits signed integer +#/ (sfInt16). The total number of samples in this array +#/ is given by the sfSoundBuffer_getSampleCount function. +#/ +#/ \param soundBuffer Sound buffer object +#/ +#/ \return Read-only pointer to the array of sound samples +#/ +#////////////////////////////////////////////////////////// +proc sfSoundBuffer_getSamples*(soundBuffer: PSoundBuffer): ptr Int16{. + cdecl, importc: "sfSoundBuffer_getSamples", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the number of samples stored in a sound buffer +#/ +#/ The array of samples can be accessed with the +#/ sfSoundBuffer_getSamples function. +proc getSampleCount*(soundBuffer: PSoundBuffer): cint{. + cdecl, importc: "sfSoundBuffer_getSampleCount", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the sample rate of a sound buffer +#/ +#/ The sample rate is the number of samples played per second. +#/ The higher, the better the quality (for example, 44100 +#/ samples/s is CD quality). +proc getSampleRate*(soundBuffer: PSoundBuffer): cuint{. + cdecl, importc: "sfSoundBuffer_getSampleRate", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the number of channels used by a sound buffer +#/ +#/ If the sound is mono then the number of channels will +#/ be 1, 2 for stereo, etc. +proc getChannelCount*(soundBuffer: PSoundBuffer): cuint{. + cdecl, importc: "sfSoundBuffer_getChannelCount", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the total duration of a sound buffer +#/ +#/ \param soundBuffer Sound buffer object +#/ +#/ \return Sound duration +#/ +#////////////////////////////////////////////////////////// +proc getDuration*(soundBuffer: PSoundBuffer): TTime{. + cdecl, importc: "sfSoundBuffer_getDuration", dynlib: Lib.} + +#////////////////////////////////////////////////////////// +#/ \brief Change the global volume of all the sounds and musics +#/ +#/ The volume is a number between 0 and 100; it is combined with +#/ the individual volume of each sound / music. +#/ The default value for the volume is 100 (maximum). +#/ +#/ \param volume New global volume, in the range [0, 100] +#/ +#////////////////////////////////////////////////////////// +proc listenerSetGlobalVolume*(volume: cfloat){. + cdecl, importc: "sfListener_setGlobalVolume", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the current value of the global volume +#/ +#/ \return Current global volume, in the range [0, 100] +#/ +#////////////////////////////////////////////////////////// +proc listenerGetGlobalVolume*(): cfloat{. + cdecl, importc: "sfListener_getGlobalVolume", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set the position of the listener in the scene +#/ +#/ The default listener's position is (0, 0, 0). +#/ +#/ \param position New position of the listener +#/ +#////////////////////////////////////////////////////////// +proc listenerSetPosition*(position: TVector3f){. + cdecl, importc: "sfListener_setPosition", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the current position of the listener in the scene +#/ +#/ \return The listener's position +#/ +#////////////////////////////////////////////////////////// +proc listenerGetPosition*(): TVector3f{. + cdecl, importc: "sfListener_getPosition", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set the orientation of the listener in the scene +#/ +#/ The orientation defines the 3D axes of the listener +#/ (left, up, front) in the scene. The orientation vector +#/ doesn't have to be normalized. +#/ The default listener's orientation is (0, 0, -1). +#/ +#/ \param position New direction of the listener +#/ +#////////////////////////////////////////////////////////// +proc listenerSetDirection*(orientation: TVector3f){. + cdecl, importc: "sfListener_setDirection", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the current orientation of the listener in the scene +#/ +#/ \return The listener's direction +#/ +#////////////////////////////////////////////////////////// +proc listenerGetDirection*(): TVector3f{. + cdecl, importc: "sfListener_getDirection", dynlib: Lib.} + +type + TSoundRecorderStartCallback* = proc (a2: pointer): bool {.cdecl.} + #/< Type of the callback used when starting a capture + TSoundRecorderProcessCallback* = proc(a2: ptr int16; a3: cuint; + a4: pointer): bool {.cdecl.} + #/< Type of the callback used to process audio data + TSoundRecorderStopCallback* = proc (a2: pointer){.cdecl.} + #/< Type of the callback used when stopping a capture +#////////////////////////////////////////////////////////// +#/ \brief Construct a new sound recorder from callback functions +#/ +#/ \param onStart Callback function which will be called when a new capture starts (can be NULL) +#/ \param onProcess Callback function which will be called each time there's audio data to process +#/ \param onStop Callback function which will be called when the current capture stops (can be NULL) +#/ \param userData Data to pass to the callback function (can be NULL) +#/ +#/ \return A new sfSoundRecorder object (NULL if failed) +#/ +#////////////////////////////////////////////////////////// +proc newSoundRecorder*(onStart: TSoundRecorderStartCallback; + onProcess: TSoundRecorderProcessCallback; + onStop: TSoundRecorderStopCallback; + userData: pointer = nil): PSoundRecorder{. + cdecl, importc: "sfSoundRecorder_create", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Destroy a sound recorder +#/ +#/ \param soundRecorder Sound recorder to destroy +#/ +#////////////////////////////////////////////////////////// +proc destroy*(soundRecorder: PSoundRecorder){. + cdecl, importc: "sfSoundRecorder_destroy", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Start the capture of a sound recorder +#/ +#/ The \a sampleRate parameter defines the number of audio samples +#/ captured per second. The higher, the better the quality +#/ (for example, 44100 samples/sec is CD quality). +#/ This function uses its own thread so that it doesn't block +#/ the rest of the program while the capture runs. +#/ Please note that only one capture can happen at the same time. +#/ +#/ \param soundRecorder Sound recorder object +#/ \param sampleRate Desired capture rate, in number of samples per second +#/ +#////////////////////////////////////////////////////////// +proc start*(soundRecorder: PSoundRecorder; sampleRate: cuint){. + cdecl, importc: "sfSoundRecorder_start", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Stop the capture of a sound recorder +#/ +#/ \param soundRecorder Sound recorder object +#/ +#////////////////////////////////////////////////////////// +proc stop*(soundRecorder: PSoundRecorder){. + cdecl, importc: "sfSoundRecorder_stop", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the sample rate of a sound recorder +#/ +#/ The sample rate defines the number of audio samples +#/ captured per second. The higher, the better the quality +#/ (for example, 44100 samples/sec is CD quality). +#/ +#/ \param soundRecorder Sound recorder object +#/ +#/ \return Sample rate, in samples per second +#/ +#////////////////////////////////////////////////////////// +proc getSampleRate*(soundRecorder: PSoundRecorder): cuint{. + cdecl, importc: "sfSoundRecorder_getSampleRate", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Check if the system supports audio capture +#/ +#/ This function should always be called before using +#/ the audio capture features. If it returns false, then +#/ any attempt to use sfSoundRecorder will fail. +#/ +#/ \return sfTrue if audio capture is supported, sfFalse otherwise +#/ +#////////////////////////////////////////////////////////// +proc soundRecorderIsAvailable*(): bool {. + cdecl, importc: "sfSoundRecorder_isAvailable", dynlib: Lib.} + +#////////////////////////////////////////////////////////// +#/ \brief Create a new sound buffer recorder +#/ +#/ \return A new sfSoundBufferRecorder object (NULL if failed) +#/ +#////////////////////////////////////////////////////////// +proc newSoundBufferRecorder*(): PSoundBufferRecorder{. + cdecl, importc: "sfSoundBufferRecorder_create", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Destroy a sound buffer recorder +#/ +#/ \param soundBufferRecorder Sound buffer recorder to destroy +#/ +#////////////////////////////////////////////////////////// +proc destroy*(soundBufferRecorder: PSoundBufferRecorder){. + cdecl, importc: "sfSoundBufferRecorder_destroy", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Start the capture of a sound recorder recorder +#/ +#/ The \a sampleRate parameter defines the number of audio samples +#/ captured per second. The higher, the better the quality +#/ (for example, 44100 samples/sec is CD quality). +#/ This function uses its own thread so that it doesn't block +#/ the rest of the program while the capture runs. +#/ Please note that only one capture can happen at the same time. +#/ +#/ \param soundBufferRecorder Sound buffer recorder object +#/ \param sampleRate Desired capture rate, in number of samples per second +#/ +#////////////////////////////////////////////////////////// +proc start*(soundBufferRecorder: PSoundBufferRecorder; sampleRate: cuint){. + cdecl, importc: "sfSoundBufferRecorder_start", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Stop the capture of a sound recorder +#/ +#/ \param soundBufferRecorder Sound buffer recorder object +#/ +#////////////////////////////////////////////////////////// +proc stop*(soundBufferRecorder: PSoundBufferRecorder){. + cdecl, importc: "sfSoundBufferRecorder_stop", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the sample rate of a sound buffer recorder +#/ +#/ The sample rate defines the number of audio samples +#/ captured per second. The higher, the better the quality +#/ (for example, 44100 samples/sec is CD quality). +#/ +#/ \param soundBufferRecorder Sound buffer recorder object +#/ +#/ \return Sample rate, in samples per second +#/ +#////////////////////////////////////////////////////////// +proc getSampleRate*(soundBufferRecorder: PSoundBufferRecorder): cuint{. + cdecl, importc: "sfSoundBufferRecorder_getSampleRate", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the sound buffer containing the captured audio data +#/ +#/ The sound buffer is valid only after the capture has ended. +#/ This function provides a read-only access to the internal +#/ sound buffer, but it can be copied if you need to +#/ make any modification to it. +#/ +#/ \param soundBufferRecorder Sound buffer recorder object +#/ +#/ \return Read-only access to the sound buffer +#/ +#////////////////////////////////////////////////////////// +proc getBuffer*(soundBufferRecorder: PSoundBufferRecorder): PSoundBuffer{. + cdecl, importc: "sfSoundBufferRecorder_getBuffer", dynlib: Lib.} + + +#////////////////////////////////////////////////////////// +#/ \brief defines the data to fill by the OnGetData callback +#/ +#////////////////////////////////////////////////////////// +type + PSoundStreamChunk* = ptr TSoundStreamChunk + TSoundStreamChunk*{.pure, final.} = object + samples*: ptr int16 #/< Pointer to the audio samples + sampleCount*: cuint #/< Number of samples pointed by Samples + + TSoundStreamGetDataCallback* = proc (a2: PSoundStreamChunk; + a3: pointer): bool{.cdecl.} + #/< Type of the callback used to get a sound stream data + TSoundStreamSeekCallback* = proc (a2: TTime; a3: pointer){.cdecl.} + #/< Type of the callback used to seek in a sound stream +#////////////////////////////////////////////////////////// +#/ \brief Create a new sound stream +#/ +#/ \param onGetData Function called when the stream needs more data (can't be NULL) +#/ \param onSeek Function called when the stream seeks (can't be NULL) +#/ \param channelCount Number of channels to use (1 = mono, 2 = stereo) +#/ \param sampleRate Sample rate of the sound (44100 = CD quality) +#/ \param userData Data to pass to the callback functions +#/ +#/ \return A new sfSoundStream object +#/ +#////////////////////////////////////////////////////////// +proc create*(onGetData: TSoundStreamGetDataCallback; onSeek: TSoundStreamSeekCallback; + channelCount: cuint; sampleRate: cuint; userData: pointer): PSoundStream{. + cdecl, importc: "sfSoundStream_create", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Destroy a sound stream +#/ +#/ \param soundStream Sound stream to destroy +#/ +#////////////////////////////////////////////////////////// +proc destroy*(soundStream: PSoundStream){. + cdecl, importc: "sfSoundStream_destroy", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Start or resume playing a sound stream +#/ +#/ This function starts the stream if it was stopped, resumes +#/ it if it was paused, and restarts it from beginning if it +#/ was it already playing. +#/ This function uses its own thread so that it doesn't block +#/ the rest of the program while the music is played. +#/ +#/ \param soundStream Sound stream object +#/ +#////////////////////////////////////////////////////////// +proc play*(soundStream: PSoundStream){. + cdecl, importc: "sfSoundStream_play", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Pause a sound stream +#/ +#/ This function pauses the stream if it was playing, +#/ otherwise (stream already paused or stopped) it has no effect. +#/ +#/ \param soundStream Sound stream object +#/ +#////////////////////////////////////////////////////////// +proc pause*(soundStream: PSoundStream){. + cdecl, importc: "sfSoundStream_pause", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Stop playing a sound stream +#/ +#/ This function stops the stream if it was playing or paused, +#/ and does nothing if it was already stopped. +#/ It also resets the playing position (unlike sfSoundStream_pause). +#/ +#/ \param soundStream Sound stream object +#/ +#////////////////////////////////////////////////////////// +proc stop*(soundStream: PSoundStream){. + cdecl, importc: "sfSoundStream_stop", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the current status of a sound stream (stopped, paused, playing) +#/ +#/ \param soundStream Sound stream object +#/ +#/ \return Current status +#/ +#////////////////////////////////////////////////////////// +proc getStatus*(soundStream: PSoundStream): TSoundStatus{. + cdecl, importc: "sfSoundStream_getStatus", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Return the number of channels of a sound stream +#/ +#/ 1 channel means a mono sound, 2 means stereo, etc. +#/ +#/ \param soundStream Sound stream object +#/ +#/ \return Number of channels +#/ +#////////////////////////////////////////////////////////// +proc getChannelCount*(soundStream: PSoundStream): cuint{. + cdecl, importc: "sfSoundStream_getChannelCount", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the sample rate of a sound stream +#/ +#/ The sample rate is the number of audio samples played per +#/ second. The higher, the better the quality. +#/ +#/ \param soundStream Sound stream object +#/ +#/ \return Sample rate, in number of samples per second +#/ +#////////////////////////////////////////////////////////// +proc getSampleRate*(soundStream: PSoundStream): cuint{. + cdecl, importc: "sfSoundStream_getSampleRate", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set the pitch of a sound stream +#/ +#/ The pitch represents the perceived fundamental frequency +#/ of a sound; thus you can make a stream more acute or grave +#/ by changing its pitch. A side effect of changing the pitch +#/ is to modify the playing speed of the stream as well. +#/ The default value for the pitch is 1. +#/ +#/ \param soundStream Sound stream object +#/ \param pitch New pitch to apply to the stream +#/ +#////////////////////////////////////////////////////////// +proc setPitch*(soundStream: PSoundStream; pitch: cfloat){. + cdecl, importc: "sfSoundStream_setPitch", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set the volume of a sound stream +#/ +#/ The volume is a value between 0 (mute) and 100 (full volume). +#/ The default value for the volume is 100. +#/ +#/ \param soundStream Sound stream object +#/ \param volume Volume of the stream +#/ +#////////////////////////////////////////////////////////// +proc setVolume*(soundStream: PSoundStream; volume: cfloat){. + cdecl, importc: "sfSoundStream_setVolume", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set the 3D position of a sound stream in the audio scene +#/ +#/ Only streams with one channel (mono streams) can be +#/ spatialized. +#/ The default position of a stream is (0, 0, 0). +#/ +#/ \param soundStream Sound stream object +#/ \param position Position of the stream in the scene +#/ +#////////////////////////////////////////////////////////// +proc setPosition*(soundStream: PSoundStream; position: TVector3f){. + cdecl, importc: "sfSoundStream_setPosition", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Make a sound stream's position relative to the listener or absolute +#/ +#/ Making a stream relative to the listener will ensure that it will always +#/ be played the same way regardless the position of the listener. +#/ This can be useful for non-spatialized streams, streams that are +#/ produced by the listener, or streams attached to it. +#/ The default value is false (position is absolute). +#/ +#/ \param soundStream Sound stream object +#/ \param relative sfTrue to set the position relative, sfFalse to set it absolute +#/ +#////////////////////////////////////////////////////////// +proc setRelativeToListener*(soundStream: PSoundStream; relative: bool){. + cdecl, importc: "sfSoundStream_setRelativeToListener", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set the minimum distance of a sound stream +#/ +#/ The "minimum distance" of a stream is the maximum +#/ distance at which it is heard at its maximum volume. Further +#/ than the minimum distance, it will start to fade out according +#/ to its attenuation factor. A value of 0 ("inside the head +#/ of the listener") is an invalid value and is forbidden. +#/ The default value of the minimum distance is 1. +#/ +#/ \param soundStream Sound stream object +#/ \param distance New minimum distance of the stream +#/ +#////////////////////////////////////////////////////////// +proc setMinDistance*(soundStream: PSoundStream; distance: cfloat){. + cdecl, importc: "sfSoundStream_setMinDistance", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set the attenuation factor of a sound stream +#/ +#/ The attenuation is a multiplicative factor which makes +#/ the stream more or less loud according to its distance +#/ from the listener. An attenuation of 0 will produce a +#/ non-attenuated stream, i.e. its volume will always be the same +#/ whether it is heard from near or from far. On the other hand, +#/ an attenuation value such as 100 will make the stream fade out +#/ very quickly as it gets further from the listener. +#/ The default value of the attenuation is 1. +#/ +#/ \param soundStream Sound stream object +#/ \param attenuation New attenuation factor of the stream +#/ +#////////////////////////////////////////////////////////// +proc setAttenuation*(soundStream: PSoundStream; attenuation: cfloat){. + cdecl, importc: "sfSoundStream_setAttenuation", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Change the current playing position of a sound stream +#/ +#/ The playing position can be changed when the stream is +#/ either paused or playing. +#/ +#/ \param soundStream Sound stream object +#/ \param timeOffset New playing position +#/ +#////////////////////////////////////////////////////////// +proc setPlayingOffset*(soundStream: PSoundStream; timeOffset: TTime){. + cdecl, importc: "sfSoundStream_setPlayingOffset", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Set whether or not a sound stream should loop after reaching the end +#/ +#/ If set, the stream will restart from beginning after +#/ reaching the end and so on, until it is stopped or +#/ sfSoundStream_setLoop(stream, sfFalse) is called. +#/ The default looping state for sound streams is false. +#/ +#/ \param soundStream Sound stream object +#/ \param loop sfTrue to play in loop, sfFalse to play once +#/ +#////////////////////////////////////////////////////////// +proc setLoop*(soundStream: PSoundStream; loop: bool){. + cdecl, importc: "sfSoundStream_setLoop", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the pitch of a sound stream +#/ +#/ \param soundStream Sound stream object +#/ +#/ \return Pitch of the stream +#/ +#////////////////////////////////////////////////////////// +proc getPitch*(soundStream: PSoundStream): cfloat{. + cdecl, importc: "sfSoundStream_getPitch", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the volume of a sound stream +#/ +#/ \param soundStream Sound stream object +#/ +#/ \return Volume of the stream, in the range [0, 100] +#/ +#////////////////////////////////////////////////////////// +proc getVolume*(soundStream: PSoundStream): cfloat{. + cdecl, importc: "sfSoundStream_getVolume", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the 3D position of a sound stream in the audio scene +#/ +#/ \param soundStream Sound stream object +#/ +#/ \return Position of the stream in the world +#/ +#////////////////////////////////////////////////////////// +proc getPosition*(soundStream: PSoundStream): TVector3f{. + cdecl, importc: "sfSoundStream_getPosition", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Tell whether a sound stream's position is relative to the +#/ listener or is absolute +#/ +#/ \param soundStream Sound stream object +#/ +#/ \return sfTrue if the position is relative, sfFalse if it's absolute +#/ +#////////////////////////////////////////////////////////// +proc isRelativeToListener*(soundStream: PSoundStream): bool{. + cdecl, importc: "sfSoundStream_isRelativeToListener", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the minimum distance of a sound stream +#/ +#/ \param soundStream Sound stream object +#/ +#/ \return Minimum distance of the stream +#/ +#////////////////////////////////////////////////////////// +proc getMinDistance*(soundStream: PSoundStream): cfloat{. + cdecl, importc: "sfSoundStream_getMinDistance", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the attenuation factor of a sound stream +#/ +#/ \param soundStream Sound stream object +#/ +#/ \return Attenuation factor of the stream +#/ +#////////////////////////////////////////////////////////// +proc getAttenuation*(soundStream: PSoundStream): cfloat{. + cdecl, importc: "sfSoundStream_getAttenuation", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Tell whether or not a sound stream is in loop mode +#/ +#/ \param soundStream Sound stream object +#/ +#/ \return sfTrue if the music is looping, sfFalse otherwise +#/ +#////////////////////////////////////////////////////////// +proc getLoop*(soundStream: PSoundStream): bool{. + cdecl, importc: "sfSoundStream_getLoop", dynlib: Lib.} +#////////////////////////////////////////////////////////// +#/ \brief Get the current playing position of a sound stream +#/ +#/ \param soundStream Sound stream object +#/ +#/ \return Current playing position +#/ +#////////////////////////////////////////////////////////// +proc getPlayingOffset*(soundStream: PSoundStream): TTime{. + cdecl, importc: "sfSoundStream_getPlayingOffset", dynlib: Lib.} diff --git a/tests/manyloc/keineschweine/dependencies/sfml/sfml_colors.nim b/tests/manyloc/keineschweine/dependencies/sfml/sfml_colors.nim new file mode 100644 index 000000000..31473b17a --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/sfml/sfml_colors.nim @@ -0,0 +1,15 @@ +import sfml +{.deadCodeElim: on.} +let + Black*: TColor = color(0, 0, 0) + White*: TColor = color(255, 255, 255) + Red*: TColor = color(255, 0, 0) + Green*: TColor = color(0, 255, 0) + Blue*: TColor = color(0, 0, 255) + Yellow*: TColor = color(255, 255, 0) + Magenta*: TColor = color(255, 0, 255) + Cyan*: TColor = color(0, 255, 255) + Transparent*: TColor = color(0, 0, 0, 0) + Gray* = color(84, 84, 84) + RoyalBlue* = color(65, 105, 225) +##todo: define more colors lul \ No newline at end of file diff --git a/tests/manyloc/keineschweine/dependencies/sfml/sfml_vector.nim b/tests/manyloc/keineschweine/dependencies/sfml/sfml_vector.nim new file mode 100644 index 000000000..474d249aa --- /dev/null +++ b/tests/manyloc/keineschweine/dependencies/sfml/sfml_vector.nim @@ -0,0 +1,2 @@ +import sfml, math, strutils +{.deadCodeElim: on.} diff --git a/tests/manyloc/keineschweine/enet_server/enet_client.nim b/tests/manyloc/keineschweine/enet_server/enet_client.nim new file mode 100644 index 000000000..f4c8732e3 --- /dev/null +++ b/tests/manyloc/keineschweine/enet_server/enet_client.nim @@ -0,0 +1,228 @@ +import enet, strutils, + sfml, sfml_colors, sg_gui, input_helpers, + math_helpers, sg_packets, estreams, tables, + json, sg_assets, client_helpers +if enetInit() != 0: + quit "Could not initialize ENet" +type + TClientSettings = object + resolution*: TVideoMode + offlineFile: string + dirserver: tuple[host: string, port: int16] + website*: string +var + clientSettings: TClientSettings + event: enet.TEvent + bConnected = false + runServer = true + gui = newGuiContainer() + zonelist = newGuiContainer() + kc = newKeyClient(setActive = true) + clock = newClock() + chatBox: PMessageArea + chatInput: PTextEntry + loginBtn, playBtn: PButton + fpsText = newText("", guiFont, 18) + connectionButtons: seq[PButton] + connectButton: PButton + u_alias, u_passwd: PTextEntry + dirServer: PServer + zone: PServer + showZoneList = false + myCreds = newScLogin(0, "", "") ##my session token + +proc handleChat(server: PServer; buf: PBuffer) = + let msg = readScChat(buf) + chatBox.add msg +proc handlePlayerLogin(server: PServer; buf: PBuffer) = + let login = readScLogin(buf) + myCreds = login + echo("I am ", $myCreds) + + +kc.registerHandler MouseLeft, down, proc() = + gui.click(input_helpers.getMousePos()) + +block: + var pos = vec2f(15, 550) + chatBox = gui.newMessageArea(pos) + pos.y += 20 + chatInput = gui.newTextEntry("...", pos, proc() = + sendPubChat dirServer, chatInput.getText() + chatInput.clearText()) + +gui.setActive(chatInput) + +proc dispMessage(args: varargs[string, `$`]) = + var s = "" + for it in items(args): + s.add it + chatbox.add(s) +proc dispMessage(text: string) {.inline.} = + chatbox.add(text) +proc dispError(text: string) {.inline.} = + chatBox.add(newScChat(kind = CError, text = text)) + +proc updateButtons() = + let conn = dirServer.connected + for b in connectionButtons: setEnabled(b, conn) + if conn: + connectButton.setString "Disconnect" + else: + connectButton.setString "Connect" + +proc poll(serv: PServer; timeout: cuint = 30) = + if serv.isNil or serv.host.isNil: return + if serv.connected: + while serv.host.hostService(event, timeout) > 0: + case event.kind + of EvtReceive: + var buf = newBuffer(event.packet) + + serv.handlePackets(buf) + + event.packet.destroy() + of EvtDisconnect: + dispMessage "Disconnected" + serv.connected = false + event.peer.data = nil + updateButtons() + of EvtNone: discard + else: + echo repr(event) + else: + if serv.host.hostService(event, timeout) > 0 and event.kind == EvtConnect: + dispMessage "Connected" + serv.connected = true + if serv.peer != event.peer: + serv.peer = event.peer + event.peer.data = serv + updateButtons() + +proc tryLogin*(b: PButton) = + var login = newCsLogin( + alias = u_alias.getText(), + passwd = u_passwd.getText()) + dirServer.send HLogin, login +proc tryTransition*(b: PButton) = + #zone.writePkt HZoneJoinReq, myCreds +proc tryConnect*(b: PButton) = + if not dirServer.connected: + var error: string + if not dirServer.connect( + clientSettings.dirServer.host, + clientSettings.dirServer.port, + error): + dispError(error) + else: + dirServer.peer.disconnect(1) + +proc playOffline*(b: PButton) = + var errors: seq[string] = @[] + if loadSettingsFromFile(clientSettings.offlineFile, errors): + transition() + else: + dispMessage "Errors reading the file (", clientSettings.offlineFile, "):" + for e in errors: dispError(e) + +proc getClientSettings*(): TClientSettings = + result = clientSettings + + +proc lobbyInit*() = + var s = json.parseFile("./client_settings.json") + clientSettings.offlineFile = "data/" + clientSettings.offlineFile.add s["default-file"].str + let dirserv = s["directory-server"] + clientSettings.dirserver.host = dirserv["host"].str + clientSettings.dirserver.port = dirserv["port"].num.int16 + clientSettings.resolution.width = s["resolution"][0].num.cint + clientSettings.resolution.height= s["resolution"][1].num.cint + clientSettings.resolution.bitsPerPixel = s["resolution"][2].num.cint + clientSettings.website = s["website"].str + zonelist.setPosition(vec2f(200.0, 100.0)) + connectionButtons = @[] + + var pos = vec2f(10, 10) + u_alias = gui.newTextEntry( + if s.existsKey("alias"): s["alias"].str else: "alias", + pos) + pos.y += 20 + u_passwd = gui.newTextEntry("buzz", pos) + pos.y += 20 + connectionButtons.add(gui.newButton( + text = "Login", + position = pos, + onClick = tryLogin, + startEnabled = false)) + pos.y += 20 + fpsText.setPosition pos + pos.y += 20 + connectButton = gui.newButton( + text = "Connect", + position = pos, + onClick = tryConnect) + pos.y += 20 + gui.newButton("Test Files", position = pos, onClick = proc(b: PButton) = + var req = newCsZoneJoinReq(myCreds) + dirServer.send HZoneJoinReq, req) + pos.y += 20 + connectionButtons.add(gui.newButton( + text = "Test Chat", + position = pos, + onClick = (proc(b: PButton) = + var pkt = newCsChat(text = "ohai") + dirServer.send HChat, pkt), + startEnabled = false)) + pos.y += 20 + downloadProgress.setPosition(pos) + downloadProgress.bg.setFillColor(color(34, 139, 34)) + downloadProgress.bg.setSize(vec2f(0, 0)) + gui.add(downloadProgress) + + playBtn = gui.newButton( + text = "Play", + position = vec2f(680.0, 8.0), + onClick = tryTransition, + startEnabled = false) + gui.newButton( + text = "Play Offline", + position = vec2f(680.0, 28.0), + onClick = playOffline) + discard """gui.newButton(text = "Scrollback + 1", position = vec2f(185, 10), onClick = proc(b: PButton) = + messageArea.scrollBack += 1 + update(messageArea)) + gui.newButton(text = "Scrollback - 1", position = vec2f(185+160, 10), onClick = proc(b: PButton) = + messageArea.scrollBack -= 1 + update(messageArea)) + gui.newButton(text = "Flood msg area", position = vec2f(185, 30), onClick = proc(b: PButton) = + for i in 0.. <30: + dispMessage($i))""" + dirServer = newServer() + dirServer.addHandler HChat, handleChat + dirServer.addHandler HLogin, handlePlayerLogin + dirServer.addHandler HFileTransfer, client_helpers.handleFilePartRecv + dirServer.addHandler HChallengeResult, client_helpers.handleFileChallengeResult + dirServer.addHandler HFileChallenge, client_helpers.handleFileChallenge + +proc lobbyReady*() = + kc.setActive() + gui.setActive(u_alias) + +var i = 0 +proc lobbyUpdate*(dt: float) = + dirServer.poll() + #let res = disp.poll() + gui.update(dt) + i = (i + 1) mod 60 + if i == 0: + fpsText.setString("FPS: "& ff(1.0/dt)) + +proc lobbyDraw*(window: PRenderWindow) = + window.clear(Black) + window.draw chatBox + window.draw gui + window.draw fpsText + if showZonelist: window.draw zonelist + window.display() + diff --git a/tests/manyloc/keineschweine/enet_server/enet_server.nim b/tests/manyloc/keineschweine/enet_server/enet_server.nim new file mode 100644 index 000000000..6dd1a6a7f --- /dev/null +++ b/tests/manyloc/keineschweine/enet_server/enet_server.nim @@ -0,0 +1,294 @@ +import enet, strutils, idgen, tables, math_helpers, + estreams, sg_packets, server_utils, sg_assets, client_helpers +when appType == "gui": + import sfml, sfml_colors, sg_gui, + input_helpers, sfml_stuff +else: + import times +type + TCallback = proc(client: PClient; buffer: PBuffer) +var + server: PHost + dirServer: PServer + standAloneMode = true + event: enet.TEvent + clientID = newIDGen[int32]() + clients = initTable[int32, PClient](64) + handlers = initTable[char, TCallback](32) + +when appType == "gui": + var + gui = newGuiContainer() + chatBox = gui.newMessageArea(vec2f(15, 550)) + window = newRenderWindow(videoMode(800, 600, 32), "Sup yo", sfDefaultSTyle) + mousepos = newText("", guiFont, 16) + fpsText = mousePos.copy() + inputClient = newKeyClient(setActive = true) + chatBox.sizeVisible = 30 + mousePos.setColor(Green) + fpsText.setposition(vec2f(0, 20)) + inputClient.registerHandler MouseLeft, down, proc() = + gui.click(input_helpers.getMousePos()) + inputClient.registerHandler MouseMiddle, down, proc() = + let pos = input_helpers.getMousePos() + mousePos.setString("($1,$2)".format(ff(pos.x), ff(pos.y))) + mousePos.setPosition(pos) + proc dispMessage(args: varargs[string, `$`]) = + var s = "" + for it in items(args): + s.add it + chatbox.add(s) + proc dispError(args: varargs[string, `$`]) = + var s = "" + for it in items(args): s.add(it) + chatBox.add(newScChat(kind = CError, text = s)) +else: + proc dispMessage(args: varargs[string, `$`]) = + var m = "" + for it in items(args): m.add(it) + echo " ", m + proc dispError(args: varargs[string, `$`]) = + var m = "" + for it in items(args): m.add(it) + echo "**", m + + +var pubChatQueue = newBuffer(1024) +proc queuePub(sender: PClient, msg: CsChat) = + var chat = newScChat(kind = CPub, fromPlayer = sender.alias, text = msg.text) + pubChatQueue.write(HChat) + pubChatQueue.pack(chat) +proc flushPubChat() = + if pubChatQueue.isDirty: + let packet = pubChatQueue.toPacket(FlagReliable) + for id, client in pairs(clients): + discard client.peer.send(0.cuchar, packet) + pubChatQueue.flush() + +handlers[HChat] = proc(client: PClient; buffer: PBuffer) = + var chat = readCsChat(buffer) + + if not client.auth: + client.sendError("You are not logged in.") + return + #if chat.target != "": ##private + # if alias2client.hasKey(chat.target): + # alias2client[chat.target].forwardPrivate(client, chat.text) + #else: + + dispmessage("<", client.alias, "> ", chat.text) + queuePub(client, chat) + +handlers[HLogin] = proc(client: PClient; buffer: PBuffer) = + var info = readCsLogin(buffer) + if client.auth: + client.sendError "You are already logged in." + return + client.alias = info.alias + client.auth = true + var resp = newScLogin(client.id, client.alias, "sessionkeylulz") + client.send HLogin, resp + client.sendMessage "welcome" + dispMessage("Client logged in: ", client) + + +handlers[HFileTransfer] = server_utils.handleFilePartAck +handlers[HFileChallenge] = server_utils.handleFileChallengeResp + +handlers[HZoneJoinReq] = proc(client: PClient; buffer: PBuffer) = + var info = readCsZoneJoinReq(buffer) + dispmessage "Got zone join request" + client.startVerifyingFiles() + + + +when isMainModule: + import parseopt, matchers, os, json + + + if enetInit() != 0: + quit "Could not initialize ENet" + + var address: enet.TAddress + + block: + var zoneCfgFile = "./server_settings.json" + for kind, key, val in getOpt(): + case kind + of cmdShortOption, cmdLongOption: + case key + of "f", "file": + if existsFile(val): + zoneCfgFile = val + else: + echo("File does not exist: ", val) + else: + echo("Unknown option: ", key," ", val) + else: + echo("Unknown option: ", key, " ", val) + var jsonSettings = parseFile(zoneCfgFile) + let + port = uint16(jsonSettings["port"].num) + zoneFile = jsonSettings["settings"].str + dirServerInfo = jsonSettings["dirserver"] + + address.host = EnetHostAny + address.port = port + + var path = getAppDir()/../"data"/zoneFile + if not existsFile(path): + echo("Zone settings file does not exist: ../data/", zoneFile) + echo(path) + quit(1) + + discard """block: + var + TestFile: FileChallengePair + contents = repeatStr(2, "abcdefghijklmnopqrstuvwxyz") + testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32) + testFile.file = checksumStr(contents) + myAssets.add testFile""" + + setCurrentDir getAppDir().parentDir() + let zonesettings = readFile(path) + var + errors: seq[string] = @[] + if not loadSettings(zoneSettings, errors): + echo("You have errors in your zone settings:") + for e in errors: echo("**", e) + quit(1) + errors.setLen 0 + + var pair: FileChallengePair + pair.challenge.file = zoneFile + pair.challenge.assetType = FZoneCfg + pair.challenge.fullLen = zoneSettings.len.int32 + pair.file = checksumStr(zoneSettings) + myAssets.add pair + + allAssets: + if not load(asset): + echo "Invalid or missing file ", file + else: + var pair: FileChallengePair + pair.challenge.file = file + pair.challenge.assetType = assetType + pair.challenge.fullLen = getFileSize( + expandPath(assetType, file)).int32 + pair.file = asset.contents + myAssets.add pair + + echo "Zone has ", myAssets.len, " associated assets" + + dirServer = newServer() + + dirServer.addHandler HDsMsg, proc(serv: PServer; buffer: PBuffer) = + var m = readDsMsg(buffer) + dispMessage(" ", m.msg) + dirServer.addHandler HZoneLogin, proc(serv: PServer; buffer: PBuffer) = + let loggedIn = readDsZoneLogin(buffer).status + if loggedIn: + #dirServerConnected = true + + if dirServerInfo.kind == JArray: + var error: string + if not dirServer.connect(dirServerInfo[0].str, dirServerInfo[1].num.int16, error): + dispError(" "&error) + + + server = enet.createHost(address, 32, 2, 0, 0) + if server == nil: + quit "Could not create the server!" + + dispMessage("Listening on port ", address.port) + + var + serverRunning = true + when appType == "gui": + var frameRate = newClock() + var pubChatDelay = newClock() + else: + var frameRate = epochTime() + var pubChatDelay = frameRate + + while serverRunning: + when appType == "gui": + let dt = frameRate.restart.asMilliseconds().float / 1000.0 + + for event in window.filterEvents(): + case event.kind + of sfml.EvtClosed: + window.close() + serverRunning = false + else: + discard + else: + let dt = epochTime() - frameRate ##is this right? probably not + frameRate = epochTime() + + while server.hostService(event, 10) > 0: + case event.kind + of EvtConnect: + var client = newClient() + clients[client.id] = client + + event.peer.data = addr client.id + client.peer = event.peer + + dispMessage("New client connected ", client) + + var + msg = "hello" + resp = createPacket(cstring(msg), msg.len + 1, FlagReliable) + + if event.peer.send(0.cuchar, resp) < 0: + echo "FAILED" + else: + echo "Replied" + of EvtReceive: + let client = clients[cast[ptr int32](event.peer.data)[]] + + var buf = newBuffer(event.packet) + let k = buf.readChar() + if handlers.hasKey(k): + handlers[k](client, buf) + else: + dispError("Unknown packet from ", client) + + destroy(event.packet) + of EvtDisconnect: + var + id = cast[ptr int32](event.peer.data)[] + client = clients[id] + if client.isNil: + disperror("CLIENT IS NIL!") + dispmessage(event.peer.data.isNil) + else: + dispMessage(clients[id], " disconnected") + GCUnref(clients[id]) + clients.del id + + event.peer.data = nil + else: + discard + + when appType == "gui": + fpsText.setString(ff(1.0/dt)) + if pubChatDelay.getElapsedTime.asSeconds > 0.25: + pubChatDelay.restart() + flushPubChat() + else: + pubChatDelay -= dt + if frameRate - pubChatDelay > 0.25: + flushPubChat() + + when appType == "gui": + window.clear(Black) + window.draw(GUI) + window.draw chatbox + window.draw mousePos + window.draw fpstext + window.display() + + server.destroy() + enetDeinit() \ No newline at end of file diff --git a/tests/manyloc/keineschweine/enet_server/nakefile.nim b/tests/manyloc/keineschweine/enet_server/nakefile.nim new file mode 100644 index 000000000..1ca93a340 --- /dev/null +++ b/tests/manyloc/keineschweine/enet_server/nakefile.nim @@ -0,0 +1,13 @@ +import nake +nakeimports + +const + ServerDefines = "-d:NoSFML --forceBuild" + +task "server", "build the server": + if shell("nimrod", ServerDefines, "-r", "compile", "enet_server") != 0: + quit "Failed to build" +task "gui", "build the server GUI mode": + if shell("nimrod", "--app:gui", ServerDefines, "-r", "compile", "enet_server") != 0: + quit "Failed to build" + diff --git a/tests/manyloc/keineschweine/enet_server/nimrod.cfg b/tests/manyloc/keineschweine/enet_server/nimrod.cfg new file mode 100644 index 000000000..72ef47ee0 --- /dev/null +++ b/tests/manyloc/keineschweine/enet_server/nimrod.cfg @@ -0,0 +1,9 @@ +path = ".." +path = "../dependencies/sfml" +path = "../dependencies/enet" +path = "../dependencies/nake" +path = "../dependencies/genpacket" +path = "../lib" +define = "noChipmunk" +define = "noSFML" + diff --git a/tests/manyloc/keineschweine/enet_server/server_settings.json b/tests/manyloc/keineschweine/enet_server/server_settings.json new file mode 100644 index 000000000..7d2f1d822 --- /dev/null +++ b/tests/manyloc/keineschweine/enet_server/server_settings.json @@ -0,0 +1,8 @@ +{ + "name": "Alpha Zone", + "desc": "Beta Testing", + "host": "localhost", + "port": 8024, + "settings": "alphazone.json", + "dirserver":["localhost",2049,"alphazone","skittles"] +} diff --git a/tests/manyloc/keineschweine/enet_server/server_utils.nim b/tests/manyloc/keineschweine/enet_server/server_utils.nim new file mode 100644 index 000000000..8e8141075 --- /dev/null +++ b/tests/manyloc/keineschweine/enet_server/server_utils.nim @@ -0,0 +1,120 @@ +import enet, sg_packets, estreams, md5, zlib_helpers, client_helpers, strutils, + idgen, sg_assets, tables, os +type + PClient* = ref object + id*: int32 + auth*: bool + alias*: string + peer*: PPeer + + FileChallengePair* = tuple[challenge: ScFileChallenge; file: TChecksumFile] + PFileChallengeSequence* = ref TFileChallengeSequence + TFileChallengeSequence = object + index: int #which file is active + transfer: ScFileTransfer + file: ptr FileChallengePair +var + clientID = newIdGen[int32]() + myAssets*: seq[FileChallengePair] = @[] + fileChallenges = initTable[int32, PFileChallengeSequence](32) +const FileChunkSize = 256 + +proc free(client: PClient) = + if client.id != 0: + fileChallenges.del client.id + clientID.del client.id +proc newClient*(): PClient = + new(result, free) + result.id = clientID.next() + result.alias = "billy" + +proc `$`*(client: PClient): string = + result = "$1:$2".format(client.id, client.alias) + +proc send*[T](client: PClient; pktType: char; pkt: var T) = + var buf = newBuffer(128) + buf.write pktType + buf.pack pkt + discard client.peer.send(0.cuchar, buf, flagReliable) + +proc sendMessage*(client: PClient; txt: string) = + var m = newScChat(CSystem, text = txt) + client.send HChat, m +proc sendError*(client: PClient; error: string) = + var m = newScChat(CError, text = error) + client.send HChat, m + + + + +proc next*(challenge: PFileChallengeSequence, client: PClient) +proc sendChunk*(challenge: PFileChallengeSequence, client: PClient) + +proc startVerifyingFiles*(client: PClient) = + var fcs: PFileChallengeSequence + new(fcs) + fcs.index = -1 + fileChallenges[client.id] = fcs + next(fcs, client) + +proc next*(challenge: PFileChallengeSequence, client: PClient) = + inc(challenge.index) + if challenge.index >= myAssets.len: + client.sendMessage "You are cleared to enter" + fileChallenges.del client.id + return + else: + echo myAssets.len, "assets" + challenge.file = addr myAssets[challenge.index] + client.send HFileChallenge, challenge.file.challenge # :rolleyes: + echo "sent challenge" + +proc sendChunk*(challenge: PFileChallengeSequence, client: PClient) = + let size = min(FileChunkSize, challenge.transfer.fileSize - challenge.transfer.pos) + challenge.transfer.data.setLen size + copyMem( + addr challenge.transfer.data[0], + addr challenge.file.file.compressed[challenge.transfer.pos], + size) + client.send HFileTransfer, challenge.transfer + echo "chunk sent" + +proc startSend*(challenge: PFileChallengeSequence, client: PClient) = + challenge.transfer.fileSize = challenge.file.file.compressed.len().int32 + challenge.transfer.pos = 0 + challenge.transfer.data = "" + challenge.transfer.data.setLen FileChunkSize + challenge.sendChunk(client) + echo "starting xfer" + +## HFileTransfer +proc handleFilePartAck*(client: PClient; buffer: PBuffer) = + echo "got filepartack" + var + ftrans = readCsFilepartAck(buffer) + fcSeq = fileChallenges[client.id] + fcSeq.transfer.pos = ftrans.lastPos + fcSeq.sendChunk client + +## HFileCHallenge +proc handleFileChallengeResp*(client: PClient; buffer: PBuffer) = + echo "got file challenge resp" + var + fcResp = readCsFileChallenge(buffer) + fcSeq = fileChallenges[client.id] + let index = $(fcSeq.index + 1) / $(myAssets.len) + if fcResp.needFile: + client.sendMessage "Sending file... "&index + fcSeq.startSend(client) + else: + var resp = newScChallengeResult(false) + if fcResp.checksum == fcSeq.file.file.sum: ##client is good + client.sendMessage "Checksum is good. "&index + resp.status = true + client.send HChallengeResult, resp + fcSeq.next(client) + else: + client.sendMessage "Checksum is bad, sending file... "&index + client.send HChallengeResult, resp + fcSeq.startSend(client) + diff --git a/tests/manyloc/keineschweine/keineschweine.nim b/tests/manyloc/keineschweine/keineschweine.nim new file mode 100644 index 000000000..e868b96a5 --- /dev/null +++ b/tests/manyloc/keineschweine/keineschweine.nim @@ -0,0 +1,725 @@ +import + os, math, strutils, gl, tables, + sfml, sfml_audio, sfml_colors, chipmunk, math_helpers, + input_helpers, animations, game_objects, sfml_stuff, map_filter, + sg_gui, sg_assets, sound_buffer, enet_client +when defined(profiler): + import nimprof +{.deadCodeElim: on.} +type + PPlayer* = ref TPlayer + TPlayer* = object + id: uint16 + vehicle: PVehicle + spectator: bool + alias: string + nameTag: PText + items: seq[PItem] + PVehicle* = ref TVehicle + TVehicle* = object + body*: chipmunk.PBody + shape*: chipmunk.PShape + record*: PVehicleRecord + sprite*: PSprite + spriteRect*: TIntRect + occupant: PPlayer + when false: + position*: TVector2f + velocity*: TVector2f + angle*: float + PItem* = ref object + record: PItemRecord + cooldown: float + PLiveBullet* = ref TLiveBullet ##represents a live bullet in the arena + TLiveBullet* = object + lifetime*: float + dead: bool + anim*: PAnimation + record*: PBulletRecord + fromPlayer*: PPlayer + trailDelay*: float + body: chipmunk.PBody + shape: chipmunk.PShape +import vehicles +const + LGrabbable* = (1 shl 0).TLayers + LBorders* = (1 shl 1).TLayers + LPlayer* = ((1 shl 2) and LBorders.int).TLayers + LEnemy* = ((1 shl 4) and LBorders.int).TLayers + LEnemyFire* = (LPlayer).TLayers + LPlayerFire* = (LEnemy).TLayers + CTBullet = 1.TCollisionType + CTVehicle= 2.TCollisionType + ##temporary constants + W_LIMIT = 2.3 + V_LIMIT = 35 + MaxLocalBots = 3 +var + localPlayer: PPlayer + localBots: seq[PPlayer] = @[] + activeVehicle: PVehicle + myVehicles: seq[PVehicle] = @[] + objects: seq[PGameObject] = @[] + liveBullets: seq[PLiveBullet] = @[] + explosions: seq[PAnimation] = @[] + gameRunning = true + frameRate = newClock() + showStars = off + levelArea: TIntRect + videoMode: TVideoMode + window: PRenderWindow + worldView: PView + guiView: PView + space = newSpace() + ingameClient = newKeyClient("ingame") + specInputClient = newKeyClient("spec") + specGui = newGuiContainer() + stars: seq[PSpriteSheet] = @[] + playBtn: PButton + shipSelect = newGuiContainer() + delObjects: seq[int] = @[] + showShipSelect = false + myPosition: array[0..1, TVector3f] ##for audio positioning +let + nameTagOffset = vec2f(0.0, 1.0) +when defined(escapeMenuTest): + import browsers + var + escMenu = newGuiContainer(vec2f(100, 100)) + escMenuOpen = false + pos = vec2f(0, 0) + escMenu.newButton("Some Website", pos, proc(b: PButton) = + openDefaultBrowser(getClientSettings().website)) + pos.y += 20.0 + escMenu.newButton("Back to Lobby", pos, proc(b: PButton) = + echo "herro") + proc toggleEscape() = + escMenuOpen = not escMenuOpen + ingameClient.registerHandler(KeyEscape, down, toggleEscape) + specInputClient.registerHandler(KeyEscape, down, toggleEscape) +when defined(foo): + var mouseSprite: sfml.PCircleShape +when defined(recordMode): + var + snapshots: seq[PImage] = @[] + isRecording = false + proc startRecording() = + if snapshots.len > 100: return + echo "Started recording" + isRecording = true + proc stopRecording() = + if isRecording: + echo "Stopped recording. ", snapshots.len, " images." + isRecording = false + proc zeroPad*(s: string; minLen: int): string = + if s.len < minLen: + result = repeatChar(minLen - s.len, '0') + result.add s + else: + result = s + var + recordButton = newButton( + nil, text = "Record", position = vec2f(680, 50), + onClick = proc(b: PButton) = startRecording()) + +proc newNameTag*(text: string): PText = + result = newText() + result.setFont(guiFont) + result.setCharacterSize(14) + result.setColor(Red) + result.setString(text) + +var debugText = newNameTag("Loading...") +debugText.setPosition(vec2f(0.0, 600.0 - 50.0)) + +when defined(showFPS): + var fpsText = newNameTag("0") + #fpsText.setCharacterSize(16) + fpsText.setPosition(vec2f(300.0, (800 - 50).float)) + +proc mouseToSpace*(): TVector = + result = window.convertCoords(vec2i(getMousePos()), worldView).sfml2cp() + +proc explode*(b: PLiveBullet) +## TCollisionBeginFunc +proc collisionBulletPlayer(arb: PArbiter; space: PSpace; + data: pointer): Bool{.cdecl.} = + var + bullet = cast[PLiveBullet](arb.a.data) + target = cast[PVehicle](arb.b.data) + if target.occupant.isNil or target.occupant == bullet.fromPlayer: return + bullet.explode() + +proc angularDampingSim(body: PBody, gravity: TVector, damping, dt: CpFloat){.cdecl.} = + body.w -= (body.w * 0.98 * dt) + body.updateVelocity(gravity, damping, dt) + +proc initLevel() = + loadAllAssets() + + if not space.isNil: space.destroy() + space = newSpace() + space.addCollisionHandler CTBullet, CTVehicle, collisionBulletPlayer, + nil, nil, nil, nil + + let levelSettings = getLevelSettings() + levelArea.width = levelSettings.size.x + levelArea.height= levelSettings.size.y + let borderSeq = @[ + vector(0, 0), vector(levelArea.width.float, 0.0), + vector(levelArea.width.float, levelArea.height.float), vector(0.0, levelArea.height.float)] + for i in 0..3: + var seg = space.addShape( + newSegmentShape( + space.staticBody, + borderSeq[i], + borderSeq[(i + 1) mod 4], + 8.0)) + seg.setElasticity 0.96 + seg.setLayers(LBorders) + if levelSettings.starfield.len > 0: + showStars = true + for sprite in levelSettings.starfield: + sprite.tex.setRepeated(true) + sprite.sprite.setTextureRect(levelArea) + sprite.sprite.setOrigin(vec2f(0, 0)) + stars.add(sprite) + var pos = vec2f(0.0, 0.0) + for veh in playableVehicles(): + shipSelect.newButton( + veh.name, + position = pos, + onClick = proc(b: PButton) = + echo "-__-") + pos.y += 18.0 + + +proc newItem*(record: PItemRecord): PItem = + new(result) + result.record = record +proc newItem*(name: string): PItem {.inline.} = + return newItem(fetchItm(name)) +proc canUse*(itm: PItem): bool = + if itm.cooldown > 0.0: return + return true +proc update*(itm: PItem; dt: float) = + if itm.cooldown > 0: + itm.cooldown -= dt + +proc free(obj: PLiveBullet) = + obj.shape.free + obj.body.free + obj.record = nil + + +template newExplosion(obj, animation): stmt = + explosions.add(newAnimation(animation, AnimOnce, obj.body.getPos.cp2sfml, obj.body.getAngle)) +template newExplosion(obj, animation, angle): stmt = + explosions.add(newAnimation(animation, AnimOnce, obj.body.getPos.cp2sfml, angle)) + +proc explode*(b: PLiveBullet) = + if b.dead: return + b.dead = true + space.removeShape b.shape + space.removeBody b.body + if not b.record.explosion.anim.isNil: + newExplosion(b, b.record.explosion.anim) + playSound(b.record.explosion.sound, b.body.getPos()) + +proc bulletUpdate(body: PBody, gravity: TVector, damping, dt: CpFloat){.cdecl.} = + body.updateVelocity(gravity, damping, dt) + +template getPhysical() {.immediate.} = + result.body = space.addBody(newBody( + record.physics.mass, + record.physics.moment)) + result.shape = space.addShape( + chipmunk.newCircleShape( + result.body, + record.physics.radius, + vectorZero)) + +proc newBullet*(record: PBulletRecord; fromPlayer: PPlayer): PLiveBullet = + new(result, free) + result.anim = newAnimation(record.anim, AnimLoop) + result.fromPlayer = fromPlayer + result.lifetime = record.lifetime + result.record = record + getPhysical() + if fromPlayer == localPlayer: + result.shape.setLayers(LPlayerFire) + else: + result.shape.setLayers(LEnemyFire) + result.shape.setCollisionType CTBullet + result.shape.setUserData(cast[ptr TLiveBullet](result)) + let + fireAngle = fromPlayer.vehicle.body.getAngle() + fireAngleV = vectorForAngle(fireAngle) + result.body.setAngle fireAngle + result.body.setPos(fromPlayer.vehicle.body.getPos() + (fireAngleV * fromPlayer.vehicle.shape.getCircleRadius())) + #result.body.velocityFunc = bulletUpdate + result.body.setVel((fromPlayer.vehicle.body.getVel() * record.inheritVelocity) + (fireAngleV * record.baseVelocity)) + +proc update*(b: PLiveBullet; dt: float): bool = + if b.dead: return true + b.lifetime -= dt + b.anim.next(dt) + #b.anim.sprite.setPosition(b.body.getPos.floor()) + b.anim.setPos(b.body.getPos) + b.anim.setAngle(b.body.getAngle()) + if b.lifetime <= 0.0: + b.explode() + return true + b.trailDelay -= dt + if b.trailDelay <= 0.0: + b.trailDelay += b.record.trail.timer + if b.record.trail.anim.isNil: return + newExplosion(b, b.record.trail.anim) +proc draw*(window: PRenderWindow; b: PLiveBullet) {.inline.} = + draw(window, b.anim.sprite) + + +proc free*(veh: PVehicle) = + ("Destroying vehicle "& veh.record.name).echo + destroy(veh.sprite) + if veh.shape.isNil: "Free'd vehicle's shape was NIL!".echo + else: space.removeShape(veh.shape) + if veh.body.isNil: "Free'd vehicle's BODY was NIL!".echo + else: space.removeBody(veh.body) + veh.body.free() + veh.shape.free() + veh.sprite = nil + veh.body = nil + veh.shape = nil + + +proc newVehicle*(record: PVehicleRecord): PVehicle = + echo("Creating "& record.name) + new(result, free) + result.record = record + result.sprite = result.record.anim.spriteSheet.sprite.copy() + result.spriteRect = result.sprite.getTextureRect() + getPhysical() + result.body.setAngVelLimit W_LIMIT + result.body.setVelLimit result.record.handling.topSpeed + result.body.velocityFunc = angularDampingSim + result.shape.setCollisionType CTVehicle + result.shape.setUserData(cast[ptr TVehicle](result)) +proc newVehicle*(name: string): PVehicle = + result = newVehicle(fetchVeh(name)) + +proc update*(obj: PVehicle) = + obj.sprite.setPosition(obj.body.getPos.floor) + obj.spriteRect.left = (((-obj.body.getAngVel + W_LIMIT) / (W_LIMIT*2.0) * (obj.record.anim.spriteSheet.cols - 1).float).floor.int * obj.record.anim.spriteSheet.framew).cint + obj.spriteRect.top = ((obj.offsetAngle.wmod(TAU) / TAU) * obj.record.anim.spriteSheet.rows.float).floor.cint * obj.record.anim.spriteSheet.frameh.cint + obj.sprite.setTextureRect(obj.spriteRect) + + +proc newPlayer*(alias: string = "poo"): PPlayer = + new(result) + result.spectator = true + result.alias = alias + result.nameTag = newNameTag(result.alias) + result.items = @[] +proc updateItems*(player: PPlayer, dt: float) = + for i in items(player.items): + update(i, dt) +proc addItem*(player: PPlayer; name: string) = + player.items.add newItem(name) +proc useItem*(player: PPlayer; slot: int) = + if slot > player.items.len - 1: return + let item = player.items[slot] + if item.canUse: + item.cooldown += item.record.cooldown + let b = newBullet(item.record.bullet, player) + liveBullets.add(b) + sound_buffer.playSound(item.record.useSound, b.body.getPos) + +proc update*(obj: PPlayer) = + if not obj.spectator: + obj.vehicle.update() + obj.nameTag.setPosition(obj.vehicle.body.getPos.floor + (nameTagOffset * (obj.vehicle.record.physics.radius + 5).cfloat)) + +proc draw(window: PRenderWindow, player: PPlayer) {.inline.} = + if not player.spectator: + if player.vehicle != nil: + window.draw(player.vehicle.sprite) + window.draw(player.nameTag) + +proc setVehicle(p: PPlayer; v: PVehicle) = + p.vehicle = v #sorry mom, this is just how things worked out ;( + if not v.isNil: + v.occupant = p + +proc createBot() = + if localBots.len < MaxLocalBots: + var bot = newPlayer("Dodo Brown") + bot.setVehicle(newVehicle("Turret0")) + if bot.isNil: + echo "BOT IS NIL" + return + elif bot.vehicle.isNil: + echo "BOT VEH IS NIL" + return + localBots.add(bot) + bot.vehicle.body.setPos(vector(100, 100)) + echo "new bot at ", $bot.vehicle.body.getPos() + +var inputCursor = newVertexArray(sfml.Lines, 2) +inputCursor[0].position = vec2f(10.0, 10.0) +inputCursor[1].position = vec2f(50.0, 90.0) + +proc hasVehicle(p: PPlayer): bool {.inline.} = + result = not p.spectator and not p.vehicle.isNil + +proc setMyVehicle(v: PVehicle) {.inline.} = + activeVehicle = v + localPlayer.setVehicle v + +proc unspec() = + var veh = newVehicle("Turret0") + if not veh.isNil: + setMyVehicle veh + localPlayer.spectator = false + ingameClient.setActive + veh.body.setPos vector(100, 100) + veh.shape.setLayers(LPlayer) + when defined(debugWeps): + localPlayer.addItem("Mass Driver") + localPlayer.addItem("Neutron Bomb") + localPlayer.additem("Dem Lasers") + localPlayer.addItem("Mold Spore Beam") + localPlayer.addItem("Genericorp Mine") + localPlayer.addItem("Gravitic Bomb") +proc spec() = + setMyVehicle nil + localPlayer.spectator = true + specInputClient.setActive + +var + specLimiter = newClock() + timeBetweenSpeccing = 1.0 #seconds +proc toggleSpec() {.inline.} = + if specLimiter.getElapsedTime.asSeconds < timeBetweenSpeccing: + return + specLimiter.restart() + if localPlayer.isNil: + echo("OMG WTF PLAYER IS NILL!!") + elif localPlayer.spectator: unspec() + else: spec() + +proc addObject*(name: string) = + var o = newObject(name) + if not o.isNil: + echo "Adding object ", o + discard space.addBody(o.body) + discard space.addShape(o.shape) + o.shape.setLayers(LGrabbable) + objects.add(o) +proc explode(obj: PGameObject) = + echo obj, " exploded" + let ind = objects.find(obj) + if ind != -1: + delObjects.add ind +proc update(obj: PGameObject; dt: float) = + if not(obj.anim.next(dt)): + obj.explode() + else: + obj.anim.setPos(obj.body.getPos) + obj.anim.setAngle(obj.body.getAngle) + +proc toggleShipSelect() = + showShipSelect = not showShipSelect +proc handleLClick() = + let pos = input_helpers.getMousePos() + when defined(escapeMenuTest): + if escMenuOpen: + escMenu.click(pos) + return + if showShipSelect: + shipSelect.click(pos) + if localPlayer.spectator: + specGui.click(pos) + +ingameClient.registerHandler(KeyF12, down, proc() = toggleSpec()) +ingameClient.registerHandler(KeyF11, down, toggleShipSelect) +ingameClient.registerHandler(MouseLeft, down, handleLClick) +when defined(recordMode): + if not existsDir("data/snapshots"): + createDir("data/snapshots") + ingameClient.registerHandler(keynum9, down, proc() = + if not isRecording: startRecording() + else: stopRecording()) + ingameClient.registerHandler(keynum0, down, proc() = + if snapshots.len > 0 and not isRecording: + echo "Saving images (LOL)" + for i in 0..high(snapshots): + if not(snapshots[i].save("data/snapshots/image"&(zeroPad($i, 3))&".jpg")): + echo "Could not save" + snapshots[i].destroy() + snapshots.setLen 0) +when defined(DebugKeys): + ingameClient.registerHandler MouseRight, down, proc() = + echo($activevehicle.body.getAngle.vectorForAngle()) + ingameClient.registerHandler KeyBackslash, down, proc() = + createBot() + ingameClient.registerHandler(KeyNum1, down, proc() = + if localPlayer.items.len == 0: + localPlayer.addItem("Mass Driver") + echo "Gave you a mass driverz") + ingameClient.registerHandler(KeyL, down, proc() = + echo("len(livebullets) = ", len(livebullets))) + ingameClient.registerHandler(KeyRShift, down, proc() = + if keyPressed(KeyR): + echo("Friction: ", ff(activeVehicle.shape.getFriction())) + echo("Damping: ", ff(space.getDamping())) + elif keypressed(KeyM): + echo("Mass: ", activeVehicle.body.getMass.ff()) + echo("Moment: ", activeVehicle.body.getMoment.ff()) + elif keypressed(KeyI): + echo(repr(activeVehicle.record)) + elif keyPressed(KeyH): + activeVehicle.body.setPos(vector(100.0, 100.0)) + activeVehicle.body.setVel(vectorZero) + elif keyPressed(KeyComma): + activeVehicle.body.setPos mouseToSpace()) + ingameClient.registerHandler(KeyY, down, proc() = + const looloo = ["Asteroid1", "Asteroid2"] + addObject(looloo[random(looloo.len)])) + ingameClient.registerHandler(KeyO, down, proc() = + if objects.len == 0: + echo "Objects is empty" + return + for i, o in pairs(objects): + echo i, " ", o) + ingameClient.registerHandler(KeyLBracket, down, sound_buffer.report) + var + mouseJoint: PConstraint + mouseBody = space.addBody(newBody(CpInfinity, CpInfinity)) + ingameClient.registerHandler(MouseMiddle, down, proc() = + var point = mouseToSpace() + var shape = space.pointQueryFirst(point, LGrabbable, 0) + if not mouseJoint.isNil: + space.removeConstraint mouseJoint + mouseJoint.destroy() + mouseJoint = nil + if shape.isNil: + return + let body = shape.getBody() + mouseJoint = space.addConstraint( + newPivotJoint(mouseBody, body, vectorZero, body.world2local(point))) + mouseJoint.maxForce = 50000.0 + mouseJoint.errorBias = pow(1.0 - 0.15, 60)) + +var specCameraSpeed = 5.0 +specInputClient.registerHandler(MouseLeft, down, handleLClick) +specInputClient.registerHandler(KeyF11, down, toggleShipSelect) +specInputClient.registerHandler(KeyF12, down, proc() = toggleSpec()) +specInputClient.registerHandler(KeyLShift, down, proc() = specCameraSpeed *= 2) +specInputClient.registerHandler(KeyLShift, up, proc() = specCameraSpeed /= 2) + +specInputClient.registerHandler(KeyP, down, proc() = + echo("addObject(solar mold)") + addObject("Solar Mold")) + +proc resetForcesCB(body: PBody; data: pointer) {.cdecl.} = + body.resetForces() + +var frameCount= 0 +proc mainUpdate(dt: float) = + if localPlayer.spectator: + if keyPressed(KeyLeft): + worldView.move(vec2f(-1.0, 0.0) * specCameraSpeed) + elif keyPressed(KeyRight): + worldView.move(vec2f( 1.0, 0.0) * specCameraSpeed) + if keyPressed(KeyUp): + worldView.move(vec2f(0.0, -1.0) * specCameraSpeed) + elif keyPressed(KeyDown): + worldView.move(vec2f(0.0, 1.0) * specCameraSpeed) + elif not activeVehicle.isNil: + if keyPressed(KeyUp): + activeVehicle.accel(dt) + elif keyPressed(keyDown): + activeVehicle.reverse(dt) + if keyPressed(KeyRight): + activeVehicle.turn_right(dt) + elif keyPressed(KeyLeft): + activeVehicle.turn_left(dt) + if keyPressed(keyz): + activeVehicle.strafe_left(dt) + elif keyPressed(keyx): + activeVehicle.strafe_right(dt) + if keyPressed(KeyLControl): + localPlayer.useItem 0 + if keyPressed(KeyTab): + localPlayer.useItem 1 + if keyPressed(KeyQ): + localPlayer.useItem 2 + if keyPressed(KeyW): + localPlayer.useItem 3 + if Keypressed(keyA): + localPlayer.useItem 4 + if keyPressed(sfml.KeyS): + localPlayer.useItem 5 + if keyPressed(KeyD): + localPlayer.useItem 6 + worldView.setCenter(activeVehicle.body.getPos.floor)#cp2sfml) + + if localPlayer != nil: + localPlayer.update() + localPlayer.updateItems(dt) + for b in localBots: + b.update() + + for o in items(objects): + o.update(dt) + for i in countdown(high(delObjects), 0): + objects.del i + delObjects.setLen 0 + + var i = 0 + while i < len(liveBullets): + if liveBullets[i].update(dt): + liveBullets.del i + else: + inc i + i = 0 + while i < len(explosions): + if explosions[i].next(dt): inc i + else: explosions.del i + + when defined(DebugKeys): + mouseBody.setPos(mouseToSpace()) + + space.step(dt) + space.eachBody(resetForcesCB, nil) + + when defined(foo): + var coords = window.convertCoords(vec2i(getMousePos()), worldView) + mouseSprite.setPosition(coords) + + if localPlayer != nil and localPlayer.vehicle != nil: + let + pos = localPlayer.vehicle.body.getPos() + ang = localPlayer.vehicle.body.getAngle.vectorForAngle() + myPosition[0].x = pos.x + myPosition[0].z = pos.y + myPosition[1].x = ang.x + myPosition[1].z = ang.y + listenerSetPosition(myPosition[0]) + listenerSetDirection(myPosition[1]) + + inc frameCount + when defined(showFPS): + if frameCount mod 60 == 0: + fpsText.setString($(1.0/dt).round) + if frameCount mod 250 == 0: + updateSoundBuffer() + frameCount = 0 + +proc mainRender() = + window.clear(Black) + window.setView(worldView) + + if showStars: + for star in stars: + window.draw(star.sprite) + window.draw(localPlayer) + + for b in localBots: + window.draw(b) + for o in objects: + window.draw(o) + + for b in explosions: window.draw(b) + for b in liveBullets: window.draw(b) + + when defined(Foo): + window.draw(mouseSprite) + + window.setView(guiView) + + when defined(EscapeMenuTest): + if escMenuOpen: + window.draw escMenu + when defined(showFPS): + window.draw(fpsText) + when defined(recordMode): + window.draw(recordButton) + + if localPlayer.spectator: + window.draw(specGui) + if showShipSelect: window.draw shipSelect + window.display() + + when defined(recordMode): + if isRecording: + if snapshots.len < 100: + if frameCount mod 5 == 0: + snapshots.add(window.capture()) + else: stopRecording() + +proc readyMainState() = + specInputClient.setActive() + +when isMainModule: + import parseopt + + localPlayer = newPlayer() + LobbyInit() + + videoMode = getClientSettings().resolution + window = newRenderWindow(videoMode, "sup", sfDefaultStyle) + window.setFrameRateLimit 60 + + worldView = window.getView.copy() + guiView = worldView.copy() + shipSelect.setPosition vec2f(665.0, 50.0) + + when defined(foo): + mouseSprite = sfml.newCircleShape(14) + mouseSprite.setFillColor Transparent + mouseSprite.setOutlineColor RoyalBlue + mouseSprite.setOutlineThickness 1.4 + mouseSprite.setOrigin vec2f(14, 14) + + LobbyReady() + playBtn = specGui.newButton( + "Unspec - F12", position = vec2f(680.0, 8.0), onClick = proc(b: PButton) = + toggleSpec()) + + block: + var bPlayOffline = false + for kind, key, val in getOpt(): + case kind + of cmdArgument: + if key == "offline": bPlayOffline = true + else: + echo "Invalid argument ", key, " ", val + if bPlayOffline: + playoffline(nil) + + gameRunning = true + while gameRunning: + for event in window.filterEvents: + if event.kind == EvtClosed: + gameRunning = false + break + elif event.kind == EvtMouseWheelMoved and getActiveState() == Field: + if event.mouseWheel.delta == 1: + worldView.zoom(0.9) + else: + worldView.zoom(1.1) + let dt = frameRate.restart.asMilliSeconds().float / 1000.0 + case getActiveState() + of Field: + mainUpdate(dt) + mainRender() + of Lobby: + lobbyUpdate(dt) + lobbyDraw(window) + else: + initLevel() + echo("Done? lol") + doneWithSaidTransition() + readyMainState() diff --git a/tests/manyloc/keineschweine/keineschweine.nimrod.cfg b/tests/manyloc/keineschweine/keineschweine.nimrod.cfg new file mode 100644 index 000000000..048285ad9 --- /dev/null +++ b/tests/manyloc/keineschweine/keineschweine.nimrod.cfg @@ -0,0 +1,8 @@ +path = "lib" +path = "dependencies/sfml" +path = "dependencies/chipmunk" +path = "dependencies/nake" +path = "dependencies/enet" +path = "dependencies/genpacket" +path = "enet_server" +debugger = off diff --git a/tests/manyloc/keineschweine/lib/animations.nim b/tests/manyloc/keineschweine/lib/animations.nim new file mode 100644 index 000000000..a021005f0 --- /dev/null +++ b/tests/manyloc/keineschweine/lib/animations.nim @@ -0,0 +1,75 @@ +import + math, + sfml, chipmunk, + sg_assets, sfml_stuff, math_helpers +type + PAnimation* = ref TAnimation + TAnimation* = object + sprite*: PSprite + record*: PAnimationRecord + delay*: float + index*: int + direction*: int + spriteRect*: TIntRect + style*: TAnimationStyle + TAnimationStyle* = enum + AnimLoop = 0'i8, AnimBounce, AnimOnce + +proc setPos*(obj: PAnimation; pos: TVector) {.inline.} +proc setPos*(obj: PAnimation; pos: TVector2f) {.inline.} +proc setAngle*(obj: PAnimation; radians: float) {.inline.} + +proc free*(obj: PAnimation) = + obj.sprite.destroy() + obj.record = nil + +proc newAnimation*(src: PAnimationRecord; style: TAnimationStyle): PAnimation = + new(result, free) + result.sprite = src.spriteSheet.sprite.copy() + result.record = src + result.delay = src.delay + result.index = 0 + result.direction = 1 + result.spriteRect = result.sprite.getTextureRect() + result.style = style +proc newAnimation*(src: PAnimationRecord; style: TAnimationStyle; + pos: TVector2f; angle: float): PAnimation = + result = newAnimation(src, style) + result.setPos pos + setAngle(result, angle) + +proc next*(obj: PAnimation; dt: float): bool {.discardable.} = + ## step the animation. Returns false if the object is out of frames + result = true + obj.delay -= dt + if obj.delay <= 0.0: + obj.delay += obj.record.delay + obj.index += obj.direction + #if obj.index > (obj.record.spriteSheet.cols - 1) or obj.index < 0: + if not(obj.index in 0..(obj.record.spriteSheet.cols - 1)): + case obj.style + of AnimOnce: + return false + of AnimBounce: + obj.direction *= -1 + obj.index += obj.direction * 2 + of AnimLoop: + obj.index = 0 + obj.spriteRect.left = obj.index.cint * obj.record.spriteSheet.frameW.cint + obj.sprite.setTextureRect obj.spriteRect + +proc setPos*(obj: PAnimation; pos: TVector) = + setPosition(obj.sprite, pos.floor()) +proc setPos*(obj: PAnimation; pos: TVector2f) = + setPosition(obj.sprite, pos) +proc setAngle*(obj: PAnimation; radians: float) = + let rads = (radians + obj.record.angle).wmod(TAU) + if obj.record.spriteSheet.rows > 1: + ## (rotation percent * rows).floor * frameheight + obj.spriteRect.top = (rads / TAU * obj.record.spriteSheet.rows.float).floor.cint * obj.record.spriteSheet.frameh.cint + obj.sprite.setTextureRect obj.spriteRect + else: + setRotation(obj.sprite, degrees(rads)) #stupid sfml, who uses degrees these days? -__- + +proc draw*(window: PRenderWindow; obj: PAnimation) {.inline.} = + window.draw(obj.sprite) diff --git a/tests/manyloc/keineschweine/lib/client_helpers.nim b/tests/manyloc/keineschweine/lib/client_helpers.nim new file mode 100644 index 000000000..84e42b62e --- /dev/null +++ b/tests/manyloc/keineschweine/lib/client_helpers.nim @@ -0,0 +1,142 @@ +import + tables, sg_packets, enet, estreams, sg_gui, sfml, + zlib_helpers, md5, sg_assets, os +type + PServer* = ptr TServer + TServer* = object + connected*: bool + addy: enet.TAddress + host*: PHost + peer*: PPeer + handlers*: TTable[char, TScPktHandler] + TScPktHandler* = proc(serv: PServer; buffer: PBuffer) + TFileTransfer = object + fileName: string + assetType: TAssetType + fullLen: int + pos: int32 + data: string + readyToSave: bool +var + currentFileTransfer: TFileTransfer + downloadProgress* = newButton(nil, "", vec2f(0,0), nil) +currentFileTransfer.data = "" + +proc addHandler*(serv: PServer; packetType: char; handler: TScPktHandler) = + serv.handlers[packetType] = handler + +proc newServer*(): PServer = + result = cast[PServer](alloc0(sizeof(TServer))) + result.connected = false + result.host = createHost(nil, 1, 2, 0, 0) + result.handlers = initTable[char, TScPktHandler](32) + +proc connect*(serv: PServer; host: string; port: int16; error: var string): bool = + if setHost(serv.addy, host) != 0: + error = "Could not resolve host " + error.add host + return false + serv.addy.port = port.cushort + serv.peer = serv.host.connect(serv.addy, 2, 0) + if serv.peer.isNil: + error = "Could not connect to host " + error.add host + return false + return true + +proc send*[T](serv: PServer; packetType: char; pkt: var T) = + if serv.connected: + var b = newBuffer(100) + b.write packetType + b.pack pkt + serv.peer.send(0.cuchar, b, FlagUnsequenced) + +proc sendPubChat*(server: PServer; msg: string) = + var chat = newCsChat("", msg) + server.send HChat, chat + +proc handlePackets*(server: PServer; buf: PBuffer) = + while not buf.atEnd(): + let typ = readChar(buf) + if server.handlers.hasKey(typ): + server.handlers[typ](server, buf) + else: + break + +proc updateFileProgress*() = + let progress = currentFileTransfer.pos / currentFileTransfer.fullLen + downloadProgress.bg.setSize(vec2f(progress * 100, 20)) + downloadProgress.setString($currentFileTransfer.pos &'/'& $currentFileTransfer.fullLen) + +## HFileTransfer +proc handleFilePartRecv*(serv: PServer; buffer: PBuffer) {.procvar.} = + var + f = readScFileTransfer(buffer) + updateFileProgress() + if not(f.pos == currentFileTransfer.pos): + echo "returning early from filepartrecv" + return ##issues, probably + if currentFileTransfer.data.len == 0: + echo "setting current file size" + currentFileTransfer.data.setLen f.fileSize + let len = f.data.len + copymem( + addr currentFileTransfer.data[f.pos], + addr f.data[0], + len) + currentFileTransfer.pos = f.pos + len.int32 + if currentFileTransfer.pos == f.fileSize: #file should be done, rizzight + currentFileTransfer.data = uncompress( + currentFileTransfer.data, currentFileTransfer.fullLen) + currentFileTransfer.readyToSave = true + var resp: CsFileChallenge + resp.checksum = toMD5(currentFileTransfer.data) + serv.send HFileChallenge, resp + echo "responded with challenge (ready to save)" + else: + var resp = newCsFilepartAck(currentFileTransfer.pos) + serv.send HFileTransfer, resp + echo "responded for next part" + +proc saveCurrentFile() = + if not currentFileTransfer.readyToSave: return + let + path = expandPath(currentFileTransfer.assetType, currentFileTransfer.fileName) + parent = parentDir(path) + if not existsDir(parent): + createDir(parent) + echo("Created dir") + writeFile path, currentFIleTransfer.data + echo "Write file" + +## HChallengeResult +proc handleFileChallengeResult*(serv: PServer; buffer: PBuffer) {.procvar.} = + var res = readScChallengeResult(buffer).status + echo "got challnege result: ", res + if res and currentFileTransfer.readyToSave: + echo "saving" + saveCurrentFile() + else: + currentFileTransfer.readyToSave = false + currentFileTransfer.pos = 0 + echo "REsetting current file" + +## HFileCHallenge +proc handleFileChallenge*(serv: PServer; buffer: PBuffer) {.procvar.} = + var + challenge = readScFileChallenge(buffer) + path = expandPath(challenge) + resp: CsFileChallenge + if not existsFile(path): + resp.needFile = true + echo "Got file challenge, need file." + else: + resp.checksum = toMD5(readFile(path)) + echo "got file challenge, sending sum" + currentFileTransfer.fileName = challenge.file + currentFileTransfer.assetType = challenge.assetType + currentFileTransfer.fullLen = challenge.fullLen.int + currentFileTransfer.pos = 0 + currentFileTransfer.data.setLen 0 + currentFileTransfer.readyToSave = false + serv.send HFileChallenge, resp diff --git a/tests/manyloc/keineschweine/lib/estreams.nim b/tests/manyloc/keineschweine/lib/estreams.nim new file mode 100644 index 000000000..bd0a2c31a --- /dev/null +++ b/tests/manyloc/keineschweine/lib/estreams.nim @@ -0,0 +1,126 @@ +import endians + +proc swapEndian16*(outp, inp: pointer) = + ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to + ## contain at least 2 bytes. + var i = cast[cstring](inp) + var o = cast[cstring](outp) + o[0] = i[1] + o[1] = i[0] +when cpuEndian == bigEndian: + proc bigEndian16(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 2) +else: + proc bigEndian16*(outp, inp: pointer) {.inline.} = swapEndian16(outp, inp) + +import enet + +type + PBuffer* = ref object + data*: string + pos: int + +proc free(b: PBuffer) = + GCunref b.data +proc newBuffer*(len: int): PBuffer = + new result, free + result.data = newString(len) +proc newBuffer*(pkt: PPacket): PBuffer = + new result, free + result.data = newString(pkt.dataLength) + copyMem(addr result.data[0], pkt.data, pkt.dataLength) +proc toPacket*(buffer: PBuffer; flags: TPacketFlag): PPacket = + buffer.data.setLen buffer.pos + result = createPacket(cstring(buffer.data), buffer.pos, flags) + +proc isDirty*(buffer: PBuffer): bool {.inline.} = + result = (buffer.pos != 0) +proc atEnd*(buffer: PBuffer): bool {.inline.} = + result = (buffer.pos == buffer.data.len) +proc reset*(buffer: PBuffer) {.inline.} = + buffer.pos = 0 + +proc flush*(buf: PBuffer) = + buf.pos = 0 + buf.data.setLen(0) +proc send*(peer: PPeer; channel: cuchar; buf: PBuffer; flags: TPacketFlag): cint {.discardable.} = + result = send(peer, channel, buf.toPacket(flags)) + +proc read*[T: int16|uint16](buffer: PBuffer; outp: var T) = + bigEndian16(addr outp, addr buffer.data[buffer.pos]) + inc buffer.pos, 2 +proc read*[T: float32|int32|uint32](buffer: PBuffer; outp: var T) = + bigEndian32(addr outp, addr buffer.data[buffer.pos]) + inc buffer.pos, 4 +proc read*[T: float64|int64|uint64](buffer: PBuffer; outp: var T) = + bigEndian64(addr outp, addr buffer.data[buffer.pos]) + inc buffer.pos, 8 +proc read*[T: int8|uint8|byte|bool|char](buffer: PBuffer; outp: var T) = + copyMem(addr outp, addr buffer.data[buffer.pos], 1) + inc buffer.pos, 1 + +proc writeBE*[T: int16|uint16](buffer: PBuffer; val: var T) = + setLen buffer.data, buffer.pos + 2 + bigEndian16(addr buffer.data[buffer.pos], addr val) + inc buffer.pos, 2 +proc writeBE*[T: int32|uint32|float32](buffer: PBuffer; val: var T) = + setLen buffer.data, buffer.pos + 4 + bigEndian32(addr buffer.data[buffer.pos], addr val) + inc buffer.pos, 4 +proc writeBE*[T: int64|uint64|float64](buffer: PBuffer; val: var T) = + setLen buffer.data, buffer.pos + 8 + bigEndian64(addr buffer.data[buffer.pos], addr val) + inc buffer.pos, 8 +proc writeBE*[T: char|int8|uint8|byte|bool](buffer: PBuffer; val: var T) = + setLen buffer.data, buffer.pos + 1 + copyMem(addr buffer.data[buffer.pos], addr val, 1) + inc buffer.pos, 1 + + +proc write*(buffer: PBuffer; val: var string) = + var length = len(val).uint16 + writeBE buffer, length + setLen buffer.data, buffer.pos + length.int + copyMem(addr buffer.data[buffer.pos], addr val[0], length.int) + inc buffer.pos, length.int +proc write*[T: TNumber|bool|char|byte](buffer: PBuffer; val: T) = + var v: T + shallowCopy v, val + writeBE buffer, v + +proc readInt8*(buffer: PBuffer): int8 = + read buffer, result +proc readInt16*(buffer: PBuffer): int16 = + read buffer, result +proc readInt32*(buffer: PBuffer): int32 = + read buffer, result +proc readInt64*(buffer: PBuffer): int64 = + read buffer, result +proc readFloat32*(buffer: PBuffer): float32 = + read buffer, result +proc readFloat64*(buffer: PBuffer): float64 = + read buffer, result +proc readStr*(buffer: PBuffer): string = + let len = readInt16(buffer).int + result = "" + if len > 0: + result.setLen len + copyMem(addr result[0], addr buffer.data[buffer.pos], len) + inc buffer.pos, len +proc readChar*(buffer: PBuffer): char {.inline.} = return readInt8(buffer).char +proc readBool*(buffer: PBuffer): bool {.inline.} = return readInt8(buffer).bool + + +when isMainModule: + var b = newBuffer(100) + var str = "hello there" + b.write str + echo(repr(b)) + b.pos = 0 + echo(repr(b.readStr())) + + b.flush() + echo "flushed" + b.writeC([1,2,3]) + echo(repr(b)) + + diff --git a/tests/manyloc/keineschweine/lib/game_objects.nim b/tests/manyloc/keineschweine/lib/game_objects.nim new file mode 100644 index 000000000..37019ebcb --- /dev/null +++ b/tests/manyloc/keineschweine/lib/game_objects.nim @@ -0,0 +1,34 @@ +import chipmunk, sfml, animations, sg_assets +type + PGameObject* = ref TGameObject + TGameObject = object + body*: chipmunk.PBody + shape*: chipmunk.PShape + record*: PObjectRecord + anim*: PAnimation + + +proc `$`*(obj: PGameObject): string = + result = "' +proc free(obj: PGameObject) = + obj.record = nil + free(obj.anim) + obj.anim = nil +proc newObject*(record: PObjectRecord): PGameObject = + if record.isNil: return nil + new(result, free) + result.record = record + result.anim = newAnimation(record.anim, AnimLoop) + when false: + result.sprite = record.anim.spriteSheet.sprite.copy() + result.body = newBody(result.record.physics.mass, 10.0) + result.shape = chipmunk.newCircleShape(result.body, result.record.physics.radius, vectorZero) + result.body.setPos(vector(100, 100)) +proc newObject*(name: string): PGameObject = + result = newObject(fetchObj(name)) +proc draw*(window: PRenderWindow, obj: PGameObject) {.inline.} = + window.draw(obj.anim.sprite) diff --git a/tests/manyloc/keineschweine/lib/idgen.nim b/tests/manyloc/keineschweine/lib/idgen.nim new file mode 100644 index 000000000..8124ba9bd --- /dev/null +++ b/tests/manyloc/keineschweine/lib/idgen.nim @@ -0,0 +1,23 @@ +type + PIDGen*[T: Ordinal] = ref TIDGen[T] + TIDGen*[T: Ordinal] = object + max: T + freeIDs: seq[T] + EOutOfIDs* = object of EInvalidKey + +#proc free[T](idg: PIDgen[T]) = +# result.freeIDs = nil +proc newIDGen*[T: Ordinal](): PIDGen[T] = + new(result)#, free) + result.max = 0.T + result.freeIDs = @[] +proc next*[T](idg: PIDGen[T]): T = + if idg.freeIDs.len > 0: + result = idg.freeIDs.pop + elif idg.max < high(T)-T(1): + inc idg.max + result = idg.max + else: + raise newException(EOutOfIDs, "ID generator hit max value") +proc del*[T](idg: PIDGen[T]; id: T) = + idg.freeIDs.add id diff --git a/tests/manyloc/keineschweine/lib/input_helpers.nim b/tests/manyloc/keineschweine/lib/input_helpers.nim new file mode 100644 index 000000000..120576dfb --- /dev/null +++ b/tests/manyloc/keineschweine/lib/input_helpers.nim @@ -0,0 +1,138 @@ +import + sfml, tables, hashes +type + TKeyEventKind* = enum down, up + TInputFinishedProc* = proc() + TKeyCallback = proc() + PKeyClient* = ref object + onKeyDown: TTable[int32, TKeyCallback] + onKeyUp: TTable[int32, TKeyCallback] + name: string + PTextInput* = ref object + text*: string + cursor: int + onEnter: TInputFinishedProc +var + keyState: array[-MouseButtonCount.int32 .. KeyCount.int32, bool] + mousePos: TVector2f + activeClient: PKeyClient = nil + activeInput: PTextInput = nil + +proc setActive*(client: PKeyClient) = + activeClient = client + echo("** set active client ", client.name) +proc newKeyClient*(name: string = "unnamed", setactive = false): PKeyClient = + new(result) + result.onKeyDown = initTable[int32, TKeyCallback](16) + result.onKeyUp = initTable[int32, TKeyCallback](16) + result.name = name + if setActive: + setActive(result) + +proc keyPressed*(key: TKeyCode): bool {.inline.} = + return keyState[key.int32] +proc buttonPressed*(btn: TMouseButton): bool {.inline.} = + return keyState[-btn.int32] + +proc clearKey*(key: TKeyCode) {.inline.} = + keyState[key.int32] = false +proc clearButton*(btn: TMouseButton) {.inline.} = + keyState[-btn.int32] = false + +proc addKeyEvent*(key: TKeyCode, ev: TKeyEventKind) {.inline.} = + if activeClient.isNil: return + let k = key.int32 + case ev + of down: + keyState[k] = true + if activeClient.onKeyDown.hasKey(k): + activeClient.onKeyDown[k]() + else: + keyState[k] = false + if activeClient.onKeyUp.hasKey(k): + activeClient.onKeyUp[k]() +proc addButtonEvent*(btn: TMouseButton, ev: TKeyEventKind) {.inline.} = + if activeClient.isNil: return + let b = -btn.int32 + case ev + of down: + keyState[b] = true + if activeClient.onKeyDown.hasKey(b): + activeClient.onKeyDown[b]() + else: + keyState[b] = false + if activeClient.onKeyUp.hasKey(b): + activeClient.onKeyUp[b]() +proc registerHandler*(client: PKeyClient; key: TKeyCode; + ev: TKeyEventKind; fn: TKeyCallback) = + case ev + of down: client.onKeyDown[key.int32] = fn + of up: client.onKeyUp[key.int32] = fn +proc registerHandler*(client: PKeyClient; btn: TMouseButton; + ev: TKeyEventKind; fn: TKeyCallback) = + case ev + of down: client.onKeyDown[-btn.int32] = fn + of up: client.onKeyUp[-btn.int32] = fn + +proc newTextInput*(text = "", pos = 0, onEnter: TInputFinishedProc = nil): PTextInput = + new(result) + result.text = text + result.cursor = pos + result.onEnter = onEnter +proc setActive*(i: PTextInput) = + activeInput = i +proc stopCapturingText*() = + activeInput = nil +proc clear*(i: PTextInput) = + i.text.setLen 0 + i.cursor = 0 +proc recordText*(i: PTextInput; c: cint) = + if c > 127 or i.isNil: return + if c in 32..126: ##printable + if i.cursor == i.text.len: i.text.add(c.int.chr) + else: + let rem = i.text.substr(i.cursor) + i.text.setLen(i.cursor) + i.text.add(chr(c.int)) + i.text.add(rem) + inc(i.cursor) + elif c == 8: ## \b backspace + if i.text.len > 0 and i.cursor > 0: + dec(i.cursor) + let rem = i.text.substr(i.cursor + 1) + i.text.setLen(i.cursor) + i.text.add(rem) + elif c == 10 or c == 13:## \n, \r enter + if not i.onEnter.isNil: i.onEnter() +proc recordText*(i: PTextInput; e: TTextEvent) {.inline.} = + recordText(i, e.unicode) + +proc setMousePos*(x, y: cint) {.inline.} = + mousePos.x = x.float + mousePos.y = y.float +proc getMousePos*(): TVector2f {.inline.} = result = mousePos + +var event: TEvent +# Handle and filter input-related events +iterator filterEvents*(window: PRenderWindow): PEvent = + while window.pollEvent(addr event): + case event.kind + of EvtKeyPressed: addKeyEvent(event.key.code, down) + of EvtKeyReleased: addKeyEvent(event.key.code, up) + of EvtMouseButtonPressed: addButtonEvent(event.mouseButton.button, down) + of EvtMouseButtonReleased: addButtonEvent(event.mouseButton.button, up) + of EvtTextEntered: recordText(activeInput, event.text) + of EvtMouseMoved: setMousePos(event.mouseMove.x, event.mouseMove.y) + else: yield(addr event) +# Handle and return input-related events +iterator pollEvents*(window: PRenderWindow): PEvent = + while window.pollEvent(addr event): + case event.kind + of EvtKeyPressed: addKeyEvent(event.key.code, down) + of EvtKeyReleased: addKeyEvent(event.key.code, up) + of EvtMouseButtonPressed: addButtonEvent(event.mouseButton.button, down) + of EvtMouseButtonReleased: addButtonEvent(event.mouseButton.button, up) + of EvtTextEntered: recordText(activeInput, event.text) + of EvtMouseMoved: setMousePos(event.mouseMove.x, event.mouseMove.y) + else: nil + yield(addr event) \ No newline at end of file diff --git a/tests/manyloc/keineschweine/lib/map_filter.nim b/tests/manyloc/keineschweine/lib/map_filter.nim new file mode 100644 index 000000000..54173fa61 --- /dev/null +++ b/tests/manyloc/keineschweine/lib/map_filter.nim @@ -0,0 +1,41 @@ +template filterIt2*(seq, pred: expr, body: stmt): stmt {.immediate, dirty.} = + ## sequtils defines a filterIt() that returns a new seq, but this one is called + ## with a statement body to iterate directly over it + for it in items(seq): + if pred: body + +proc map*[A, B](x: seq[A], func: proc(y: A): B {.closure.}): seq[B] = + result = @[] + for item in x.items: + result.add func(item) + +proc mapInPlace*[A](x: var seq[A], func: proc(y: A): A {.closure.}) = + for i in 0..x.len-1: + x[i] = func(x[i]) + +template unless*(condition: expr; body: stmt): stmt {.dirty.} = + if not(condition): + body + +when isMainModule: + proc dumpSeq[T](x: seq[T]) = + for index, item in x.pairs: + echo index, " ", item + echo "-------" + + var t= @[1,2,3,4,5] + var res = t.map(proc(z: int): int = result = z * 10) + dumpSeq res + + from strutils import toHex, repeatStr + var foocakes = t.map(proc(z: int): string = + result = toHex((z * 23).biggestInt, 4)) + dumpSeq foocakes + + t.mapInPlace(proc(z: int): int = result = z * 30) + dumpSeq t + + var someSeq = @[9,8,7,6,5,4,3,2,1] ## numbers < 6 or even + filterIt2 someSeq, it < 6 or (it and 1) == 0: + echo(it) + echo "-----------" \ No newline at end of file diff --git a/tests/manyloc/keineschweine/lib/math_helpers.nim b/tests/manyloc/keineschweine/lib/math_helpers.nim new file mode 100644 index 000000000..8af56d1ed --- /dev/null +++ b/tests/manyloc/keineschweine/lib/math_helpers.nim @@ -0,0 +1,10 @@ +import strutils, math + +proc degrees*(rad: float): float = + return rad * 180.0 / PI +proc radians*(deg: float): float = + return deg * PI / 180.0 + +## V not math, sue me +proc ff*(f: float, precision = 2): string {.inline.} = + return formatFloat(f, ffDecimal, precision) diff --git a/tests/manyloc/keineschweine/lib/sfml_stuff.nim b/tests/manyloc/keineschweine/lib/sfml_stuff.nim new file mode 100644 index 000000000..a5ac91195 --- /dev/null +++ b/tests/manyloc/keineschweine/lib/sfml_stuff.nim @@ -0,0 +1,37 @@ +import + math, strutils, + sfml, input_helpers +when not defined(NoChipmunk): + import chipmunk + proc floor*(a: TVector): TVector2f {.inline.} = + result.x = a.x.floor + result.y = a.y.floor + proc sfml2cp*(a: TVector2f): TVector {.inline.} = + result.x = a.x + result.y = a.y + proc cp2sfml*(a: TVector): TVector2f {.inline.} = + result.x = a.x + result.y = a.y + +proc vec2f*(a: TVector2i): TVector2f = + result.x = a.x.cfloat + result.y = a.y.cfloat +proc vec2i*(a: TVector2f): TVector2i = + result.x = a.x.cint + result.y = a.y.cint +proc vec3f*(x, y, z: float): TVector3f = + result.x = x.cfloat + result.y = y.cfloat + result.z = z.cfloat + +proc `$`*(a: var TIntRect): string = + result = "[TIntRect $1,$2 $3x$4]".format($a.left, $a.top, $a.width, $a.height) +proc `$`*(a: TKeyEvent): string = + return "KeyEvent: code=$1 alt=$2 control=$3 shift=$4 system=$5".format( + $a.code, $a.alt, $a.control, $a.shift, $a.system) + +proc `wmod`*(x, y: float): float = return x - y * (x/y).floor +proc move*(a: var TIntRect, left, top: cint): bool = + if a.left != left or a.top != top: result = true + a.left = left + a.top = top diff --git a/tests/manyloc/keineschweine/lib/sg_assets.nim b/tests/manyloc/keineschweine/lib/sg_assets.nim new file mode 100644 index 000000000..766bf5d82 --- /dev/null +++ b/tests/manyloc/keineschweine/lib/sg_assets.nim @@ -0,0 +1,609 @@ +import + re, json, strutils, tables, math, os, math_helpers, + sg_packets, md5, zlib_helpers + +when defined(NoSFML): + import server_utils + type TVector2i = object + x*, y*: int32 + proc vec2i(x, y: int32): TVector2i = + result.x = x + result.y = y +else: + import sfml, sfml_audio, sfml_stuff +when not defined(NoChipmunk): + import chipmunk + +type + TChecksumFile* = object + unpackedSize*: int + sum*: MD5Digest + compressed*: string + PZoneSettings* = ref TZoneSettings + TZoneSettings* = object + vehicles: seq[PVehicleRecord] + items: seq[PItemRecord] + objects: seq[PObjectRecord] + bullets: seq[PBulletRecord] + levelSettings: PLevelSettings + PLevelSettings* = ref TLevelSettings + TLevelSettings* = object + size*: TVector2i + starfield*: seq[PSpriteSheet] + PVehicleRecord* = ref TVehicleRecord + TVehicleRecord* = object + id*: int16 + name*: string + playable*: bool + anim*: PAnimationRecord + physics*: TPhysicsRecord + handling*: THandlingRecord + TItemKind* = enum + Projectile, Utility, Ammo + PObjectRecord* = ref TObjectRecord + TObjectRecord* = object + id*: int16 + name*: string + anim*: PAnimationRecord + physics*: TPhysicsRecord + PItemRecord* = ref TItemRecord + TItemRecord* = object + id*: int16 + name*: string + anim*: PAnimationRecord + physics*: TPhysicsRecord ##apply when the item is dropped in the arena + cooldown*: float + energyCost*: float + useSound*: PSoundRecord + case kind*: TItemKind + of Projectile: + bullet*: PBulletRecord + else: + nil + PBulletRecord* = ref TBulletRecord + TBulletRecord* = object + id*: int16 + name*: string + anim*: PAnimationRecord + physics*: TPhysicsRecord + lifetime*, inheritVelocity*, baseVelocity*: float + explosion*: TExplosionRecord + trail*: TTrailRecord + TTrailRecord* = object + anim*: PAnimationRecord + timer*: float ##how often it should be created + TPhysicsRecord* = object + mass*: float + radius*: float + moment*: float + THandlingRecord = object + thrust*, top_speed*: float + reverse*, strafe*, rotation*: float + TSoulRecord = object + energy*: int + health*: int + TExplosionRecord* = object + anim*: PAnimationRecord + sound*: PSoundRecord + PAnimationRecord* = ref TAnimationRecord + TAnimationRecord* = object + spriteSheet*: PSpriteSheet + angle*: float + delay*: float ##animation delay + PSoundRecord* = ref TSoundRecord + TSoundRecord* = object + file*: string + when defined(NoSFML): + contents*: TChecksumFile + else: + soundBuf*: PSoundBuffer + PSpriteSheet* = ref TSpriteSheet + TSpriteSheet* = object + file*: string + framew*,frameh*: int + rows*, cols*: int + when defined(NoSFML): + contents*: TChecksumFile + when not defined(NoSFML): + sprite*: PSprite + tex*: PTexture + TGameState* = enum + Lobby, Transitioning, Field +const + TAU* = PI * 2.0 + MomentMult* = 0.62 ## global moment of inertia multiplier +var + cfg: PZoneSettings + SpriteSheets* = initTable[string, PSpriteSheet](64) + SoundCache * = initTable[string, PSoundRecord](64) + nameToVehID*: TTable[string, int] + nameToItemID*: TTable[string, int] + nameToObjID*: TTable[string, int] + nameToBulletID*: TTable[string, int] + activeState = Lobby + +proc newSprite(filename: string; errors: var seq[string]): PSpriteSheet +proc load*(ss: PSpriteSheet): bool {.discardable.} +proc newSound(filename: string; errors: var seq[string]): PSoundRecord +proc load*(s: PSoundRecord): bool {.discardable.} + +proc validateSettings*(settings: PJsonNode; errors: var seq[string]): bool +proc loadSettings*(rawJson: string, errors: var seq[string]): bool +proc loadSettingsFromFile*(filename: string, errors: var seq[string]): bool + +proc fetchVeh*(name: string): PVehicleRecord +proc fetchItm*(itm: string): PItemRecord +proc fetchObj*(name: string): PObjectRecord +proc fetchBullet(name: string): PBulletRecord + +proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings +proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord +proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord +proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord +proc importPhys(data: PJsonNode): TPhysicsRecord +proc importAnim(data: PJsonNode; errors: var seq[string]): PAnimationRecord +proc importHandling(data: PJsonNode): THandlingRecord +proc importBullet(data: PJsonNode; errors: var seq[string]): PBulletRecord +proc importSoul(data: PJsonNode): TSoulRecord +proc importExplosion(data: PJsonNode; errors: var seq[string]): TExplosionRecord +proc importSound(data: PJsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord + +## this is the only pipe between lobby and main.nim +proc getActiveState*(): TGameState = + result = activeState +proc transition*() = + assert activeState == Lobby, "Transition() called from a state other than lobby!" + activeState = Transitioning +proc doneWithSaidTransition*() = + assert activeState == Transitioning, "Finished() called from a state other than transitioning!" + activeState = Field + + +proc checksumFile*(filename: string): TChecksumFile = + let fullText = readFile(filename) + result.unpackedSize = fullText.len + result.sum = toMD5(fullText) + result.compressed = compress(fullText) +proc checksumStr*(str: string): TChecksumFile = + result.unpackedSize = str.len + result.sum = toMD5(str) + result.compressed = compress(str) + + +##at this point none of these should ever be freed +proc free*(obj: PZoneSettings) = + echo "Free'd zone settings" +proc free*(obj: PSpriteSheet) = + echo "Free'd ", obj.file +proc free*(obj: PSoundRecord) = + echo "Free'd ", obj.file + +proc loadAllAssets*() = + var + loaded = 0 + failed = 0 + for name, ss in SpriteSheets.pairs(): + if load(ss): + inc loaded + else: + inc failed + echo loaded," sprites loaded. ", failed, " sprites failed." + loaded = 0 + failed = 0 + for name, s in SoundCache.pairs(): + if load(s): + inc loaded + else: + inc failed + echo loaded, " sounds loaded. ", failed, " sounds failed." +proc getLevelSettings*(): PLevelSettings = + result = cfg.levelSettings + +iterator playableVehicles*(): PVehicleRecord = + for v in cfg.vehicles.items(): + if v.playable: + yield v + +template allAssets*(body: stmt) {.dirty.}= + block: + var assetType = FGraphics + for file, asset in pairs(SpriteSheets): + body + assetType = FSound + for file, asset in pairs(SoundCache): + body + +template cacheImpl(procName, cacheName, resultType: expr; body: stmt) {.dirty, immediate.} = + proc procName*(filename: string; errors: var seq[string]): resulttype = + if hasKey(cacheName, filename): + return cacheName[filename] + new(result, free) + body + cacheName[filename] = result + +template checkFile(path: expr): stmt {.dirty, immediate.} = + if not existsFile(path): + errors.add("File missing: "& path) + +cacheImpl newSprite, SpriteSheets, PSpriteSheet: + result.file = filename + if not(filename =~ re"\S+_(\d+)x(\d+)\.\S\S\S"): + errors.add "Bad file: "&filename&" must be in format name_WxH.png" + return + result.framew = strutils.parseInt(matches[0]) + result.frameh = strutils.parseInt(matches[1]) + checkFile("data/gfx"/result.file) + +cacheImpl newSound, SoundCache, PSoundRecord: + result.file = filename + checkFile("data/sfx"/result.file) + +proc expandPath*(assetType: TAssetType; fileName: string): string = + result = "data/" + case assetType + of FGraphics: result.add "gfx/" + of FSound: result.add "sfx/" + else: nil + result.add fileName +proc expandPath*(fc: ScFileChallenge): string {.inline.} = + result = expandPath(fc.assetType, fc.file) + +when defined(NoSFML): + proc load*(ss: PSpriteSheet): bool = + if not ss.contents.unpackedSize == 0: return + ss.contents = checksumFile(expandPath(FGraphics, ss.file)) + result = true + proc load*(s: PSoundRecord): bool = + if not s.contents.unpackedSize == 0: return + s.contents = checksumFile(expandPath(FSound, s.file)) + result = true +else: + proc load*(ss: PSpriteSheet): bool = + if not ss.sprite.isNil: + return + var image = sfml.newImage("data/gfx/"/ss.file) + if image == nil: + echo "Image could not be loaded" + return + let size = image.getSize() + ss.rows = int(size.y / ss.frameh) #y is h + ss.cols = int(size.x / ss.framew) #x is w + ss.tex = newTexture(image) + image.destroy() + ss.sprite = newSprite() + ss.sprite.setTexture(ss.tex, true) + ss.sprite.setTextureRect(intrect(0, 0, ss.framew.cint, ss.frameh.cint)) + ss.sprite.setOrigin(vec2f(ss.framew / 2, ss.frameh / 2)) + result = true + proc load*(s: PSoundRecord): bool = + s.soundBuf = newSoundBuffer("data/sfx"/s.file) + if not s.soundBuf.isNil: + result = true + +template addError(e: expr): stmt {.immediate.} = + errors.add(e) + result = false +proc validateSettings*(settings: PJsonNode, errors: var seq[string]): bool = + result = true + if settings.kind != JObject: + addError("Settings root must be an object") + return + if not settings.existsKey("vehicles"): + addError("Vehicles section missing") + if not settings.existsKey("objects"): + errors.add("Objects section is missing") + result = false + if not settings.existsKey("level"): + errors.add("Level settings section is missing") + result = false + else: + let lvl = settings["level"] + if lvl.kind != JObject or not lvl.existsKey("size"): + errors.add("Invalid level settings") + result = false + elif not lvl.existsKey("size") or lvl["size"].kind != JArray or lvl["size"].len != 2: + errors.add("Invalid/missing level size") + result = false + if not settings.existsKey("items"): + errors.add("Items section missing") + result = false + else: + let items = settings["items"] + if items.kind != JArray or items.len == 0: + errors.add "Invalid or empty item list" + else: + var id = 0 + for i in items.items: + if i.kind != JArray: errors.add("Item #$1 is not an array"% $id) + elif i.len != 3: errors.add("($1) Item record should have 3 fields"%($id)) + elif i[0].kind != JString or i[1].kind != JString or i[2].kind != JObject: + errors.add("($1) Item should be in form [name, type, {item: data}]"% $id) + result = false + inc id + +proc loadSettingsFromFile*(filename: string, errors: var seq[string]): bool = + if not existsFile(filename): + errors.add("File does not exist: "&filename) + else: + result = loadSettings(readFile(filename), errors) + +proc loadSettings*(rawJson: string, errors: var seq[string]): bool = + var settings: PJsonNode + try: + settings = parseJson(rawJson) + except EJsonParsingError: + errors.add("JSON parsing error: "& getCurrentExceptionMsg()) + return + except: + errors.add("Unknown exception: "& getCurrentExceptionMsg()) + return + if not validateSettings(settings, errors): + return + if cfg != nil: #TODO try this + echo("Overwriting zone settings") + free(cfg) + cfg = nil + new(cfg, free) + cfg.levelSettings = importLevel(settings, errors) + cfg.vehicles = @[] + cfg.items = @[] + cfg.objects = @[] + cfg.bullets = @[] + nameToVehID = initTable[string, int](32) + nameToItemID = initTable[string, int](32) + nameToObjID = initTable[string, int](32) + nameToBulletID = initTable[string, int](32) + var + vID = 0'i16 + bID = 0'i16 + for vehicle in settings["vehicles"].items: + var veh = importVeh(vehicle, errors) + veh.id = vID + cfg.vehicles.add veh + nameToVehID[veh.name] = veh.id + inc vID + vID = 0 + if settings.existsKey("bullets"): + for blt in settings["bullets"].items: + var bullet = importBullet(blt, errors) + bullet.id = bID + cfg.bullets.add bullet + nameToBulletID[bullet.name] = bullet.id + inc bID + for item in settings["items"].items: + var itm = importItem(item, errors) + itm.id = vID + cfg.items.add itm + nameToItemID[itm.name] = itm.id + inc vID + if itm.kind == Projectile: + if itm.bullet.isNil: + errors.add("Projectile #$1 has no bullet!"% $vID) + elif itm.bullet.id == -1: + ## this item has an anonymous bullet, fix the ID and name + itm.bullet.id = bID + itm.bullet.name = itm.name + cfg.bullets.add itm.bullet + nameToBulletID[itm.bullet.name] = itm.bullet.id + inc bID + vID = 0 + for obj in settings["objects"].items: + var o = importObject(obj, errors) + o.id = vID + cfg.objects.add o + nameToObjID[o.name] = o.id + inc vID + result = (errors.len == 0) + +proc `$`*(obj: PSpriteSheet): string = + return "" % [obj.file, $obj.framew, $obj.frameh, $obj.rows, $obj.cols] + +proc fetchVeh*(name: string): PVehicleRecord = + return cfg.vehicles[nameToVehID[name]] +proc fetchItm*(itm: string): PItemRecord = + return cfg.items[nameToItemID[itm]] +proc fetchObj*(name: string): PObjectRecord = + return cfg.objects[nameToObjID[name]] +proc fetchBullet(name: string): PBulletRecord = + return cfg.bullets[nameToBulletID[name]] + +proc getField(node: PJsonNode, field: string, target: var float) = + if not node.existsKey(field): + return + if node[field].kind == JFloat: + target = node[field].fnum + elif node[field].kind == JInt: + target = node[field].num.float +proc getField(node: PJsonNode, field: string, target: var int) = + if not node.existsKey(field): + return + if node[field].kind == JInt: + target = node[field].num.int + elif node[field].kind == JFloat: + target = node[field].fnum.int +proc getField(node: PJsonNode; field: string; target: var bool) = + if not node.existsKey(field): + return + case node[field].kind + of JBool: + target = node[field].bval + of JInt: + target = (node[field].num != 0) + of JFloat: + target = (node[field].fnum != 0.0) + else: nil + +template checkKey(node: expr; key: string): stmt = + if not existsKey(node, key): + return + +proc importTrail(data: PJsonNode; errors: var seq[string]): TTrailRecord = + checkKey(data, "trail") + result.anim = importAnim(data["trail"], errors) + result.timer = 1000.0 + getField(data["trail"], "timer", result.timer) + result.timer /= 1000.0 +proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings = + new(result) + result.size = vec2i(5000, 5000) + result.starfield = @[] + + checkKey(data, "level") + var level = data["level"] + if level.existsKey("size") and level["size"].kind == JArray and level["size"].len == 2: + result.size.x = level["size"][0].num.cint + result.size.y = level["size"][1].num.cint + if level.existsKey("starfield"): + for star in level["starfield"].items: + result.starfield.add(newSprite(star.str, errors)) +proc importPhys(data: PJsonNode): TPhysicsRecord = + result.radius = 20.0 + result.mass = 10.0 + + if data.existsKey("physics") and data["physics"].kind == JObject: + let phys = data["physics"] + phys.getField("radius", result.radius) + phys.getField("mass", result.mass) + when not defined(NoChipmunk): + result.moment = momentForCircle(result.mass, 0.0, result.radius, vectorZero) * MomentMult +proc importHandling(data: PJsonNode): THandlingRecord = + result.thrust = 45.0 + result.topSpeed = 100.0 #unused + result.reverse = 30.0 + result.strafe = 30.0 + result.rotation = 2200.0 + + checkKey(data, "handling") + if data["handling"].kind != JObject: + return + + let hand = data["handling"] + hand.getField("thrust", result.thrust) + hand.getField("top_speed", result.topSpeed) + hand.getField("reverse", result.reverse) + hand.getField("strafe", result.strafe) + hand.getField("rotation", result.rotation) +proc importAnim(data: PJsonNode, errors: var seq[string]): PAnimationRecord = + new(result) + result.angle = 0.0 + result.delay = 1000.0 + result.spriteSheet = nil + + if data.existsKey("anim"): + let anim = data["anim"] + if anim.kind == JObject: + if anim.existsKey("file"): + result.spriteSheet = newSprite(anim["file"].str, errors) + + anim.getField "angle", result.angle + anim.getField "delay", result.delay + elif data["anim"].kind == JString: + result.spriteSheet = newSprite(anim.str, errors) + + result.angle = radians(result.angle) ## comes in as degrees + result.delay /= 1000 ## delay comes in as milliseconds +proc importSoul(data: PJsonNode): TSoulRecord = + result.energy = 10000 + result.health = 1 + checkKey(data, "soul") + let soul = data["soul"] + soul.getField("energy", result.energy) + soul.getField("health", result.health) +proc importExplosion(data: PJsonNode; errors: var seq[string]): TExplosionRecord = + checkKey(data, "explode") + let expl = data["explode"] + result.anim = importAnim(expl, errors) + result.sound = importSound(expl, errors, "sound") +proc importSound*(data: PJsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord = + if data.kind == JObject: + checkKey(data, fieldName) + result = newSound(data[fieldName].str, errors) + elif data.kind == JString: + result = newSound(data.str, errors) + +proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord = + new(result) + result.playable = false + if data.kind != JArray or data.len != 2 or + (data.kind == JArray and + (data[0].kind != JString or data[1].kind != JObject)): + result.name = "(broken)" + errors.add "Vehicle record is malformed" + return + var vehData = data[1] + result.name = data[0].str + result.anim = importAnim(vehdata, errors) + result.physics = importPhys(vehdata) + result.handling = importHandling(vehdata) + vehdata.getField("playable", result.playable) + if result.anim.spriteSheet.isNil and result.playable: + result.playable = false +proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord = + new(result) + if data.kind != JArray or data.len != 2: + result.name = "(broken)" + return + result.name = data[0].str + result.anim = importAnim(data[1], errors) + result.physics = importPhys(data[1]) +proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord = + new(result) + if data.kind != JArray or data.len != 3: + result.name = "(broken)" + errors.add "Item record is malformed" + return + result.name = data[0].str + result.anim = importAnim(data[2], errors) + result.physics = importPhys(data[2]) + + result.cooldown = 100.0 + data[2].getField("cooldown", result.cooldown) + result.cooldown /= 1000.0 ##cooldown is stored in ms + + result.useSound = importSound(data[2], errors, "useSound") + + case data[1].str.toLower + of "projectile": + result.kind = Projectile + if data[2]["bullet"].kind == JString: + result.bullet = fetchBullet(data[2]["bullet"].str) + elif data[2]["bullet"].kind == JInt: + result.bullet = cfg.bullets[data[2]["bullet"].num.int] + elif data[2]["bullet"].kind == JObject: + result.bullet = importBullet(data[2]["bullet"], errors) + else: + errors.add "UNKNOWN BULLET TYPE for item "& result.name + of "ammo": + result.kind = Ammo + of "utility": + nil + else: + errors.add "Invalid item type \""& data[1].str &"\" for item "& result.name + +proc importBullet(data: PJsonNode; errors: var seq[string]): PBulletRecord = + new(result) + result.id = -1 + + var bdata: PJsonNode + if data.kind == JArray: + result.name = data[0].str + bdata = data[1] + elif data.kind == JObject: + bdata = data + else: + errors.add "Malformed bullet record" + return + + result.anim = importAnim(bdata, errors) + result.physics = importPhys(bdata) + + result.lifetime = 2000.0 + result.inheritVelocity = 1000.0 + result.baseVelocity = 30.0 + getField(bdata, "lifetime", result.lifetime) + getField(bdata, "inheritVelocity", result.inheritVelocity) + getField(bdata, "baseVelocity", result.baseVelocity) + result.lifetime /= 1000.0 ## lifetime is stored as milliseconds + result.inheritVelocity /= 1000.0 ## inherit velocity 1000 = 1.0 (100%) + result.explosion = importExplosion(bdata, errors) + result.trail = importTrail(bdata, errors) diff --git a/tests/manyloc/keineschweine/lib/sg_gui.nim b/tests/manyloc/keineschweine/lib/sg_gui.nim new file mode 100644 index 000000000..6741fe55e --- /dev/null +++ b/tests/manyloc/keineschweine/lib/sg_gui.nim @@ -0,0 +1,281 @@ +import + sfml, sfml_colors, + input_helpers, sg_packets +from strutils import countlines +{.deadCodeElim: on.} +type + PGuiContainer* = ref TGuiContainer + TGuiContainer* = object of TObject + position: TVector2f + activeEntry: PTextEntry + widgets: seq[PGuiObject] + buttons: seq[PButton] + PGuiObject* = ref TGuiObject + TGuiObject* = object of TObject + PButton* = ref TButton + TButton* = object of TGuiObject + enabled: bool + bg*: sfml.PRectangleShape + text*: PText + onClick*: TButtonClicked + bounds: TFloatRect + PButtonCollection* = ref TButtonCollection + TButtonCollection* = object of TGuiContainer + PTextEntry* = ref TTextEntry + TTextEntry* = object of TButton + inputClient: input_helpers.PTextInput + PMessageArea* = ref TMessageArea + TMessageArea* = object of TGuiObject + pos: TVector2f + messages: seq[TMessage] + texts: seq[PText] + scrollBack*: int + sizeVisible*: int + direction*: int + TMessage = object + color: TColor + text: string + lines: int + TButtonClicked = proc(button: PButton) +var + guiFont* = newFont("data/fnt/LiberationMono-Regular.ttf") + messageProto* = newText("", guiFont, 16) +let + vectorZeroF* = vec2f(0.0, 0.0) + +proc newGuiContainer*(): PGuiContainer +proc newGuiContainer*(pos: TVector2f): PGuiContainer {.inline.} +proc free*(container: PGuiContainer) +proc add*(container: PGuiContainer; widget: PGuiObject) +proc clearButtons*(container: PGuiContainer) +proc click*(container: PGuiContainer; position: TVector2f) +proc setActive*(container: PGuiContainer; entry: PTextEntry) +proc setPosition*(container: PGuiContainer; position: TVector2f) + +proc update*(container: PGuiContainer; dt: float) +proc draw*(window: PRenderWindow; container: PGuiContainer) {.inline.} + +proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea {.discardable.} +proc add*(m: PMessageArea; msg: ScChat) + +proc draw*(window: PRenderWindow; b: PButton) {.inline.} +proc click*(b: PButton; p: TVector2f) +proc setPosition*(b: PButton; p: TVector2f) +proc setString*(b: PButton; s: string) {.inline.} + +proc newButton*(container: PGuiContainer; text: string; position: TVector2f; + onClick: TButtonClicked; startEnabled: bool = true): PButton {.discardable.} +proc init(b: PButton; text: string; position: TVector2f; onClick: TButtonClicked) +proc setEnabled*(b: PButton; enabled: bool) +proc disable*(b: PButton) {.inline.} +proc enable*(b: PButton) {.inline.} + +proc newTextEntry*(container: PGuiContainer; text: string; + position: TVector2f; onEnter: TInputFinishedProc = nil): PTextEntry {.discardable.} +proc init(t: PTextEntry; text: string; onEnter: TInputFinishedProc) +proc draw*(window: PRenderWindow, t: PTextEntry) {.inline.} +proc setActive*(t: PTextEntry) {.inline.} +proc clearText*(t: PTextEntry) {.inline.} +proc getText*(t: PTextEntry): string {.inline.} + +proc update*(m: PMessageArea) + +if guiFont == nil: + echo("Could not load font, crying softly to myself.") + quit(1) + +proc newGuiContainer*(): PGuiContainer = + new(result, free) + result.widgets = @[] + result.buttons = @[] +proc newGuiContainer*(pos: TVector2f): PGuiContainer = + result = newGuiContainer() + result.setPosition pos +proc free*(container: PGuiContainer) = + container.widgets = nil + container.buttons = nil +proc add*(container: PGuiContainer; widget: PGuiObject) = + container.widgets.add(widget) +proc add*(container: PGuiContainer; button: PButton) = + if container.isNil: return + container.buttons.add(button) +proc clearButtons*(container: PGuiContainer) = + container.buttons.setLen 0 +proc click*(container: PGuiContainer; position: TVector2f) = + for b in container.buttons: + click(b, position) +proc setActive*(container: PGuiContainer; entry: PTextEntry) = + container.activeEntry = entry + setActive(entry) +proc setPosition*(container: PGuiContainer; position: TVector2f) = + container.position = position + + +proc update*(container: PGuiContainer; dt: float) = + if not container.activeEntry.isNil: + container.activeEntry.setString(container.activeEntry.getText()) +proc draw*(window: PRenderWindow; container: PGuiContainer) = + for b in container.buttons: + window.draw b + +proc free(c: PButton) = + c.bg.destroy() + c.text.destroy() + c.bg = nil + c.text = nil + c.onClick = nil +proc newButton*(container: PGuiContainer; text: string; + position: TVector2f; onClick: TButtonClicked; + startEnabled: bool = true): PButton = + new(result, free) + init(result, + text, + if not container.isNil: position + container.position else: position, + onClick) + container.add result + if not startEnabled: disable(result) + +proc init(b: PButton; text: string; position: TVector2f; onClick: TButtonClicked) = + b.bg = newRectangleShape() + b.bg.setSize(vec2f(80.0, 16.0)) + b.bg.setFillColor(color(20, 30, 15)) + b.text = newText(text, guiFont, 16) + b.onClick = onClick + b.setPosition(position) + b.enabled = true +proc copy*(c: PButton): PButton = + new(result, free) + result.bg = c.bg.copy() + result.text = c.text.copy() + result.onClick = c.onClick + result.setPosition(result.bg.getPosition()) + +proc setEnabled*(b: PButton; enabled: bool) = + b.enabled = enabled + if enabled: + b.text.setColor(White) + else: + b.text.setColor(Gray) +proc enable*(b: PButton) = setEnabled(b, true) +proc disable*(b: PButton) = setEnabled(b, false) + +proc draw*(window: PRenderWindow; b: PButton) = + window.draw b.bg + window.draw b.text +proc setPosition*(b: PButton, p: TVector2f) = + b.bg.setPosition(p) + b.text.setPosition(p) + b.bounds = b.text.getGlobalBounds() +proc setString*(b: PButton; s: string) = + b.text.setString(s) +proc click*(b: PButton, p: TVector2f) = + if b.enabled and (addr b.bounds).contains(p.x, p.y): + b.onClick(b) + +proc free(obj: PTextEntry) = + free(PButton(obj)) +proc newTextEntry*(container: PGuiContainer; text: string; + position: TVector2F; onEnter: TInputFinishedProc = nil): PTextEntry = + new(result, free) + init(PButton(result), text, position + container.position, proc(b: PButton) = + setActive(container, PTextEntry(b))) + init(result, text, onEnter) + container.add result +proc init(t: PTextEntry; text: string; onEnter: TInputFinishedProc) = + t.inputClient = newTextInput(text, text.len, onEnter) +proc draw(window: PRenderWindow; t: PTextEntry) = + window.draw PButton(t) +proc clearText*(t: PTextEntry) = + t.inputClient.clear() +proc getText*(t: PTextEntry): string = + return t.inputClient.text +proc setActive*(t: PTextEntry) = + if not t.isNil and not t.inputClient.isNil: + input_helpers.setActive(t.inputClient) + + +discard """proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea = + new(result) + result.messages = @[] + result.pos = position + container.add(result) +proc add*(m: PMessageArea, text: string): PText = + result = messageProto.copy() + result.setString(text) + m.messages.add(result) + let nmsgs = len(m.messages) + var pos = vec2f(m.pos.x, m.pos.y) + for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)): + setPosition(m.messages[i], pos) + pos.y -= 16.0 + +proc draw*(window: PRenderWindow; m: PMessageArea) = + let nmsgs = len(m.messages) + if nmsgs == 0: return + for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)): + window.draw(m.messages[i]) +""" +proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea = + new(result) + result.messages = @[] + result.texts = @[] + result.pos = position + container.position + result.sizeVisible = 10 + result.scrollBack = 0 + result.direction = -1 ## to push old messages up + container.add(result) + +proc add*(m: PMessageArea, msg: ScChat) = + const prependName = {CPub, CPriv} + var mmm: TMessage + if msg.kind in prependName: + mmm.text = "<" + mmm.text.add msg.fromPlayer + mmm.text.add "> " + mmm.text.add msg.text + else: + mmm.text = msg.text + case msg.kind + of CPub: mmm.color = RoyalBlue + of CPriv, CSystem: mmm.color = Green + of CError: mmm.color = Red + + mmm.lines = countLines(mmm.text)+1 + + m.messages.add mmm + update m +proc add*(m: PMessageArea, msg: string) {.inline.} = + var chat = newScChat(kind = CSystem, text = msg) + add(m, chat) + +proc proctor*(m: PText; msg: ptr TMessage; pos: ptr TVector2f) = + m.setString msg.text + m.setColor msg.color + m.setPosition pos[] +proc update*(m: PMessageArea) = + if m.texts.len < m.sizeVisible: + echo "adding ", m.sizeVisible - m.texts.len, " fields" + for i in 1..m.sizeVisible - m.texts.len: + var t = messageProto.copy() + m.texts.add messageProto.copy() + elif m.texts.len > m.sizeVisible: + echo "cutting ", m.texts.len - m.sizeVisible, " fields" + for i in m.sizeVisible.. < m.texts.len: + m.texts.pop().destroy() + let nmsgs = m.messages.len() + if m.sizeVisible == 0 or nmsgs == 0: + echo "no messages? ", m.sizeVisible, ", ", nmsgs + return + var pos = vec2f(m.pos.x, m.pos.y) + for i in 0.. min(m.sizeVisible, nmsgs)-1: + ##echo nmsgs - i - 1 - m.scrollBack + let msg = addr m.messages[nmsgs - i - 1 - m.scrollBack] + proctor(m.texts[i], msg, addr pos) + pos.y += (16 * m.direction * msg.lines).cfloat + +proc draw*(window: PRenderWindow; m: PMessageArea) = + let nmsgs = len(m.texts) + if nmsgs == 0: return + for i in countdown(nmsgs - 1, max(nmsgs - m.sizeVisible, 0)): + window.draw m.texts[i] + diff --git a/tests/manyloc/keineschweine/lib/sg_packets.nim b/tests/manyloc/keineschweine/lib/sg_packets.nim new file mode 100644 index 000000000..30e83c147 --- /dev/null +++ b/tests/manyloc/keineschweine/lib/sg_packets.nim @@ -0,0 +1,106 @@ +import genpacket_enet, sockets, md5, enet +defPacketImports() + +type + PacketID* = char + +template idpacket(pktName, id, s2c, c2s: expr): stmt {.immediate, dirty.} = + let `H pktName`* {.inject.} = id + defPacket(`Sc pktName`, s2c) + defPacket(`Cs pktName`, c2s) + +forwardPacketT(Uint8, int8) +forwardPacketT(Uint16, int16) +forwardPacketT(TPort, int16) + +idPacket(Login, 'a', + tuple[id: int32; alias: string; sessionKey: string], + tuple[alias: string, passwd: string]) + +let HZoneJoinReq* = 'j' +defPacket(CsZoneJoinReq, tuple[session: ScLogin]) + +defPacket(ScZoneRecord, tuple[ + name: string = "", desc: string = "", + ip: string = "", port: TPort = 0.Tport]) +idPacket(ZoneList, 'z', + tuple[network: string = "", zones: seq[ScZoneRecord]], + tuple[time: string]) + +let HPoing* = 'p' +defPacket(Poing, tuple[id: int32, time: float32]) + +type ChatType* = enum + CPub = 0'i8, CPriv, CSystem, CError +forwardPacketT(ChatType, int8) +idPacket(Chat, 'C', + tuple[kind: ChatType = CPub; fromPlayer: string = ""; text: string = ""], + tuple[target: string = ""; text: string = ""]) + +idPacket(Hello, 'h', + tuple[resp: string], + tuple[i: int8 = 14]) + +let HPlayerList* = 'P' +defPacket(ScPlayerRec, tuple[id: int32; alias: string = ""]) +defPacket(ScPlayerList, tuple[players: seq[ScPlayerRec]]) + +let HTeamList* = 'T' +defPacket(ScTeam, tuple[id: int8; name: string = ""]) +defPacket(ScTeamList, tuple[teams: seq[ScTeam]]) +let HTeamChange* = 't' + +idPacket(ZoneQuery, 'Q', + tuple[playerCount: Uint16], ##i should include a time here or something + tuple[pad: char = '\0']) + +type SpawnKind = enum + SpawnItem = 1'i8, SpawnVehicle, SpawnObject +forwardPacketT(SpawnKind, int8) +defPacket(ScSpawn, tuple[ + kind: SpawnKind; id: uint16; record: uint16; amount: uint16]) + + + + +type TAssetType* = enum + FZoneCfg = 1'i8, FGraphics, FSound + +forwardPacketT(TAssetType, int8) +forwardPacket(MD5Digest, array[0..15, int8]) + +idPacket(FileChallenge, 'F', + tuple[file: string; assetType: TAssetType; fullLen: int32], + tuple[needFile: bool; checksum: MD5Digest]) + + +let HChallengeResult* = '(' +defPacket(ScChallengeResult, tuple[status: bool]) + +let HFileTransfer* = 'f' +defPacket(ScFileTransfer, tuple[fileSize: int32; pos: int32; data: string]) +defPacket(CsFilepartAck, tuple[lastpos: int32]) + +##dir server messages +let HZoneLogin* = 'u' +defPacket(SdZoneLogin, tuple[name: string; key: string; record: ScZoneRecord]) +defPacket(DsZoneLogin, tuple[status: bool]) +let HDsMsg* = 'c' +defPacket(DsMsg, tuple[msg: string]) +let HVerifyClient* = 'v' +defPacket(SdVerifyClient, tuple[session: ScLogin]) + +when isMainModule: + + var buf = newBuffer(100) + var m = toMd5("hello there") + echo(repr(m)) + buf.pack m + + echo(repr(buf.data)) + echo(len(buf.data)) + + buf.reset() + + var x = buf.readMD5Digest() + echo(repr(x)) diff --git a/tests/manyloc/keineschweine/lib/sound_buffer.nim b/tests/manyloc/keineschweine/lib/sound_buffer.nim new file mode 100644 index 000000000..a88eb08de --- /dev/null +++ b/tests/manyloc/keineschweine/lib/sound_buffer.nim @@ -0,0 +1,38 @@ +when defined(NoSFML) or defined(NoChipmunk): + {.error.} +import sfml_audio, sfml_stuff, sg_assets, chipmunk +const + MinDistance* = 350.0 + Attenuation* = 20.0 +var + liveSounds: seq[PSound] = @[] + deadSounds: seq[PSound] = @[] + +proc playSound*(sound: PSoundRecord, pos: TVector) = + if sound.isNil or sound.soundBuf.isNil: return + var s: PSound + if deadSounds.len == 0: + s = sfml_audio.newSound() + s.setLoop false + s.setRelativeToListener true + s.setAttenuation Attenuation + s.setMinDistance MinDistance + else: + s = deadSounds.pop() + s.setPosition(vec3f(pos.x, 0, pos.y)) + s.setBuffer(sound.soundBuf) + s.play() + liveSounds.add s + +proc updateSoundBuffer*() = + var i = 0 + while i < len(liveSounds): + if liveSounds[i].getStatus == Stopped: + deadSounds.add liveSounds[i] + liveSounds.del i + else: + inc i + +proc report*() = + echo "live: ", liveSounds.len + echo "dead: ", deadSounds.len diff --git a/tests/manyloc/keineschweine/lib/vehicles.nim b/tests/manyloc/keineschweine/lib/vehicles.nim new file mode 100644 index 000000000..edbd84ff9 --- /dev/null +++ b/tests/manyloc/keineschweine/lib/vehicles.nim @@ -0,0 +1,35 @@ +import + sfml, chipmunk, + sg_assets, sfml_stuff, keineschweine + + +proc accel*(obj: PVehicle, dt: float) = + #obj.velocity += vec2f( + # cos(obj.angle) * obj.record.handling.thrust.float * dt, + # sin(obj.angle) * obj.record.handling.thrust.float * dt) + obj.body.applyImpulse( + vectorForAngle(obj.body.getAngle()) * dt * obj.record.handling.thrust, + vectorZero) +proc reverse*(obj: PVehicle, dt: float) = + #obj.velocity += vec2f( + # -cos(obj.angle) * obj.record.handling.reverse.float * dt, + # -sin(obj.angle) * obj.record.handling.reverse.float * dt) + obj.body.applyImpulse( + -vectorForAngle(obj.body.getAngle()) * dt * obj.record.handling.reverse, + vectorZero) +proc strafe_left*(obj: PVehicle, dt: float) = + obj.body.applyImpulse( + vectorForAngle(obj.body.getAngle()).perp() * obj.record.handling.strafe * dt, + vectorZero) +proc strafe_right*(obj: PVehicle, dt: float) = + obj.body.applyImpulse( + vectorForAngle(obj.body.getAngle()).rperp()* obj.record.handling.strafe * dt, + vectorZero) +proc turn_right*(obj: PVehicle, dt: float) = + #obj.angle = (obj.angle + (obj.record.handling.rotation.float / 10.0 * dt)) mod TAU + obj.body.setTorque(obj.record.handling.rotation) +proc turn_left*(obj: PVehicle, dt: float) = + #obj.angle = (obj.angle - (obj.record.handling.rotation.float / 10.0 * dt)) mod TAU + obj.body.setTorque(-obj.record.handling.rotation) +proc offsetAngle*(obj: PVehicle): float {.inline.} = + return (obj.record.anim.angle + obj.body.getAngle()) diff --git a/tests/manyloc/keineschweine/lib/zlib_helpers.nim b/tests/manyloc/keineschweine/lib/zlib_helpers.nim new file mode 100644 index 000000000..ef977afb0 --- /dev/null +++ b/tests/manyloc/keineschweine/lib/zlib_helpers.nim @@ -0,0 +1,40 @@ +import zlib + +proc compress*(source: string): string = + var + sourcelen = source.len + destlen = sourcelen + (sourcelen.float * 0.1).int + 16 + result = "" + result.setLen destLen + var res = zlib.compress(cstring(result), addr destLen, cstring(source), sourceLen) + if res != Z_OK: + echo "Error occured: ", res + elif destLen < result.len: + result.setLen(destLen) + +proc uncompress*(source: string, destLen: var int): string = + result = "" + result.setLen destLen + var res = zlib.uncompress(cstring(result), addr destLen, cstring(source), source.len) + if res != Z_OK: + echo "Error occured: ", res + + +when isMainModule: + import strutils + var r = compress("Hello") + echo repr(r) + var l = "Hello".len + var rr = uncompress(r, l) + echo repr(rr) + assert rr == "Hello" + + proc `*`(a: string; b: int): string {.inline.} = result = repeatStr(b, a) + var s = "yo dude sup bruh homie" * 50 + r = compress(s) + echo s.len, " -> ", r.len + + l = s.len + rr = uncompress(r, l) + echo r.len, " -> ", rr.len + assert rr == s \ No newline at end of file diff --git a/tests/manyloc/keineschweine/nakefile.nim b/tests/manyloc/keineschweine/nakefile.nim new file mode 100644 index 000000000..f175321b9 --- /dev/null +++ b/tests/manyloc/keineschweine/nakefile.nim @@ -0,0 +1,155 @@ +import nake +import httpclient, zipfiles, times, math +nakeImports + +randomize() + +const + GameAssets = "http://dl.dropbox.com/u/37533467/data-08-01-2012.7z" + BinLibs = "http://dl.dropbox.com/u/37533467/libs-2012-09-12.zip" + ExeName = "keineschweine" + ServerDefines = "-d:NoSFML -d:NoChipmunk" + TestBuildDefines = "-d:escapeMenuTest -d:debugWeps -d:showFPS -d:moreNimrod -d:debugKeys -d:foo -d:recordMode --forceBuild" + ReleaseDefines = "-d:release --deadCodeElim:on" + ReleaseTestDefines = "-d:debugWeps -d:debugKeys --forceBuild" + +task "testprofile", "..": + if shell("nimrod", TestBuildDefines, "--profiler:on", "--stacktrace:on", "compile", ExeName) == 0: + shell "."/ExeName, "offline" + +task "test", "Build with test defines": + if shell("nimrod", TestBuildDefines, "compile", ExeName) != 0: + quit "The build failed." + +task "testrun", "Build with test defines and run": + runTask "test" + shell "."/ExeName + +task "test2", "Build release test build test release build": + if shell("nimrod", ReleaseDefines, ReleaseTestDefines, "compile", ExeName) == 0: + shell "."/ExeName + +discard """task "dirserver", "build the directory server": + withDir "server": + if shell("nimrod", ServerDefines, "compile", "dirserver") != 0: + echo "Failed to build the dirserver" + quit 1""" + +task "zoneserver", "build the zone server": + withDir "enet_server": + if shell("nimrod", ServerDefines, "compile", "enet_server") != 0: + quit "Failed to build the zoneserver" +task "zoneserver-gui", "build the zone server, with gui!": + withDir "enet_server": + if shell("nimrod", ServerDefines, "--app:gui", "compile", "enet_server") != 0: + quit "Failed to build the zoneserver" + +task "servers", "build the server and directory server": + #runTask "dirserver" + runTask "zoneserver" + echo "Successfully built both servers :')" + +task "all", "run SERVERS and TEST tasks": + runTask "servers" + runTask "test" + +task "release", "release build": + let res = shell("nimrod", ReleaseDefines, "compile", ExeName) + if res != 0: + echo "The build failed." + quit 1 + else: + runTask "clean" + ## zip up all the files and such or something useful here + +task "testskel", "create skeleton test dir for testing": + let dirname = "test-"& $random(5000) + removeDir dirName + createDir dirName/"data/fnt" + copyFile "data/fnt/LiberationMono-Regular", dirName/"data/fnt/LiberationMono-Regular.ttf" + copyFile "client_settings.json", dirName/"client_settings.json" + runTask "test" + copyFile ExeName, dirName/ExeName + withDir dirName: + shell "."/ExeName + + +task "clean", "cleanup generated files": + var dirs = @["nimcache", "server"/"nimcache"] + dirs.each(proc(x: var string) = + if existsDir(x): removeDir(x)) + +task "download", "download game assets": + var + skipAssets = false + path = expandFilename("data") + path.add DirSep + path.add(extractFilename(gameAssets)) + if existsFile(path): + echo "The file already exists\n", + "[R]emove [M]ove [Q]uit [S]kip Source: ", GameAssets + case stdin.readLine.toLower + of "r": + removeFile path + of "m": + moveFile path, path/../(extractFilename(gameAssets)&"-old") + of "s": + skipAssets = true + else: + quit 0 + else: + echo "Downloading from ", GameAssets + if not skipAssets: + echo "Downloading to ", path + downloadFile gameAssets, path + echo "Download finished" + + let targetDir = parentDir(parentDir(path)) + when defined(linux): + let z7 = findExe("7z") + if z7 == "": + echo "Could not find 7z" + elif shell(z7, "t", path) != 0: ##note to self: make sure this is right + echo "Bad download" + else: + echo "Unpacking..." + shell(z7, "x", "-w[$1]" % targetDir, path) + else: + echo "I do not know how to unpack the data on this system. Perhaps you could ", + "fill this part in?" + + echo "Download binary libs? Only libs for linux are available currently, enjoy the irony.\n", + "[Y]es [N]o Source: ", BinLibs + case stdin.readline.toLower + of "y", "yes": + discard ## o_O + else: + return + path = extractFilename(BinLibs) + downloadFile BinLibs, path + echo "Downloaded dem libs ", path + when true: echo "Unpack it yourself, sorry." + else: ## this crashes, dunno why + var + z: TZipArchive + destDir = getCurrentDir()/("unzip"& $random(5000)) + if not z.open(path, fmRead): + echo "Could not open zip, bad download?" + return + echo "Extracting to ", destDir + createDir destDir + #z.extractAll destDir + for f in z.walkFiles(): + z.extractFile(f, destDir/f) + z.close() + echo "Extracted the libs dir. Copy the ones you need to this dir." + +task "zip-lib", "zip up the libs dir": + var z: TZipArchive + if not z.open("libs-"& getDateStr() &".zip", fmReadWrite): + quit "Could not open zip" + for file in walkDirRec("libs", {pcFile, pcDir}): + echo "adding file ", file + z.addFile(file) + z.close() + echo "Great success!" \ No newline at end of file diff --git a/tests/manyloc/keineschweine/nakefile.nimrod.cfg b/tests/manyloc/keineschweine/nakefile.nimrod.cfg new file mode 100644 index 000000000..6f3e86fe6 --- /dev/null +++ b/tests/manyloc/keineschweine/nakefile.nimrod.cfg @@ -0,0 +1 @@ +path = "dependencies/nake" diff --git a/tests/manyloc/keineschweine/server/dirserver_settings.json b/tests/manyloc/keineschweine/server/dirserver_settings.json new file mode 100644 index 000000000..18c15fb46 --- /dev/null +++ b/tests/manyloc/keineschweine/server/dirserver_settings.json @@ -0,0 +1,7 @@ +{ + "network":"lamenet", + "port":2049, + "zones":[ + {"name":"alphazone","key":"skittles"} + ] +} \ No newline at end of file diff --git a/tests/manyloc/keineschweine/server/nimrod.cfg b/tests/manyloc/keineschweine/server/nimrod.cfg new file mode 100644 index 000000000..fdc45a8e1 --- /dev/null +++ b/tests/manyloc/keineschweine/server/nimrod.cfg @@ -0,0 +1,6 @@ +debugger = off +deadCodeElim = on +path = ".." +path = "../genpacket" +path = "../helpers" +define = NoSFML diff --git a/tests/manyloc/keineschweine/server/old_dirserver.nim b/tests/manyloc/keineschweine/server/old_dirserver.nim new file mode 100644 index 000000000..897fc7d32 --- /dev/null +++ b/tests/manyloc/keineschweine/server/old_dirserver.nim @@ -0,0 +1,201 @@ +## directory server +## handles client authorization and assets + +import + sockets, times, streams, streams_enh, tables, json, os, + sg_packets, sg_assets, md5, server_utils, map_filter +type + THandler = proc(client: PCLient; stream: PStream) +var + server: TSocket + handlers = initTable[char, THandler](16) + thisZone = newScZoneRecord("local", "sup") + zoneList = newScZoneList() + thisZoneSettings: string + zoneSlots: seq[tuple[name: string; key: string]] = @[] + zones: seq[PClient] = @[] + ## I was high. + clients = initTable[TupAddress, PClient](16) + alias2client = initTable[string, PClient](32) + allClients: seq[PClient] = @[] + +proc findClient*(host: string; port: int16): PClient = + let addy: TupAddress = (host, port) + if clients.hasKey(addy): + return clients[addy] + result = newClient(addy) + clients[addy] = result + allClients.add(result) + +proc loginZone(client: PClient; login: SdZoneLogin): bool = + if not client.auth: + for s in zoneSlots.items: + if s.name == login.name and s.key == login.key: + client.auth = true + client.kind = CServer + client.record = login.record + result = true + break + +proc sendZoneList(client: PClient) = + echo(">> zonelist ", client, ' ', HZoneList) + client.send(HZonelist, zonelist) +proc forwardPrivate(rcv: PClient; sender: PClient; txt: string) = + var m = newScChat(CPriv, sender.alias, txt) + rcv.send(HChat, m) +proc sendChat(client: PClient; kind: ChatType; txt: string) = + echo(">> chat ", client) + var m = newScChat(kind, "", txt) + client.send(HChat, m) + + + +var pubChatQueue = newIncomingBuffer() +proc queuePub(sender: string, msg: CsChat) = + var chat = newScChat(kind = CPub, fromPlayer = sender, text = msg.text) + pubChatQueue.write(HChat) + chat.pack(pubChatQueue) + +handlers[HHello] = (proc(client: PClient; stream: PStream) = + var h = readCsHello(stream) + if h.i == 14: + var greet = newScHello("Well hello there") + client.send(HHello, greet)) +handlers[HLogin] = proc(client: PClient; stream: PStream) = + var loginInfo = readCsLogin(stream) + echo("** login: alias = ", loginInfo.alias) + if alias2client.hasKey(loginInfo.alias): + client.sendError("Alias in use.") + return + if client.loginPlayer(loginInfo): + alias2client[client.alias] = client + client.sendMessage("Welcome "& client.alias) + var session = newScLogin(client.id, client.alias, client.session) + client.send HLogin, session + client.sendZonelist() + +handlers[HZoneList] = proc(client: PClient; stream: PStream) = + var pinfo = readCsZoneList(stream) + echo("** zonelist req") + sendZoneList client +handlers[HChat] = proc(client: PClient; stream: PStream) = + var chat = readCsChat(stream) + if not client.auth: + client.sendError("You are not logged in.") + return + if chat.target != "": ##private + if alias2client.hasKey(chat.target): + alias2client[chat.target].forwardPrivate(client, chat.text) + else: + queuePub(client.alias, chat) + +proc sendServMsg(client: PClient; msg: string) = + var m = newDsMsg(msg) + client.send HDsMsg, m +handlers[HZoneLogin] = proc(client: PClient; stream: PStream) = + var + login = readSdZoneLogin(stream) + if not client.loginZone(login): + client.sendServMsg "Invalid login" + else: + client.sendServMsg "Welcome to the servers" + echo "** Zone logged in: ", login + zones.add client + zonelist.zones.add client.record + + +handlers[HFileChallenge] = proc(client: PClient; stream: PStream) = + if client.auth: + if client.kind == CServer: + var chg = readScFileChallenge(stream) + +proc handlePkt(s: PClient; stream: PStream) = + while not stream.atEnd: + var typ = readChar(stream) + if not handlers.hasKey(typ): + break + else: + handlers[typ](s, stream) + +proc createServer(port: TPort) = + if not server.isNil: + server.close() + server = socket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP, buffered = false) + server.bindAddr(port) + + +var clientIndex = 0 +var incoming = newIncomingBuffer() +proc poll*(timeout: int = 250) = + if server.isNil: return + var + reads = @[server] + writes = @[server] + if select(reads, timeout) > 0: + var + addy = "" + port: TPort + incoming.data.setLen 512 + let res = server.recvFromAsync(incoming.data, 512, addy, port, 0) + if not res: + echo("No recv") + return + else: + var client = findClient(addy, port.int16) + echo "<< ", res, " ", client, ": ", len(incoming.data), " ", repr(incoming.data) + handlePkt(client, incoming) + incoming.flush() + if selectWrite(writes, timeout) > 0: + let nclients = allClients.len + if nclients == 0: + return + clientIndex = (clientIndex + 1) mod nclients + var c = allClients[clientIndex] + if c.outputBuf.getPosition > 0: + let res = server.sendTo(c.addy.host, c.addy.port.TPort, c.outputBuf.data) + echo("Write ", c, " result: ", res, " data: ", repr(c.outputBuf.data)) + c.outputBuf.flush() + +when isMainModule: + import parseopt, matchers, strutils + var cfgFile = "dirserver_settings.json" + for kind, key, val in getOpt(): + case kind + of cmdShortOption, cmdLongOption: + case key + of "f", "file": + if existsFile(val): + cfgFile = val + else: + echo("File does not exist: ", val) + else: + echo("Unknown option: ", key," ", val) + else: + echo("Unknown option: ", key, " ", val) + var jsonSettings = parseFile(cfgFile) + let port = TPort(jsonSettings["port"].num) + zonelist.network = jsonSettings["network"].str + for slot in jsonSettings["zones"].items: + zoneSlots.add((slot["name"].str, slot["key"].str)) + + createServer(port) + echo("Listening on port ", port, "...") + var pubChatTimer = cpuTime() #newClock() + const PubChatDelay = 1000/1000 + while true: + poll(15) + ## TODO sort this type of thing VV into a queue api + if cpuTime() - pubChatTimer > PubChatDelay: #.getElapsedTime.asMilliseconds > 100: + pubChatTimer -= pubChatDelay + if pubChatQueue.getPosition > 0: + var cn = 0 + let sizePubChat = pubChatQueue.data.len + var sent = 0 + filterIt2(allClients, it.auth == true and it.kind == CPlayer): + it.outputBuf.writeData(addr pubChatQueue.data[0], sizePubChat) + sent += 1 + #for c in allClients: + # c.outputBuf.writeData(addr pubChatQueue.data[0], sizePubChat) + pubChatQueue.flush() + echo "pubChatQueue flushed to ", sent, "clients" + diff --git a/tests/manyloc/keineschweine/server/old_server_utils.nim b/tests/manyloc/keineschweine/server/old_server_utils.nim new file mode 100644 index 000000000..af9a1b01e --- /dev/null +++ b/tests/manyloc/keineschweine/server/old_server_utils.nim @@ -0,0 +1,98 @@ +import + streams, md5, sockets, unsigned, + sg_packets, zlib_helpers, idgen +type + TClientType* = enum + CServer = 0'i8, CPlayer, CUnknown + PClient* = ref TClient + TClient* = object of TObject + id*: int32 + addy*: TupAddress + clientID*: uint16 + auth*: bool + outputBuf*: PStringStream + case kind*: TClientType + of CPlayer: + alias*: string + session*: string + lastPing*: float + failedPings*: int + of CServer: + record*: ScZoneRecord + cfg*: TChecksumFile + of CUnknown: nil + TChecksumFile* = object + unpackedSize*: int + sum*: MD5Digest + compressed*: string + TupAddress* = tuple[host: string, port: int16] + PIDGen*[T: Ordinal] = ref TIDGen[T] + TIDGen[T: Ordinal] = object + max: T + freeIDs: seq[T] +var cliID = newIdGen[int32]() + +proc sendMessage*(client: PClient; txt: string) +proc sendError*(client: PClient; txt: string) +proc `$`*(client: PClient): string + +proc newIncomingBuffer*(size = 1024): PStringStream = + result = newStringStream("") + result.data.setLen size + result.data.setLen 0 + result.flushImpl = proc(stream: PStream) = + stream.setPosition(0) + PStringStream(stream).data.setLen(0) + + +proc free*(c: PClient) = + echo "Client freed: ", c + cliID.del c.id + c.outputBuf.flush() + c.outputBuf = nil +proc newClient*(addy: TupAddress): PClient = + new(result, free) + result.addy = addy + result.outputBuf = newStringStream("") + result.outputBuf.flushImpl = proc(stream: PStream) = + stream.setPosition 0 + PStringStream(stream).data.setLen 0 + +proc loginPlayer*(client: PClient; login: CsLogin): bool = + if client.auth: + client.sendError("You are already logged in.") + return + client.id = cliID.next() + client.auth = true + client.kind = CPlayer + client.alias = login.alias + client.session = getMD5(client.alias & $rand(10000)) + result = true + +proc `$`*(client: PClient): string = + if not client.auth: return $client.addy + case client.kind + of CPlayer: result = client.alias + of CServer: result = client.record.name + else: result = $client.addy +proc send*[T](client: PClient; pktType: char; pkt: var T) = + client.outputBuf.write(pktType) + pkt.pack(client.outputBuf) + +proc sendMessage*(client: PClient; txt: string) = + var m = newScChat(CSystem, text = txt) + client.send HChat, m +proc sendError*(client: PClient; txt: string) = + var m = newScChat(CError, text = txt) + client.send HChat, m + +proc checksumFile*(filename: string): TChecksumFile = + let fullText = readFile(filename) + result.unpackedSize = fullText.len + result.sum = toMD5(fullText) + result.compressed = compress(fullText) +proc checksumStr*(str: string): TChecksumFile = + result.unpackedSize = str.len + result.sum = toMD5(str) + result.compressed = compress(str) + diff --git a/tests/manyloc/keineschweine/server/old_sg_server.nim b/tests/manyloc/keineschweine/server/old_sg_server.nim new file mode 100644 index 000000000..ac85cbf62 --- /dev/null +++ b/tests/manyloc/keineschweine/server/old_sg_server.nim @@ -0,0 +1,254 @@ +import + sockets, times, streams, streams_enh, tables, json, os, unsigned, + sg_packets, sg_assets, md5, server_utils, client_helpers +var + dirServer: PServer + thisZone = newScZoneRecord("local", "sup") + thisZoneSettings: PZoneSettings + dirServerConnected = false + ## I was high. + clients = initTable[TupAddress, PClient](16) + alias2client = initTable[string, PClient](32) + allClients: seq[PClient] = @[] + zonePlayers: seq[PClient] = @[] +const + PubChatDelay = 100/1000 #100 ms + +import hashes +proc hash*(x: uint16): THash {.inline.} = + result = int32(x) + +proc findClient*(host: string; port: int16): PClient = + let addy: TupAddress = (host, port) + if clients.hasKey(addy): + return clients[addy] + result = newClient(addy) + clients[addy] = result + allClients.add(result) + + +proc sendZoneList(client: PClient) = + echo(">> zonelist ", client) + #client.send(HZonelist, zonelist) + +proc forwardPrivate(rcv: PClient; sender: PClient; txt: string) = + var m = newScChat(CPriv, sender.alias, txt) + rcv.send(HChat, m) +proc sendChat(client: PClient; kind: ChatType; txt: string) = + echo(">> chat ", client) + var m = newScChat(kind, "", txt) + client.send(HChat, m) + +var pubChatQueue = newStringStream("") +pubChatQueue.flushImpl = proc(stream: PStream) = + stream.setPosition(0) + PStringStream(stream).data.setLen(0) +proc queuePub(sender: string, msg: CsChat) = + var chat = newScChat(kind = CPub, fromPlayer = sender, text = msg.text) + pubChatQueue.write(HChat) + chat.pack(pubChatQueue) + +handlers[HHello] = (proc(client: PClient; stream: PStream) = + var h = readCsHello(stream) + if h.i == 14: + var greet = newScHello("Well hello there") + client.send(HHello, greet)) +handlers[HLogin] = proc(client: PClient; stream: PStream) = + var loginInfo = readCsLogin(stream) + echo("** login: alias = ", loginInfo.alias) + if not dirServerConnected and client.loginPlayer(loginInfo): + client.sendMessage("Welcome "& client.alias) + alias2client[client.alias] = client + client.sendZonelist() +handlers[HZoneList] = proc(client: PClient; stream: PStream) = + var pinfo = readCsZoneList(stream) + echo("** zonelist req") +handlers[HChat] = proc(client: PClient; stream: PStream) = + var chat = readCsChat(stream) + if not client.auth: + client.sendError("You are not logged in.") + return + if chat.target != "": ##private + if alias2client.hasKey(chat.target): + alias2client[chat.target].forwardPrivate(client, chat.text) + else: + queuePub(client.alias, chat) +handlers[HZoneQuery] = proc(client: PClient; stream: PStream) = + echo("Got zone query") + var q = readCsZoneQuery(stream) + var resp = newScZoneQuery(zonePlayers.len.uint16) + client.send(HZoneQuery, resp) + + + +handlers[HZoneJoinReq] = proc(client: PClient; stream: PStream) = + var req = readCsZoneJoinReq(stream) + echo "Join zone request from (",req.session.id,") ", req.session.alias + if client.auth and client.kind == CPlayer: + echo "Client is authenticated, verifying filez" + client.startVerifyingFiles() + elif dirServerConnected: + echo "Dirserver is connected, verifying client" + dirServer.send HVerifyClient, req.session + else: + echo "Dirserver is disconnected =(" + client.startVerifyingFiles() + + + +proc handlePkt(s: PClient; stream: PStream) = + while not stream.atEnd: + var typ = readChar(stream) + if not handlers.hasKey(typ): + break + else: + handlers[typ](s, stream) + +proc createServer(port: TPort) = + if not server.isNil: + server.close() + server = socket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP, buffered = false) + server.bindAddr(port) + +var clientIndex = 0 +var incoming = newIncomingBuffer() +proc poll*(timeout: int = 250) = + if server.isNil: return + var + reads = @[server] + writes = @[server] + if select(reads, timeout) > 0: + var + addy = "" + port: TPort + let res = server.recvFromAsync(incoming.data, 512, addy, port, 0) + if not res: + echo("No recv") + return + else: + var client = findClient(addy, port.int16) + #echo("<< ", res, " ", client.alias, ": ", len(line.data), " ", repr(line.data)) + handlePkt(client, incoming) + incoming.flush() + if selectWrite(writes, timeout) > 0: + let nclients = allClients.len + if nclients == 0: + return + clientIndex = (clientIndex + 1) mod nclients + var c = allClients[clientIndex] + if c.outputBuf.getPosition > 0: + let res = server.sendTo(c.addy.host, c.addy.port.TPort, c.outputBuf.data) + echo("Write ", c, " result: ", res, " data: ", c.outputBuf.data) + c.outputBuf.flush() + +when isMainModule: + import parseopt, matchers, strutils + var zoneCfgFile = "./server_settings.json" + for kind, key, val in getOpt(): + case kind + of cmdShortOption, cmdLongOption: + case key + of "f", "file": + if existsFile(val): + zoneCfgFile = val + else: + echo("File does not exist: ", val) + else: + echo("Unknown option: ", key," ", val) + else: + echo("Unknown option: ", key, " ", val) + var jsonSettings = parseFile(zoneCfgFile) + let + host = jsonSettings["host"].str + port = TPort(jsonSettings["port"].num) + zoneFile = jsonSettings["settings"].str + dirServerInfo = jsonSettings["dirserver"] + + var path = getAppDir()/../"data"/zoneFile + if not existsFile(path): + echo("Zone settings file does not exist: ../data/", zoneFile) + echo(path) + quit(1) + + ## Test file + block: + var + TestFile: FileChallengePair + contents = repeatStr(2, "abcdefghijklmnopqrstuvwxyz") + testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32) + testFile.file = checksumStr(contents) + myAssets.add testFile + + setCurrentDir getAppDir().parentDir() + block: + let zonesettings = readFile(path) + var + errors: seq[string] = @[] + if not loadSettings(zoneSettings, errors): + echo("You have errors in your zone settings:") + for e in errors: echo("**", e) + quit(1) + errors.setLen 0 + + var pair: FileChallengePair + pair.challenge.file = zoneFile + pair.challenge.assetType = FZoneCfg + pair.challenge.fullLen = zoneSettings.len.int32 + pair.file = checksumStr(zoneSettings) + myAssets.add pair + + allAssets: + if not load(asset): + echo "Invalid or missing file ", file + else: + var pair: FileChallengePair + pair.challenge.file = file + pair.challenge.assetType = assetType + pair.challenge.fullLen = getFileSize( + expandPath(assetType, file)).int32 + pair.file = asset.contents + myAssets.add pair + + echo "Zone has ", myAssets.len, " associated assets" + + + dirServer = newServerConnection(dirServerInfo[0].str, dirServerInfo[1].num.TPort) + dirServer.handlers[HDsMsg] = proc(serv: PServer; stream: PStream) = + var m = readDsMsg(stream) + echo("DirServer> ", m.msg) + dirServer.handlers[HZoneLogin] = proc(serv: PServer; stream: PStream) = + let loggedIn = readDsZoneLogin(stream).status + if loggedIn: + dirServerConnected = true + dirServer.writePkt HZoneLogin, login + + thisZone.name = jsonSettings["name"].str + thisZone.desc = jsonSettings["desc"].str + thisZone.ip = "localhost" + thisZone.port = port + var login = newSdZoneLogin( + dirServerInfo[2].str, dirServerInfo[3].str, + thisZone) + #echo "MY LOGIN: ", $login + + + + createServer(port) + echo("Listening on port ", port, "...") + var pubChatTimer = cpuTime()#newClock() + while true: + discard dirServer.pollServer(15) + poll(15) + ## TODO sort this type of thing VV into a queue api + #let now = cpuTime() + if cpuTime() - pubChatTimer > PubChatDelay: #.getElapsedTime.asMilliseconds > 100: + pubChatTimer -= pubChatDelay #.restart() + if pubChatQueue.getPosition > 0: + var cn = 0 + let sizePubChat = pubChatQueue.data.len + for c in allClients: + c.outputBuf.writeData(addr pubChatQueue.data[0], sizePubChat) + pubChatQueue.flush() + + + \ No newline at end of file diff --git a/tests/manyloc/keineschweine/server/sg_lobby.nim b/tests/manyloc/keineschweine/server/sg_lobby.nim new file mode 100644 index 000000000..042d72337 --- /dev/null +++ b/tests/manyloc/keineschweine/server/sg_lobby.nim @@ -0,0 +1,267 @@ + +import + sockets, streams, tables, times, math, strutils, json, os, md5, + sfml, sfml_vector, sfml_colors, + streams_enh, input_helpers, zlib_helpers, client_helpers, sg_packets, sg_assets, sg_gui +type + TClientSettings = object + resolution*: TVideoMode + offlineFile: string + dirserver: tuple[host: string, port: TPort] + website*: string +var + clientSettings: TClientSettings + gui = newGuiContainer() + zonelist = newGuiContainer() + u_alias, u_passwd: PTextEntry + activeInput = 0 + aliasText, passwdText: PText + fpsTimer: PButton + loginBtn: PButton + playBtn: PButton + keyClient = newKeyClient("lobby") + showZonelist = false + chatInput*: PTextEntry + messageArea*: PMessageArea + mySession*: ScLogin +var + dirServer: PServer + zone*: PServer + activeServer: PServer + bConnected = false + outgoing = newStringStream("") + downloadProgress: PButton + connectionButtons: seq[PButton] #buttons that depend on connection to function + +template dispmessage(m: expr): stmt = + messageArea.add(m) +proc connectZone(host: string; port: TPort) +proc connectToDirserv() + +proc writePkt[T](pid: PacketID; p: var T) = + if activeServer.isNil: return + activeServer.writePkt pid, p + +proc setConnected(state: bool) = + if state: + bConnected = true + for b in connectionButtons: enable(b) + else: + bConnected = false + for b in connectionButtons: disable(b) + +proc setActiveZone(ind: int; zone: ScZoneRecord) = + #hilight it or something + dispmessage("Selected " & zone.name) + connectZone(zone.ip, zone.port) + playBtn.enable() + +proc handleChat(serv: PServer; s: PStream) = + var msg = readScChat(s) + messageArea.add(msg) + +proc connectToDirserv() = + if dirServer.isNil: + dirServer = newServerConnection(clientSettings.dirserver.host, clientSettings.dirserver.port) + dirServer.handlers[HHello] = proc(serv: PServer; s: PStream) = + let msg = readScHello(s) + dispMessage(msg.resp) + setConnected(true) + dirServer.handlers[HLogin] = proc(serv: PServer; s: PStream) = + mySession = readScLogin(s) + ##do something here + dirServer.handlers[HZonelist] = proc(serv: PServer; s: PStream) = + var + info = readScZonelist(s) + zones = info.zones + if zones.len > 0: + zonelist.clearButtons() + var pos = vec2f(0.0, 0.0) + zonelist.newButton( + text = "Zonelist - "& info.network, + position = pos, + onClick = proc(b: PButton) = + dispmessage("Click on header")) + pos.y += 20 + for i in 0..zones.len - 1: + var z = zones[i] + zonelist.newButton( + text = z.name, position = pos, + onClick = proc(b: PButton) = + setActiveZone(i, z)) + pos.y += 20 + showZonelist = true + dirServer.handlers[HPoing] = proc(serv: PServer; s: PStream) = + var ping = readPoing(s) + dispmessage("Ping: "& $ping.time) + ping.time = epochTime().float32 + serv.writePkt HPoing, ping + dirServer.handlers[HChat] = handleChat + dirServer.handlers[HFileChallenge] = handleFileChallenge + var hello = newCsHello() + dirServer.writePkt HHello, hello + activeServer = dirServer + + +proc zoneListReq() = + var pkt = newCsZonelist("sup") + writePkt HZonelist, pkt + +##key handlers +keyClient.registerHandler(MouseMiddle, down, proc() = + gui.setPosition(getMousePos())) + +keyClient.registerHandler(KeyO, down, proc() = + if keyPressed(KeyRShift): echo(repr(outgoing))) +keyClient.registerHandler(KeyTab, down, proc() = + activeInput = (activeInput + 1) mod 2) #does this work? +keyClient.registerHandler(MouseLeft, down, proc() = + let p = getMousePos() + gui.click(p) + if showZonelist: zonelist.click(p)) +var mptext = newText("", guiFont, 16) +keyClient.registerHandler(MouseRight, down, proc() = + let p = getMousePos() + mptext.setPosition(p) + mptext.setString("($1,$2)"%[$p.x.int,$p.y.int])) + + +proc connectZone(host: string, port: TPort) = + echo "Connecting to zone at ", host, ':', port + if zone.isNil: + zone = newServerConnection(host, port) + zone.handlers[HFileChallenge] = handleFileChallenge + zone.handlers[HChallengeResult] = handleFileChallengeResult + zone.handlers[HFileTransfer] = handleFileTransfer + zone.handlers[HChat] = handleChat + else: + zone.sock.connect(host, port) + var hello = newCsHello() + zone.writePkt HHello, hello + + + +proc lobbyReady*() = + keyClient.setActive() + gui.setActive(u_alias) + +proc tryConnect*(b: PButton) = + connectToDirserv() +proc tryLogin*(b: PButton) = + var login = newCsLogin( + alias = u_alias.getText(), + passwd = u_passwd.getText()) + writePkt HLogin, login +proc tryTransition*(b: PButton) = + ##check if we're logged in + # + #var joinReq = newCsJ + zone.writePkt HZoneJoinReq, mySession + #var errors: seq[string] = @[] + #if loadSettings("", errors): + # transition() + #else: + # for e in errors: dispmessage(e) +proc playOffline*(b: PButton) = + var errors: seq[string] = @[] + if loadSettingsFromFile(clientSettings.offlineFile, errors): + transition() + else: + dispmessage("Errors reading the file ("& clientSettings.offlineFile &"):") + for e in errors: dispmessage(e) + +proc getClientSettings*(): TClientSettings = + result = clientSettings + +proc lobbyInit*() = + var s = json.parseFile("./client_settings.json") + clientSettings.offlineFile = "data/" + clientSettings.offlineFile.add s["default-file"].str + let dirserv = s["directory-server"] + clientSettings.dirserver.host = dirserv["host"].str + clientSettings.dirserver.port = dirserv["port"].num.TPort + clientSettings.resolution.width = s["resolution"][0].num.cint + clientSettings.resolution.height= s["resolution"][1].num.cint + clientSettings.resolution.bitsPerPixel = s["resolution"][2].num.cint + clientSettings.website = s["website"].str + zonelist.setPosition(vec2f(200.0, 100.0)) + connectionButtons = @[] + + downloadProgress = gui.newButton( + text = "", position = vec2f(10, 130), onClick = nil) + downloadProgress.bg.setFillColor(color(34, 139, 34)) + downloadProgress.bg.setSize(vec2f(0, 0)) + + var pos = vec2f(10, 10) + u_alias = gui.newTextEntry( + if s.existsKey("alias"): s["alias"].str else: "alias", + pos) + pos.y += 20 + u_passwd = gui.newTextEntry("buzz", pos) + pos.y += 20 + connectionButtons.add(gui.newButton( + text = "Login", + position = pos, + onClick = tryLogin, + startEnabled = false)) + pos.y += 20 + fpsText.setPosition(pos) + + playBtn = gui.newButton( + text = "Play", + position = vec2f(680.0, 8.0), + onClick = tryTransition, + startEnabled = false) + gui.newButton( + text = "Play Offline", + position = vec2f(680.0, 28.0), + onClick = playOffline) + fpsTimer = gui.newButton( + text = "FPS: ", + position = vec2f(10.0, 70.0), + onClick = proc(b: PButton) = nil) + gui.newButton( + text = "Connect", + position = vec2f(10.0, 90.0), + onClick = tryConnect) + connectionButtons.add(gui.newButton( + text = "Test Chat", + position = vec2f(10.0, 110.0), + onClick = (proc(b: PButton) = + var pkt = newCsChat(text = "ohai") + writePkt HChat, pkt), + startEnabled = false)) + chatInput = gui.newTextEntry("...", vec2f(10.0, 575.0), proc() = + sendChat dirServer, chatInput.getText() + chatInput.clearText()) + messageArea = gui.newMessageArea(vec2f(10.0, 575.0 - 20.0)) + messageArea.sizeVisible = 25 + gui.newButton(text = "Scrollback + 1", position = vec2f(185, 10), onClick = proc(b: PButton) = + messageArea.scrollBack += 1 + update(messageArea)) + gui.newButton(text = "Scrollback - 1", position = vec2f(185+160, 10), onClick = proc(b: PButton) = + messageArea.scrollBack -= 1 + update(messageArea)) + gui.newButton(text = "Flood msg area", position = vec2f(185, 30), onClick = proc(b: PButton) = + for i in 0.. <30: + dispMessage($i)) + +var i = 0 +proc lobbyUpdate*(dt: float) = + #let res = disp.poll() + gui.update(dt) + i = (i + 1) mod 60 + if i == 0: + fpsTimer.setString("FPS: "& $round(1.0/dt)) + if not pollServer(dirServer, 5) and bConnected: + setConnected(false) + echo("Lost connection") + discard pollServer(zone, 5) + +proc lobbyDraw*(window: PRenderWindow) = + window.clear(Black) + window.draw messageArea + window.draw mptext + window.draw gui + if showZonelist: window.draw zonelist + window.display() diff --git a/tests/readme.txt b/tests/readme.txt old mode 100755 new mode 100644 diff --git a/tests/rectest.nim b/tests/rectest.nim old mode 100755 new mode 100644 diff --git a/tests/reject/99bottles.nim b/tests/reject/99bottles.nim old mode 100755 new mode 100644 diff --git a/tests/reject/mambsym1.nim b/tests/reject/mambsym1.nim old mode 100755 new mode 100644 diff --git a/tests/reject/mambsym2.nim b/tests/reject/mambsym2.nim old mode 100755 new mode 100644 diff --git a/tests/reject/mbind4.nim b/tests/reject/mbind4.nim old mode 100755 new mode 100644 diff --git a/tests/reject/mnamspc1.nim b/tests/reject/mnamspc1.nim old mode 100755 new mode 100644 diff --git a/tests/reject/mnamspc2.nim b/tests/reject/mnamspc2.nim old mode 100755 new mode 100644 diff --git a/tests/reject/mopaque.nim b/tests/reject/mopaque.nim old mode 100755 new mode 100644 index b7c5180fd..7eee4bd96 --- a/tests/reject/mopaque.nim +++ b/tests/reject/mopaque.nim @@ -3,3 +3,5 @@ type line*: int filename*: string buffer: cstring + +proc noProcVar*(): int = 18 diff --git a/tests/reject/t99bott.nim b/tests/reject/t99bott.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tadrdisc.nim b/tests/reject/tadrdisc.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tambsym.nim b/tests/reject/tambsym.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tambsym3.nim b/tests/reject/tambsym3.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tarraycons.nim b/tests/reject/tarraycons.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tatomic.nim b/tests/reject/tatomic.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tbind2.nim b/tests/reject/tbind2.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tbind4.nim b/tests/reject/tbind4.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tblock1.nim b/tests/reject/tblock1.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tcaseoverlaprange2.nim b/tests/reject/tcaseoverlaprange2.nim new file mode 100644 index 000000000..d6e301508 --- /dev/null +++ b/tests/reject/tcaseoverlaprange2.nim @@ -0,0 +1,18 @@ +discard """ + line: 13 + errormsg: "duplicate case label" +""" + + + + +proc checkDuplicates(myval: int32): bool = + case myval + of 0x7B: + echo "this should not compile" + of 0x78 .. 0x7D: + result = true + else: + nil + +echo checkDuplicates(0x7B) diff --git a/tests/reject/tconstr1.nim b/tests/reject/tconstr1.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tconstraints.nim b/tests/reject/tconstraints.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tdeprecated.nim b/tests/reject/tdeprecated.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tdestructor.nim b/tests/reject/tdestructor.nim new file mode 100644 index 000000000..da9192a3f --- /dev/null +++ b/tests/reject/tdestructor.nim @@ -0,0 +1,21 @@ +discard """ + line: 20 + errormsg: " usage of a type with a destructor in a non destructible context" +""" + +type + TMyObj = object + x, y: int + p: pointer + +proc destruct(o: var TMyObj) {.destructor.} = + if o.p != nil: dealloc o.p + +proc open: TMyObj = + result = TMyObj(x: 1, y: 2, p: alloc(3)) + + +proc `$`(x: TMyObj): string = $x.y + +echo open() + diff --git a/tests/reject/teffects1.nim b/tests/reject/teffects1.nim index 8db8c764d..440117265 100644 --- a/tests/reject/teffects1.nim +++ b/tests/reject/teffects1.nim @@ -1,5 +1,5 @@ discard """ - line: 49 + line: 51 file: "sysio.nim" errormsg: "can raise an unlisted exception: ref EIO" """ diff --git a/tests/reject/temptycaseobj.nim b/tests/reject/temptycaseobj.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tenummix.nim b/tests/reject/tenummix.nim index 5d141128c..22c07f14a 100644 --- a/tests/reject/tenummix.nim +++ b/tests/reject/tenummix.nim @@ -1,6 +1,6 @@ discard """ file: "system.nim" - line: 670 + line: 695 errormsg: "type mismatch" """ diff --git a/tests/reject/tillrec.nim b/tests/reject/tillrec.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tinc.nim b/tests/reject/tinc.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tinout.nim b/tests/reject/tinout.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tinvalidnewseq.nim b/tests/reject/tinvalidnewseq.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tinvwhen.nim b/tests/reject/tinvwhen.nim old mode 100755 new mode 100644 diff --git a/tests/reject/titer4.nim b/tests/reject/titer4.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tmethod.nim b/tests/reject/tmethod.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tnamedparams.nim b/tests/reject/tnamedparams.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tnamspc.nim b/tests/reject/tnamspc.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tno_int_in_bool_context.nim b/tests/reject/tno_int_in_bool_context.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tnoop.nim b/tests/reject/tnoop.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tnot.nim b/tests/reject/tnot.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tnotnil.nim b/tests/reject/tnotnil.nim index b02e33713..fba7fa917 100644 --- a/tests/reject/tnotnil.nim +++ b/tests/reject/tnotnil.nim @@ -1,6 +1,6 @@ discard """ line: 22 - errormgs: "type mismatch" + errormsg: "type mismatch" """ type diff --git a/tests/reject/topaque.nim b/tests/reject/topaque.nim old mode 100755 new mode 100644 diff --git a/tests/reject/topena1.nim b/tests/reject/topena1.nim old mode 100755 new mode 100644 diff --git a/tests/reject/toverl.nim b/tests/reject/toverl.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tprocvar.nim b/tests/reject/tprocvar.nim old mode 100755 new mode 100644 diff --git a/tests/reject/trawstr.nim b/tests/reject/trawstr.nim old mode 100755 new mode 100644 diff --git a/tests/reject/trecinca.nim b/tests/reject/trecinca.nim old mode 100755 new mode 100644 index bedea8d7e..73a0ec937 --- a/tests/reject/trecinca.nim +++ b/tests/reject/trecinca.nim @@ -1,7 +1,7 @@ discard """ file: "tests/reject/trecincb.nim" line: 9 - errormsg: "recursive dependency: 'trecincb.nim'" + errormsg: "recursive dependency: 'tests/reject/trecincb.nim'" """ # Test recursive includes diff --git a/tests/reject/trecincb.nim b/tests/reject/trecincb.nim old mode 100755 new mode 100644 diff --git a/tests/reject/treciter.nim b/tests/reject/treciter.nim old mode 100755 new mode 100644 diff --git a/tests/reject/trectype.nim b/tests/reject/trectype.nim old mode 100755 new mode 100644 diff --git a/tests/reject/trefs.nim b/tests/reject/trefs.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tsidee1.nim b/tests/reject/tsidee1.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tsidee4.nim b/tests/reject/tsidee4.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tsimtych.nim b/tests/reject/tsimtych.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tstatret.nim b/tests/reject/tstatret.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tstmtexp.nim b/tests/reject/tstmtexp.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tsubrange.nim b/tests/reject/tsubrange.nim new file mode 100644 index 000000000..b4a333bf3 --- /dev/null +++ b/tests/reject/tsubrange.nim @@ -0,0 +1,21 @@ +discard """ + line: 20 + errormsg: "cannot convert 60 to TRange" +""" + +type + TRange = range[0..40] + +proc p(r: TRange) = + nil + +var + r: TRange + y = 50 +r = y + +p y + +const + myConst: TRange = 60 + diff --git a/tests/reject/ttempl2.nim b/tests/reject/ttempl2.nim old mode 100755 new mode 100644 diff --git a/tests/reject/ttypelessemptyset.nim b/tests/reject/ttypelessemptyset.nim old mode 100755 new mode 100644 diff --git a/tests/reject/ttypenoval.nim b/tests/reject/ttypenoval.nim old mode 100755 new mode 100644 diff --git a/tests/reject/tunderscores.nim b/tests/reject/tunderscores.nim old mode 100755 new mode 100644 diff --git a/tests/reject/twrongtupleaccess.nim b/tests/reject/twrongtupleaccess.nim old mode 100755 new mode 100644 diff --git a/tests/reject/typredef.nim b/tests/reject/typredef.nim old mode 100755 new mode 100644 diff --git a/tests/rodfiles/gtkex1.nim b/tests/rodfiles/gtkex1.nim old mode 100755 new mode 100644 diff --git a/tests/rodfiles/gtkex2.nim b/tests/rodfiles/gtkex2.nim old mode 100755 new mode 100644 diff --git a/tests/rodfiles/hallo.nim b/tests/rodfiles/hallo.nim old mode 100755 new mode 100644 diff --git a/tests/run/mambsys1.nim b/tests/run/mambsys1.nim old mode 100755 new mode 100644 diff --git a/tests/run/mambsys2.nim b/tests/run/mambsys2.nim old mode 100755 new mode 100644 diff --git a/tests/run/mbind3.nim b/tests/run/mbind3.nim old mode 100755 new mode 100644 diff --git a/tests/run/minit.nim b/tests/run/minit.nim old mode 100755 new mode 100644 diff --git a/tests/run/mmultim3.nim b/tests/run/mmultim3.nim old mode 100755 new mode 100644 diff --git a/tests/run/tack.nim b/tests/run/tack.nim old mode 100755 new mode 100644 diff --git a/tests/run/tambsym2.nim b/tests/run/tambsym2.nim old mode 100755 new mode 100644 diff --git a/tests/run/tambsys.nim b/tests/run/tambsys.nim old mode 100755 new mode 100644 diff --git a/tests/run/tarray.nim b/tests/run/tarray.nim old mode 100755 new mode 100644 diff --git a/tests/run/tarray2.nim b/tests/run/tarray2.nim old mode 100755 new mode 100644 diff --git a/tests/run/tarray3.nim b/tests/run/tarray3.nim old mode 100755 new mode 100644 diff --git a/tests/run/tarraycons.nim b/tests/run/tarraycons.nim old mode 100755 new mode 100644 diff --git a/tests/run/tassert.nim b/tests/run/tassert.nim old mode 100755 new mode 100644 diff --git a/tests/run/tasynciossl.nim b/tests/run/tasynciossl.nim index e5fb9610c..fbed46efb 100644 --- a/tests/run/tasynciossl.nim +++ b/tests/run/tasynciossl.nim @@ -26,7 +26,7 @@ proc swarmConnect(s: PAsyncSocket) = proc serverRead(s: PAsyncSocket) = var line = "" - assert s.recvLine(line) + assert s.readLine(line) if line != "": #echo(line) if line.startsWith("Message "): diff --git a/tests/run/tbind1.nim b/tests/run/tbind1.nim old mode 100755 new mode 100644 diff --git a/tests/run/tbind3.nim b/tests/run/tbind3.nim old mode 100755 new mode 100644 diff --git a/tests/run/tbintre2.nim b/tests/run/tbintre2.nim old mode 100755 new mode 100644 diff --git a/tests/run/tbintree.nim b/tests/run/tbintree.nim old mode 100755 new mode 100644 diff --git a/tests/run/tbug499771.nim b/tests/run/tbug499771.nim old mode 100755 new mode 100644 index 633ab39f6..682148422 --- a/tests/run/tbug499771.nim +++ b/tests/run/tbug499771.nim @@ -1,12 +1,14 @@ discard """ file: "tbug499771.nim" - output: "TSubRange: 5 from 1 to 10" + output: '''TSubRange: 5 from 1 to 10 +true true true''' """ -type TSubRange = range[1 .. 10] +type + TSubRange = range[1 .. 10] + TEnum = enum A, B, C var sr: TSubRange = 5 echo("TSubRange: " & $sr & " from " & $low(TSubRange) & " to " & $high(TSubRange)) - - - +const cset = {A} + {B} +echo A in cset, " ", B in cset, " ", C notin cset diff --git a/tests/run/tbug511622.nim b/tests/run/tbug511622.nim old mode 100755 new mode 100644 diff --git a/tests/run/tcase_setconstr.nim b/tests/run/tcase_setconstr.nim old mode 100755 new mode 100644 diff --git a/tests/run/tcasestm.nim b/tests/run/tcasestm.nim old mode 100755 new mode 100644 diff --git a/tests/run/tcgbug.nim b/tests/run/tcgbug.nim old mode 100755 new mode 100644 diff --git a/tests/run/tclosure.nim b/tests/run/tclosure.nim old mode 100755 new mode 100644 diff --git a/tests/run/tcnstseq.nim b/tests/run/tcnstseq.nim old mode 100755 new mode 100644 diff --git a/tests/run/tconstr2.nim b/tests/run/tconstr2.nim old mode 100755 new mode 100644 diff --git a/tests/run/tcontinuexc.nim b/tests/run/tcontinuexc.nim old mode 100755 new mode 100644 diff --git a/tests/run/tcopy.nim b/tests/run/tcopy.nim old mode 100755 new mode 100644 diff --git a/tests/run/tcountup.nim b/tests/run/tcountup.nim old mode 100755 new mode 100644 diff --git a/tests/run/tcurrncy.nim b/tests/run/tcurrncy.nim old mode 100755 new mode 100644 diff --git a/tests/run/tdestructor.nim b/tests/run/tdestructor.nim new file mode 100644 index 000000000..8aae2fce2 --- /dev/null +++ b/tests/run/tdestructor.nim @@ -0,0 +1,26 @@ +discard """ + output: '''some text +Destructor called!''' +""" + +type + TMyObj = object + x, y: int + p: pointer + +proc destruct(o: var TMyObj) {.destructor.} = + if o.p != nil: dealloc o.p + echo "Destructor called!" + +proc open: TMyObj = + # allow for superfluous () + result = (TMyObj(x: 1, y: 2, p: alloc(3))) + + +proc `$`(x: TMyObj): string = $x.y + +proc main() = + var x = open() + echo "some text" + +main() diff --git a/tests/run/temit.nim b/tests/run/temit.nim old mode 100755 new mode 100644 diff --git a/tests/run/tenumhole.nim b/tests/run/tenumhole.nim old mode 100755 new mode 100644 diff --git a/tests/run/texcsub.nim b/tests/run/texcsub.nim old mode 100755 new mode 100644 diff --git a/tests/run/texplicitgeneric1.nim b/tests/run/texplicitgeneric1.nim old mode 100755 new mode 100644 diff --git a/tests/run/texplicitgeneric2.nim b/tests/run/texplicitgeneric2.nim old mode 100755 new mode 100644 diff --git a/tests/run/tfieldindex.nim b/tests/run/tfieldindex.nim new file mode 100644 index 000000000..3ffa65e58 --- /dev/null +++ b/tests/run/tfieldindex.nim @@ -0,0 +1,21 @@ +discard """ + output: "1" +""" + +type + TMyTuple = tuple[a, b: int] + +proc indexOf*(t: typedesc, name: string): int {.compiletime.} = + ## takes a tuple and looks for the field by name. + ## returs index of that field. + var + d: t + i = 0 + for n, x in fieldPairs(d): + if n == name: return i + i.inc + raise newException(EInvalidValue, "No field " & name & " in type " & + astToStr(t)) + +echo TMyTuple.indexOf("b") + diff --git a/tests/run/tfielditerator.nim b/tests/run/tfielditerator.nim old mode 100755 new mode 100644 diff --git a/tests/run/tfielditerator2.nim b/tests/run/tfielditerator2.nim new file mode 100644 index 000000000..76fa568f2 --- /dev/null +++ b/tests/run/tfielditerator2.nim @@ -0,0 +1,64 @@ +discard """ + output: ''' +a char: true +a char: false +an int: 5 +an int: 6 +a string: abc +false +true +true +false +true +a: a +b: b +x: 5 +y: 6 +z: abc +myDisc: enC +c: Z +enC +Z +''' +""" + +type + TMyObj = object + a, b: char + x, y: int + z: string + + TEnum = enum enA, enB, enC + TMyCaseObj = object + case myDisc: TEnum + of enA: a: int + of enB: b: string + of enC: c: char + +proc p(x: char) = echo "a char: ", x <= 'a' +proc p(x: int) = echo "an int: ", x +proc p(x: string) = echo "a string: ", x + +proc myobj(a, b: char, x, y: int, z: string): TMyObj = + result.a = a; result.b = b; result.x = x; result.y = y; result.z = z + +var x = myobj('a', 'b', 5, 6, "abc") +var y = myobj('A', 'b', 5, 9, "abc") + +for f in fields(x): + p f + +for a, b in fields(x, y): + echo a == b + +for key, val in fieldPairs(x): + echo key, ": ", val + +var co: TMyCaseObj +co.myDisc = enC +co.c = 'Z' +for key, val in fieldPairs(co): + echo key, ": ", val + +for val in fields(co): + echo val diff --git a/tests/run/tfinally.nim b/tests/run/tfinally.nim old mode 100755 new mode 100644 index 29313c3fd..273aac72b --- a/tests/run/tfinally.nim +++ b/tests/run/tfinally.nim @@ -1,6 +1,8 @@ discard """ file: "tfinally.nim" - output: "came here 3" + output: '''came +here +3''' """ # Test return in try statement: @@ -9,10 +11,10 @@ proc main: int = try: return 1 finally: - stdout.write("came ") + echo("came") return 2 finally: - stdout.write("here ") + echo("here ") return 3 echo main() #OUT came here 3 diff --git a/tests/run/tfinally2.nim b/tests/run/tfinally2.nim old mode 100755 new mode 100644 index 3ed212a7c..e1e8d4c7e --- a/tests/run/tfinally2.nim +++ b/tests/run/tfinally2.nim @@ -1,6 +1,9 @@ discard """ file: "tfinally2.nim" - output: "ABCD" + output: '''A +B +C +D''' """ # Test break in try statement: @@ -11,15 +14,15 @@ proc main: int = try: break AB finally: - stdout.write("A") - stdout.write("skipped") + echo("A") + echo("skipped") finally: block B: - stdout.write("B") - stdout.write("skipped") - stdout.write("C") + echo("B") + echo("skipped") + echo("C") finally: - stdout.writeln("D") + echo("D") discard main() #OUT ABCD diff --git a/tests/run/tfinally3.nim b/tests/run/tfinally3.nim old mode 100755 new mode 100644 diff --git a/tests/run/tfloat1.nim b/tests/run/tfloat1.nim old mode 100755 new mode 100644 diff --git a/tests/run/tfloat2.nim b/tests/run/tfloat2.nim old mode 100755 new mode 100644 diff --git a/tests/run/tfloat3.nim b/tests/run/tfloat3.nim old mode 100755 new mode 100644 diff --git a/tests/run/tformat.nim b/tests/run/tformat.nim old mode 100755 new mode 100644 diff --git a/tests/run/tgenericassign.nim b/tests/run/tgenericassign.nim old mode 100755 new mode 100644 diff --git a/tests/run/tgenericassigntuples.nim b/tests/run/tgenericassigntuples.nim old mode 100755 new mode 100644 diff --git a/tests/run/tgenerics1.nim b/tests/run/tgenerics1.nim old mode 100755 new mode 100644 diff --git a/tests/run/thintoff.nim b/tests/run/thintoff.nim old mode 100755 new mode 100644 diff --git a/tests/run/tinit.nim b/tests/run/tinit.nim old mode 100755 new mode 100644 diff --git a/tests/run/tints.nim b/tests/run/tints.nim old mode 100755 new mode 100644 diff --git a/tests/run/titer3.nim b/tests/run/titer3.nim old mode 100755 new mode 100644 diff --git a/tests/run/titer5.nim b/tests/run/titer5.nim old mode 100755 new mode 100644 diff --git a/tests/run/titer6.nim b/tests/run/titer6.nim old mode 100755 new mode 100644 diff --git a/tests/run/titerslice.nim b/tests/run/titerslice.nim new file mode 100644 index 000000000..e5d2e14a3 --- /dev/null +++ b/tests/run/titerslice.nim @@ -0,0 +1,9 @@ +discard """ + output: '''2 +3 +4''' +""" + +var t1 = @["1", "2", "3", "4"] +for t in t1[1..3]: + echo t diff --git a/tests/run/titervaropenarray.nim b/tests/run/titervaropenarray.nim old mode 100755 new mode 100644 diff --git a/tests/run/tkoeniglookup.nim b/tests/run/tkoeniglookup.nim old mode 100755 new mode 100644 diff --git a/tests/run/tlenopenarray.nim b/tests/run/tlenopenarray.nim deleted file mode 100755 index 3ab27c8f7..000000000 --- a/tests/run/tlenopenarray.nim +++ /dev/null @@ -1,7 +0,0 @@ -discard """ - file: "tlenopenarray.nim" - output: "1" -""" - -echo len([1_000_000]) #OUT 1 - diff --git a/tests/run/tlists.nim b/tests/run/tlists.nim old mode 100755 new mode 100644 diff --git a/tests/run/tlocals.nim b/tests/run/tlocals.nim new file mode 100644 index 000000000..94a34139b --- /dev/null +++ b/tests/run/tlocals.nim @@ -0,0 +1,11 @@ +discard """ + output: "(x: string here, a: 1, b: 3)" +""" + +proc simple[T](a, b: T) = + var + x = "string here" + echo locals() + +simple(1, 3) + diff --git a/tests/run/tlowhigh.nim b/tests/run/tlowhigh.nim old mode 100755 new mode 100644 diff --git a/tests/run/tmacro2.nim b/tests/run/tmacro2.nim old mode 100755 new mode 100644 diff --git a/tests/run/tmacro3.nim b/tests/run/tmacro3.nim old mode 100755 new mode 100644 diff --git a/tests/run/tmacro4.nim b/tests/run/tmacro4.nim old mode 100755 new mode 100644 diff --git a/tests/run/tmacros1.nim b/tests/run/tmacros1.nim old mode 100755 new mode 100644 diff --git a/tests/run/tmatrix.nim b/tests/run/tmatrix.nim old mode 100755 new mode 100644 diff --git a/tests/run/tmethods1.nim b/tests/run/tmethods1.nim old mode 100755 new mode 100644 index adcca9e19..f4add6af4 --- a/tests/run/tmethods1.nim +++ b/tests/run/tmethods1.nim @@ -5,6 +5,18 @@ discard """ method somethin(obj: TObject) = echo "do nothing" +type + TNode* = object {.inheritable.} + PNode* = ref TNode + + PNodeFoo* = ref object of TNode + + TSomethingElse = object + PSomethingElse = ref TSomethingElse + +method foo(a: PNode, b: PSomethingElse) = nil +method foo(a: PNodeFoo, b: PSomethingElse) = nil + var o: TObject o.somethin() diff --git a/tests/run/tmultim1.nim b/tests/run/tmultim1.nim old mode 100755 new mode 100644 diff --git a/tests/run/tmultim2.nim b/tests/run/tmultim2.nim old mode 100755 new mode 100644 diff --git a/tests/run/tmultim3.nim b/tests/run/tmultim3.nim old mode 100755 new mode 100644 diff --git a/tests/run/tmultim4.nim b/tests/run/tmultim4.nim old mode 100755 new mode 100644 diff --git a/tests/run/tnamedenumfields.nim b/tests/run/tnamedenumfields.nim old mode 100755 new mode 100644 diff --git a/tests/run/tnestif.nim b/tests/run/tnestif.nim old mode 100755 new mode 100644 diff --git a/tests/run/tnestprc.nim b/tests/run/tnestprc.nim old mode 100755 new mode 100644 diff --git a/tests/run/tnewderef.nim b/tests/run/tnewderef.nim old mode 100755 new mode 100644 diff --git a/tests/run/tnodeadlocks.nim b/tests/run/tnodeadlocks.nim old mode 100755 new mode 100644 diff --git a/tests/run/tobjconstr.nim b/tests/run/tobjconstr.nim new file mode 100644 index 000000000..3bd785728 --- /dev/null +++ b/tests/run/tobjconstr.nim @@ -0,0 +1,41 @@ +discard """ + output: '''(k: kindA, a: (x: abc, z: [1, 1, 3]), empty: ()) +(k: kindA, a: (x: abc, z: [1, 2, 3]), empty: ()) +(k: kindA, a: (x: abc, z: [1, 3, 3]), empty: ()) +(k: kindA, a: (x: abc, z: [1, 4, 3]), empty: ()) +(k: kindA, a: (x: abc, z: [1, 5, 3]), empty: ()) +(k: kindA, a: (x: abc, z: [1, 6, 3]), empty: ()) +(k: kindA, a: (x: abc, z: [1, 7, 3]), empty: ()) +(k: kindA, a: (x: abc, z: [1, 8, 3]), empty: ()) +(k: kindA, a: (x: abc, z: [1, 9, 3]), empty: ()) +(k: kindA, a: (x: abc, z: [1, 10, 3]), empty: ())''' +""" + +type + TArg = object + x: string + z: seq[int] + TKind = enum kindXY, kindA + TEmpty = object + TDummy = ref object + case k: TKind + of kindXY: x, y: int + of kindA: + a: TArg + empty: TEmpty + +proc `$`[T](s: seq[T]): string = + # XXX why is that not in the stdlib? + result = "[" + for i, x in s: + if i > 0: result.add(", ") + result.add($x) + result.add("]") + +proc main() = + for i in 1..10: + let d = TDummy(k: kindA, a: TArg(x: "abc", z: @[1,i,3]), empty: TEmpty()) + echo d[] + +main() + diff --git a/tests/run/tobject.nim b/tests/run/tobject.nim new file mode 100644 index 000000000..5fec84441 --- /dev/null +++ b/tests/run/tobject.nim @@ -0,0 +1,15 @@ +import unittest + +type Obj = object + foo: int + +proc makeObj(x: int): Obj = + result.foo = x + +suite "object basic methods": + test "it should convert an object to a string": + var obj = makeObj(1) + # Should be "obj: (foo: 1)" or similar. + check($obj == "(foo: 1)") + test "it should test equality based on fields": + check(makeObj(1) == makeObj(1)) diff --git a/tests/run/tofopr.nim b/tests/run/tofopr.nim old mode 100755 new mode 100644 diff --git a/tests/run/toop1.nim b/tests/run/toop1.nim old mode 100755 new mode 100644 diff --git a/tests/run/topenarrayrepr.nim b/tests/run/topenarrayrepr.nim old mode 100755 new mode 100644 diff --git a/tests/run/topenlen.nim b/tests/run/topenlen.nim old mode 100755 new mode 100644 diff --git a/tests/run/toprprec.nim b/tests/run/toprprec.nim old mode 100755 new mode 100644 diff --git a/tests/run/toverflw.nim b/tests/run/toverflw.nim old mode 100755 new mode 100644 diff --git a/tests/run/toverflw2.nim b/tests/run/toverflw2.nim old mode 100755 new mode 100644 diff --git a/tests/run/toverl2.nim b/tests/run/toverl2.nim old mode 100755 new mode 100644 diff --git a/tests/run/toverl3.nim b/tests/run/toverl3.nim new file mode 100644 index 000000000..b3e0f2fa7 --- /dev/null +++ b/tests/run/toverl3.nim @@ -0,0 +1,20 @@ +discard """ + file: "toverl3.nim" + output: '''m1 +tup1''' +""" + +# Tests more specific generic match: + +proc m[T](x: T) = echo "m2" +proc m[T](x: var ref T) = echo "m1" + +proc tup[S, T](x: tuple[a: S, b: ref T]) = echo "tup1" +proc tup[S, T](x: tuple[a: S, b: T]) = echo "tup2" + +var + obj: ref int + tu: tuple[a: int, b: ref bool] + +m(obj) +tup(tu) diff --git a/tests/run/toverlop.nim b/tests/run/toverlop.nim deleted file mode 100755 index ce302345f..000000000 --- a/tests/run/toverlop.nim +++ /dev/null @@ -1,16 +0,0 @@ -discard """ - file: "toverlop.nim" - output: "3" -""" -# Test operator overloading - -proc `%` (a, b: int): int = - return a mod b - -var x, y: int -x = 15 -y = 6 -write(stdout, x % y) -#OUT 3 - - diff --git a/tests/run/toverwr.nim b/tests/run/toverwr.nim old mode 100755 new mode 100644 diff --git a/tests/run/tovfint.nim b/tests/run/tovfint.nim old mode 100755 new mode 100644 diff --git a/tests/run/tpegs.nim b/tests/run/tpegs.nim old mode 100755 new mode 100644 diff --git a/tests/run/tpos.nim b/tests/run/tpos.nim old mode 100755 new mode 100644 diff --git a/tests/run/tprintf.nim b/tests/run/tprintf.nim old mode 100755 new mode 100644 diff --git a/tests/run/tprocvar.nim b/tests/run/tprocvar.nim old mode 100755 new mode 100644 diff --git a/tests/run/tquotewords.nim b/tests/run/tquotewords.nim old mode 100755 new mode 100644 diff --git a/tests/run/tregex.nim b/tests/run/tregex.nim old mode 100755 new mode 100644 diff --git a/tests/run/treguse.nim b/tests/run/treguse.nim old mode 100755 new mode 100644 diff --git a/tests/run/trepr.nim b/tests/run/trepr.nim old mode 100755 new mode 100644 diff --git a/tests/run/treraise.nim b/tests/run/treraise.nim old mode 100755 new mode 100644 diff --git a/tests/run/tromans.nim b/tests/run/tromans.nim old mode 100755 new mode 100644 diff --git a/tests/run/tseqcon.nim b/tests/run/tseqcon.nim old mode 100755 new mode 100644 diff --git a/tests/run/tseqtuple.nim b/tests/run/tseqtuple.nim old mode 100755 new mode 100644 diff --git a/tests/run/tsets.nim b/tests/run/tsets.nim old mode 100755 new mode 100644 diff --git a/tests/run/tsets2.nim b/tests/run/tsets2.nim old mode 100755 new mode 100644 diff --git a/tests/run/tsidee2.nim b/tests/run/tsidee2.nim old mode 100755 new mode 100644 diff --git a/tests/run/tsidee3.nim b/tests/run/tsidee3.nim old mode 100755 new mode 100644 diff --git a/tests/run/tsimmeth.nim b/tests/run/tsimmeth.nim old mode 100755 new mode 100644 diff --git a/tests/run/tsimplesort.nim b/tests/run/tsimplesort.nim old mode 100755 new mode 100644 diff --git a/tests/run/tslices.nim b/tests/run/tslices.nim old mode 100755 new mode 100644 diff --git a/tests/run/tsortdev.nim b/tests/run/tsortdev.nim old mode 100755 new mode 100644 diff --git a/tests/run/tsplit.nim b/tests/run/tsplit.nim old mode 100755 new mode 100644 diff --git a/tests/run/tstrange.nim b/tests/run/tstrange.nim old mode 100755 new mode 100644 diff --git a/tests/run/tstrlits.nim b/tests/run/tstrlits.nim old mode 100755 new mode 100644 diff --git a/tests/run/tstrutil.nim b/tests/run/tstrutil.nim old mode 100755 new mode 100644 diff --git a/tests/run/tsubrange.nim b/tests/run/tsubrange.nim new file mode 100644 index 000000000..d525eae1e --- /dev/null +++ b/tests/run/tsubrange.nim @@ -0,0 +1,19 @@ +discard """ + file: "tsubrange.nim" + outputsub: "value 50 out of range [EOutOfRange]" + exitcode: "1" +""" + +type + TRange = range[0..40] + +proc p(r: TRange) = + nil + +var + r: TRange + y = 50 +r = y + +#p y + diff --git a/tests/run/tsubrange2.nim b/tests/run/tsubrange2.nim new file mode 100644 index 000000000..42c16812f --- /dev/null +++ b/tests/run/tsubrange2.nim @@ -0,0 +1,17 @@ +discard """ + file: "tsubrange2.nim" + outputsub: "value 50 out of range [EOutOfRange]" + exitcode: "1" +""" + +type + TRange = range[0..40] + +proc p(r: TRange) = + nil + +var + r: TRange + y = 50 +p y + diff --git a/tests/run/ttables.nim b/tests/run/ttables.nim old mode 100755 new mode 100644 diff --git a/tests/run/ttoseq.nim b/tests/run/ttoseq.nim old mode 100755 new mode 100644 diff --git a/tests/run/tunhandledexc.nim b/tests/run/tunhandledexc.nim old mode 100755 new mode 100644 diff --git a/tests/run/tvarargs_vs_generic.nim b/tests/run/tvarargs_vs_generic.nim new file mode 100644 index 000000000..7c1fa67b2 --- /dev/null +++ b/tests/run/tvarargs_vs_generic.nim @@ -0,0 +1,26 @@ +discard """ + output: "direct\nopenarray\nvarargs" +""" + +proc withDirectType(args: string) = + echo "direct" + +proc withDirectType[T](arg: T) = + echo "generic" + +proc withOpenArray(args: openarray[string]) = + echo "openarray" + +proc withOpenArray[T](arg: T) = + echo "generic" + +proc withVarargs(args: varargs[string]) = + echo "varargs" + +proc withVarargs[T](arg: T) = + echo "generic" + +withDirectType "string" +withOpenArray "string" +withVarargs "string" + diff --git a/tests/run/tvardecl.nim b/tests/run/tvardecl.nim old mode 100755 new mode 100644 diff --git a/tests/run/tvariantasgn.nim b/tests/run/tvariantasgn.nim old mode 100755 new mode 100644 diff --git a/tests/run/tvariantstack.nim b/tests/run/tvariantstack.nim old mode 100755 new mode 100644 diff --git a/tests/run/tvarious1.nim b/tests/run/tvarious1.nim new file mode 100644 index 000000000..d30d91234 --- /dev/null +++ b/tests/run/tvarious1.nim @@ -0,0 +1,14 @@ +discard """ + file: "tlenopenarray.nim" + output: '''1 +0''' +""" + +echo len([1_000_000]) #OUT 1 + +type + TArray = array[0..3, int] + TVector = distinct array[0..3, int] +proc `[]`(v: TVector; idx: int): int = TArray(v)[idx] +var v: TVector +echo v[2] diff --git a/tests/run/tvarnums.nim b/tests/run/tvarnums.nim old mode 100755 new mode 100644 diff --git a/tests/run/tvartup.nim b/tests/run/tvartup.nim old mode 100755 new mode 100644 diff --git a/tests/run/tvtable.nim b/tests/run/tvtable.nim old mode 100755 new mode 100644 index 36b67277a..51894618c --- a/tests/run/tvtable.nim +++ b/tests/run/tvtable.nim @@ -15,7 +15,7 @@ type barProc[T] = proc (o: var T) # an untyped table to store the proc pointers - # it's also possible to use a strontly typed tuple here + # it's also possible to use a strongly typed tuple here VTable = array[0..1, pointer] TBase = object {.inheritable.} diff --git a/tests/run/twrongexc.nim b/tests/run/twrongexc.nim old mode 100755 new mode 100644 diff --git a/tests/run/txmlgen.nim b/tests/run/txmlgen.nim old mode 100755 new mode 100644 diff --git a/tests/run/txmltree.nim b/tests/run/txmltree.nim old mode 100755 new mode 100644 diff --git a/tests/specials.nim b/tests/specials.nim index 1818497a4..30bb6f423 100644 --- a/tests/specials.nim +++ b/tests/specials.nim @@ -131,6 +131,7 @@ proc runGcTests(r: var TResults, options: string) = test "gcleak3" test "weakrefs" test "cycleleak" + test "closureleak" # ------------------------- threading tests ----------------------------------- @@ -176,14 +177,12 @@ proc runJsTests(r: var TResults, options: string) = runSingleTest(r, filename, options & " -d:nodejs", targetJS) runSingleTest(r, filename, options & " -d:nodejs -d:release", targetJS) - # texceptions, texcpt1, texcsub, tfinally, tfinally2, - # tfinally3 for t in os.walkFiles("tests/js/t*.nim"): test(t) - test "tests/run/tactiontable" - test "tests/run/tmultim1" - test "tests/run/tmultim3" - test "tests/run/tmultim4" + for testfile in ["texceptions", "texcpt1", "texcsub", "tfinally", + "tfinally2", "tfinally3", "tactiontable", "tmultim1", + "tmultim3", "tmultim4"]: + test "tests/run/" & testfile & ".nim" # ------------------------- register special tests here ----------------------- proc runSpecialTests(r: var TResults, options: string) = @@ -199,6 +198,26 @@ proc runSpecialTests(r: var TResults, options: string) = proc rejectSpecialTests(r: var TResults, options: string) = rejectThreadTests(r, options) +proc findMainFile(dir: string): string = + # finds the file belonging to ".nimrod.cfg"; if there is no such file + # it returns the some ".nim" file if there is only one: + const cfgExt = ".nimrod.cfg" + result = "" + var nimFiles = 0 + for kind, file in os.walkDir(dir): + if kind == pcFile: + if file.endsWith(cfgExt): return file[.. -(cfgExt.len+1)] & ".nim" + elif file.endsWith(".nim"): + if result.len == 0: result = file + inc nimFiles + if nimFiles != 1: result.setlen(0) + +proc compileManyLoc(r: var TResults, options: string) = + for kind, dir in os.walkDir("tests/manyloc"): + if kind == pcDir: + let mainfile = findMainFile(dir) + compileSingleTest(r, mainfile, options) + proc compileSpecialTests(r: var TResults, options: string) = compileRodFiles(r, options) @@ -207,6 +226,8 @@ proc compileSpecialTests(r: var TResults, options: string) = compileDLLTests(r, options) compileDebuggerTests(r, options) + + compileManyLoc(r, options) #var given = callCompiler("nimrod i", "nimrod i", options) #r.addResult("nimrod i", given.msg, if given.err: reFailure else: reSuccess) diff --git a/tests/stckovfl.nim b/tests/stckovfl.nim old mode 100755 new mode 100644 diff --git a/tests/sunset.tmpl b/tests/sunset.tmpl old mode 100755 new mode 100644 diff --git a/tests/testdata/csvtest.csv b/tests/testdata/csvtest.csv old mode 100755 new mode 100644 diff --git a/tests/testdata/data.csv b/tests/testdata/data.csv old mode 100755 new mode 100644 diff --git a/tests/testdata/doc1.xml b/tests/testdata/doc1.xml old mode 100755 new mode 100644 diff --git a/tests/testdata/jsontest.json b/tests/testdata/jsontest.json old mode 100755 new mode 100644 diff --git a/tests/testdata/jsontest2.json b/tests/testdata/jsontest2.json old mode 100755 new mode 100644 diff --git a/tests/testdata/wildhtml.html b/tests/testdata/wildhtml.html old mode 100755 new mode 100644 diff --git a/tests/testdata/xmltest.html b/tests/testdata/xmltest.html old mode 100755 new mode 100644 diff --git a/tests/tester.nim b/tests/tester.nim old mode 100755 new mode 100644 index 60c84403e..8c8e31fe8 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -11,7 +11,7 @@ import parseutils, strutils, pegs, os, osproc, streams, parsecfg, browsers, json, - marshal, cgi, parseopt + marshal, cgi, parseopt, caasdriver const cmdTemplate = r"nimrod cc --hints:on $# $#" @@ -38,6 +38,7 @@ type reExeNotFound, reIgnored, # test is ignored reSuccess # test was successful + TTarget = enum targetC, targetCpp, targetObjC, targetJS @@ -363,6 +364,10 @@ proc outputJSON(reject, compile, run: TResults) = var s = pretty(doc) writeFile(jsonFile, s) +proc runCaasTests(r: var TResults) = + for test, output, status in caasTestsRunner(): + r.addResult(test, "", output, if status: reSuccess else: reOutputsDiffer) + proc main() = os.putenv "NIMTEST_NO_COLOR", "1" os.putenv "NIMTEST_OUTPUT_LVL", "PRINT_FAILURES" @@ -392,7 +397,7 @@ proc main() = of "compile": compile(r, "tests/compile/t*.nim", p.cmdLineRest.string) compile(r, "tests/ccg/t*.nim", p.cmdLineRest.string) - compile(r, "tests/ecmas.nim", p.cmdLineRest.string) + compile(r, "tests/js.nim", p.cmdLineRest.string) compileExample(r, "lib/pure/*.nim", p.cmdLineRest.string) compileExample(r, "examples/*.nim", p.cmdLineRest.string) compileExample(r, "examples/gtk/*.nim", p.cmdLineRest.string) @@ -404,6 +409,7 @@ proc main() = writeResults(runJson, r) of "special": runSpecialTests(r, p.cmdLineRest.string) + runCaasTests(r) writeResults(runJson, r) of "rodfiles": runRodFiles(r, p.cmdLineRest.string) diff --git a/tests/threads/threadex.nim b/tests/threads/threadex.nim old mode 100755 new mode 100644 diff --git a/tests/threads/tthreadanalysis.nim b/tests/threads/tthreadanalysis.nim old mode 100755 new mode 100644 diff --git a/tests/threads/tthreadanalysis2.nim b/tests/threads/tthreadanalysis2.nim old mode 100755 new mode 100644 diff --git a/tests/threads/tthreadheapviolation1.nim b/tests/threads/tthreadheapviolation1.nim old mode 100755 new mode 100644 diff --git a/tinyc/arm-gen.c b/tinyc/arm-gen.c old mode 100755 new mode 100644 diff --git a/tinyc/c67-gen.c b/tinyc/c67-gen.c old mode 100755 new mode 100644 diff --git a/tinyc/coff.h b/tinyc/coff.h old mode 100755 new mode 100644 diff --git a/tinyc/config.h b/tinyc/config.h old mode 100755 new mode 100644 diff --git a/tinyc/config.mak b/tinyc/config.mak old mode 100755 new mode 100644 diff --git a/tinyc/config.texi b/tinyc/config.texi old mode 100755 new mode 100644 diff --git a/tinyc/config_edited.h b/tinyc/config_edited.h old mode 100755 new mode 100644 diff --git a/tinyc/elf.h b/tinyc/elf.h old mode 100755 new mode 100644 diff --git a/tinyc/examples/ex1.c b/tinyc/examples/ex1.c old mode 100755 new mode 100644 diff --git a/tinyc/examples/ex2.c b/tinyc/examples/ex2.c old mode 100755 new mode 100644 diff --git a/tinyc/examples/ex3.c b/tinyc/examples/ex3.c old mode 100755 new mode 100644 diff --git a/tinyc/examples/ex4.c b/tinyc/examples/ex4.c old mode 100755 new mode 100644 diff --git a/tinyc/examples/ex5.c b/tinyc/examples/ex5.c old mode 100755 new mode 100644 diff --git a/tinyc/i386-asm.c b/tinyc/i386-asm.c old mode 100755 new mode 100644 diff --git a/tinyc/i386-asm.h b/tinyc/i386-asm.h old mode 100755 new mode 100644 diff --git a/tinyc/i386-gen.c b/tinyc/i386-gen.c old mode 100755 new mode 100644 diff --git a/tinyc/il-gen.c b/tinyc/il-gen.c old mode 100755 new mode 100644 diff --git a/tinyc/il-opcodes.h b/tinyc/il-opcodes.h old mode 100755 new mode 100644 diff --git a/tinyc/include/float.h b/tinyc/include/float.h old mode 100755 new mode 100644 diff --git a/tinyc/include/stdarg.h b/tinyc/include/stdarg.h old mode 100755 new mode 100644 diff --git a/tinyc/include/stdbool.h b/tinyc/include/stdbool.h old mode 100755 new mode 100644 diff --git a/tinyc/include/stddef.h b/tinyc/include/stddef.h old mode 100755 new mode 100644 diff --git a/tinyc/include/tcclib.h b/tinyc/include/tcclib.h old mode 100755 new mode 100644 diff --git a/tinyc/include/varargs.h b/tinyc/include/varargs.h old mode 100755 new mode 100644 diff --git a/tinyc/lib/alloca86-bt.S b/tinyc/lib/alloca86-bt.S old mode 100755 new mode 100644 diff --git a/tinyc/lib/alloca86.S b/tinyc/lib/alloca86.S old mode 100755 new mode 100644 diff --git a/tinyc/lib/bcheck.c b/tinyc/lib/bcheck.c old mode 100755 new mode 100644 diff --git a/tinyc/lib/libtcc1.c b/tinyc/lib/libtcc1.c old mode 100755 new mode 100644 diff --git a/tinyc/libtcc.c b/tinyc/libtcc.c old mode 100755 new mode 100644 diff --git a/tinyc/libtcc.h b/tinyc/libtcc.h old mode 100755 new mode 100644 diff --git a/tinyc/stab.def b/tinyc/stab.def old mode 100755 new mode 100644 diff --git a/tinyc/stab.h b/tinyc/stab.h old mode 100755 new mode 100644 diff --git a/tinyc/tcc-doc.html b/tinyc/tcc-doc.html old mode 100755 new mode 100644 diff --git a/tinyc/tcc-doc.texi b/tinyc/tcc-doc.texi old mode 100755 new mode 100644 diff --git a/tinyc/tcc.c b/tinyc/tcc.c old mode 100755 new mode 100644 diff --git a/tinyc/tcc.h b/tinyc/tcc.h old mode 100755 new mode 100644 diff --git a/tinyc/tccasm.c b/tinyc/tccasm.c old mode 100755 new mode 100644 diff --git a/tinyc/tcccoff.c b/tinyc/tcccoff.c old mode 100755 new mode 100644 diff --git a/tinyc/tccelf.c b/tinyc/tccelf.c old mode 100755 new mode 100644 diff --git a/tinyc/tccgen.c b/tinyc/tccgen.c old mode 100755 new mode 100644 diff --git a/tinyc/tccpe.c b/tinyc/tccpe.c old mode 100755 new mode 100644 diff --git a/tinyc/tccpp.c b/tinyc/tccpp.c old mode 100755 new mode 100644 diff --git a/tinyc/tcctok.h b/tinyc/tcctok.h old mode 100755 new mode 100644 diff --git a/tinyc/tests/asmtest.S b/tinyc/tests/asmtest.S old mode 100755 new mode 100644 diff --git a/tinyc/tests/boundtest.c b/tinyc/tests/boundtest.c old mode 100755 new mode 100644 diff --git a/tinyc/tests/libtcc_test.c b/tinyc/tests/libtcc_test.c old mode 100755 new mode 100644 diff --git a/tinyc/tests/tcctest.c b/tinyc/tests/tcctest.c old mode 100755 new mode 100644 diff --git a/tinyc/win32/build-tcc.bat b/tinyc/win32/build-tcc.bat old mode 100755 new mode 100644 diff --git a/tinyc/win32/examples/dll.c b/tinyc/win32/examples/dll.c old mode 100755 new mode 100644 diff --git a/tinyc/win32/examples/fib.c b/tinyc/win32/examples/fib.c old mode 100755 new mode 100644 diff --git a/tinyc/win32/examples/hello_dll.c b/tinyc/win32/examples/hello_dll.c old mode 100755 new mode 100644 diff --git a/tinyc/win32/examples/hello_win.c b/tinyc/win32/examples/hello_win.c old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/_mingw.h b/tinyc/win32/include/_mingw.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/assert.h b/tinyc/win32/include/assert.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/conio.h b/tinyc/win32/include/conio.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/ctype.h b/tinyc/win32/include/ctype.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/dir.h b/tinyc/win32/include/dir.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/direct.h b/tinyc/win32/include/direct.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/dirent.h b/tinyc/win32/include/dirent.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/dos.h b/tinyc/win32/include/dos.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/errno.h b/tinyc/win32/include/errno.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/excpt.h b/tinyc/win32/include/excpt.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/fcntl.h b/tinyc/win32/include/fcntl.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/fenv.h b/tinyc/win32/include/fenv.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/float.h b/tinyc/win32/include/float.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/inttypes.h b/tinyc/win32/include/inttypes.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/io.h b/tinyc/win32/include/io.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/limits.h b/tinyc/win32/include/limits.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/locale.h b/tinyc/win32/include/locale.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/malloc.h b/tinyc/win32/include/malloc.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/math.h b/tinyc/win32/include/math.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/mem.h b/tinyc/win32/include/mem.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/memory.h b/tinyc/win32/include/memory.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/process.h b/tinyc/win32/include/process.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/setjmp.h b/tinyc/win32/include/setjmp.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/share.h b/tinyc/win32/include/share.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/signal.h b/tinyc/win32/include/signal.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/stdarg.h b/tinyc/win32/include/stdarg.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/stdbool.h b/tinyc/win32/include/stdbool.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/stddef.h b/tinyc/win32/include/stddef.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/stdint.h b/tinyc/win32/include/stdint.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/stdio.h b/tinyc/win32/include/stdio.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/stdlib.h b/tinyc/win32/include/stdlib.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/string.h b/tinyc/win32/include/string.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/sys/fcntl.h b/tinyc/win32/include/sys/fcntl.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/sys/file.h b/tinyc/win32/include/sys/file.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/sys/locking.h b/tinyc/win32/include/sys/locking.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/sys/stat.h b/tinyc/win32/include/sys/stat.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/sys/time.h b/tinyc/win32/include/sys/time.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/sys/timeb.h b/tinyc/win32/include/sys/timeb.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/sys/types.h b/tinyc/win32/include/sys/types.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/sys/unistd.h b/tinyc/win32/include/sys/unistd.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/sys/utime.h b/tinyc/win32/include/sys/utime.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/tchar.h b/tinyc/win32/include/tchar.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/time.h b/tinyc/win32/include/time.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/unistd.h b/tinyc/win32/include/unistd.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/values.h b/tinyc/win32/include/values.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/varargs.h b/tinyc/win32/include/varargs.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/wchar.h b/tinyc/win32/include/wchar.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/wctype.h b/tinyc/win32/include/wctype.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/basetsd.h b/tinyc/win32/include/winapi/basetsd.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/basetyps.h b/tinyc/win32/include/winapi/basetyps.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/winbase.h b/tinyc/win32/include/winapi/winbase.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/wincon.h b/tinyc/win32/include/winapi/wincon.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/windef.h b/tinyc/win32/include/winapi/windef.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/windows.h b/tinyc/win32/include/winapi/windows.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/winerror.h b/tinyc/win32/include/winapi/winerror.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/wingdi.h b/tinyc/win32/include/winapi/wingdi.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/winnetwk.h b/tinyc/win32/include/winapi/winnetwk.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/winnls.h b/tinyc/win32/include/winapi/winnls.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/winnt.h b/tinyc/win32/include/winapi/winnt.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/winreg.h b/tinyc/win32/include/winapi/winreg.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/winsvc.h b/tinyc/win32/include/winapi/winsvc.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/winuser.h b/tinyc/win32/include/winapi/winuser.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/include/winapi/winver.h b/tinyc/win32/include/winapi/winver.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/lib/chkstk.S b/tinyc/win32/lib/chkstk.S old mode 100755 new mode 100644 diff --git a/tinyc/win32/lib/crt1.c b/tinyc/win32/lib/crt1.c old mode 100755 new mode 100644 diff --git a/tinyc/win32/lib/dllcrt1.c b/tinyc/win32/lib/dllcrt1.c old mode 100755 new mode 100644 diff --git a/tinyc/win32/lib/dllmain.c b/tinyc/win32/lib/dllmain.c old mode 100755 new mode 100644 diff --git a/tinyc/win32/lib/gdi32.def b/tinyc/win32/lib/gdi32.def old mode 100755 new mode 100644 diff --git a/tinyc/win32/lib/kernel32.def b/tinyc/win32/lib/kernel32.def old mode 100755 new mode 100644 diff --git a/tinyc/win32/lib/libtcc1.a b/tinyc/win32/lib/libtcc1.a old mode 100755 new mode 100644 diff --git a/tinyc/win32/lib/msvcrt.def b/tinyc/win32/lib/msvcrt.def old mode 100755 new mode 100644 diff --git a/tinyc/win32/lib/user32.def b/tinyc/win32/lib/user32.def old mode 100755 new mode 100644 diff --git a/tinyc/win32/lib/wincrt1.c b/tinyc/win32/lib/wincrt1.c old mode 100755 new mode 100644 diff --git a/tinyc/win32/libtcc/libtcc.a b/tinyc/win32/libtcc/libtcc.a old mode 100755 new mode 100644 diff --git a/tinyc/win32/libtcc/libtcc.h b/tinyc/win32/libtcc/libtcc.h old mode 100755 new mode 100644 diff --git a/tinyc/win32/tcc-win32.txt b/tinyc/win32/tcc-win32.txt old mode 100755 new mode 100644 diff --git a/tinyc/win32/tools/tiny_impdef.c b/tinyc/win32/tools/tiny_impdef.c old mode 100755 new mode 100644 diff --git a/tinyc/win32/tools/tiny_libmaker.c b/tinyc/win32/tools/tiny_libmaker.c old mode 100755 new mode 100644 diff --git a/tinyc/x86_64-gen.c b/tinyc/x86_64-gen.c old mode 100755 new mode 100644 diff --git a/todo.txt b/todo.txt old mode 100755 new mode 100644 index a6ae409bd..65a9127d1 --- a/todo.txt +++ b/todo.txt @@ -1,35 +1,59 @@ version 0.9.2 ============= -- implement constructors + full 'not nil' checking -- ``restrict`` pragma + backend support -- fix: 'result' is not properly cleaned for NRVO -- document NimMain and check whether it works for threading - - -version 0.9.4 -============= - -- make 'bind' default for templates and introduce 'mixin'; - special rule for ``[]=`` -- implicit deref for parameter matching; overloading based on 'var T' -- ``=`` should be overloadable; requires specialization for ``=`` -- optimize genericAssign in the code generator - - -version 0.9.X -============= - - FFI: * test libffi on windows * test: times.format with the FFI +- acyclic vs prunable; introduce GC hints +- CGEN: ``restrict`` pragma + backend support; computed goto support +- document NimMain and check whether it works for threading + + +Bugs +==== + +- docgen: sometimes effects are listed twice +- 'result' is not properly cleaned for NRVO +- instantiated generics are listed in error messages +- sneaking with qualifiedLookup() is really broken! +- aporia.nim(968, 5) Error: ambiguous identifier: 'DELETE' -- + use a qualifier +- blocks can "export" an identifier but the CCG generates {} for them ... +- osproc execProcesses can deadlock if all processes fail (as experienced + in c++ mode) +- bootstrapping does not work in C++ mode +- result = result shr 8 for the "system()" wrapper + + +version 0.9.4 +============= + +- macros as type pragmas +- ``try`` as an expression +- provide tool/API to track leaks/object counts +- hybrid GC +- use big blocks in the allocator +- implement full 'not nil' checking +- make 'bind' default for templates and introduce 'mixin'; + special rule for ``[]=`` +- implicit deref for parameter matching; overloading based on 'var T' +- ``=`` should be overloadable; requires specialization for ``=``; general + lift mechanism in the compiler is already implemented for 'fields' +- lazy overloading resolution: + * get rid of ``expr[typ]``, use perhaps ``static[typ]`` instead + * special case ``tyStmt`` + + +version 0.9.X +============= + - test&finish first class iterators: * nested iterators - implement the missing features wrt inheritance +- object pooling support for *hard* realtime systems - improve the compiler as a service - better support for macros that rewrite procs - macros need access to types and symbols (partially implemented) -- result = result shr 8 for the "system()" wrapper - rethink the syntax/grammar: * parser is not strict enough with newlines * change comment handling in the AST @@ -48,6 +72,7 @@ Concurrency an ``injectLoop`` pragma - 'writes: []' effect; track reads/writes for shared types - use the effect system for static deadlock prevention and race detection +- ``~`` operator for effects - introduce 'noaddr' pragma to prevent taking the address of a location; this is very handy to prevent aliasing of global data @@ -56,11 +81,9 @@ version 0.9.XX ============== - object branch transitions can't work with the current 'reset'; add a 'reset' - with an additional parameter --> re-evaluate this issue after constructors - have been added -- fix destructors; don't work yet when used as expression; alternative for - version 1: disallow expressions yielding a type with a destructor that are - not in a 'let/var' context (p(a.openFile, b.openFile) makes no sense anyway) + with an additional parameter --> simple: + provide a 'reset(x, TObj(k: nkValue))' instead? why bother? '=' does the + same. - document nimdoc properly finally - make 'clamp' a magic for the range stuff - better type syntax for functions and tuples: tuple(int, int); (int,int)->int @@ -71,6 +94,7 @@ Not essential for 1.0.0 - 'const' objects including case objects - mocking support with ``tyProxy`` that does: fallback for ``.`` operator + - overloading of ``.``? Special case ``.=``? - allow implicit forward declarations of procs via a pragma (so that the wrappers can deactivate it) - implement the "snoopResult" pragma; no, make a strutils with string append @@ -86,6 +110,12 @@ Not essential for 1.0.0 - implement marker procs for message passing - activate more thread tests - implement closures that support nesting of *procs* > 1 +- GC: implement simple generational GC + * first version: mark black in write barrier + * second version: introduce fake roots instead of marking black + * third version: find some clever heuristic which is preferable +- object constructors: static check for fields if discriminator is known at + compile time GC @@ -99,19 +129,9 @@ GC Optimizations ============= -- optimize 'if' with a constant condition +- optimize 'if' with a constant condition --> necessary in frontend for better + dead code elimination - escape analysis for string/seq seems to be easy to do too; even further write barrier specialization - inlining of first class functions - proc specialization in the code gen for write barrier specialization - - -Bugs -==== - -- sneaking with qualifiedLookup() is really broken! -- aporia.nim(968, 5) Error: ambiguous identifier: 'DELETE' -- - use a qualifier -- blocks can "export" an identifier but the CCG generates {} for them ... -- JS gen: fix exception handling -- the better scoping for locals is the wrong default for endb diff --git a/tools/cmerge.nim b/tools/cmerge.nim old mode 100755 new mode 100644 diff --git a/tools/detect/detect.nim b/tools/detect/detect.nim old mode 100755 new mode 100644 diff --git a/tools/detect/timesize.c b/tools/detect/timesize.c old mode 100755 new mode 100644 diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim old mode 100755 new mode 100644 diff --git a/tools/nimgrep.nimrod.cfg b/tools/nimgrep.nimrod.cfg old mode 100755 new mode 100644 diff --git a/tools/niminst/buildbat.tmpl b/tools/niminst/buildbat.tmpl old mode 100755 new mode 100644 diff --git a/tools/niminst/buildsh.tmpl b/tools/niminst/buildsh.tmpl old mode 100755 new mode 100644 index 60a06c93f..e2e2d5ab5 --- a/tools/niminst/buildsh.tmpl +++ b/tools/niminst/buildsh.tmpl @@ -58,6 +58,8 @@ case $uos in ;; *darwin* ) myos="macosx" + CC="clang" + LINKER="clang" LINK_FLAGS="$LINK_FLAGS -ldl -lm" if [ "$HOSTTYPE" = "x86_64" ] ; then ucpu="amd64" diff --git a/tools/niminst/deinstall.tmpl b/tools/niminst/deinstall.tmpl old mode 100755 new mode 100644 diff --git a/tools/niminst/inno.tmpl b/tools/niminst/inno.tmpl old mode 100755 new mode 100644 diff --git a/tools/niminst/install.tmpl b/tools/niminst/install.tmpl old mode 100755 new mode 100644 diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim old mode 100755 new mode 100644 index f41ab665e..98a7ab8bb --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -32,6 +32,7 @@ type actionNone, # action not yet known actionCSource # action: create C sources actionInno, # action: create Inno Setup installer + actionScripts # action: create install and deinstall scripts actionZip, # action: create zip file actionDeb # action: prepare deb package @@ -128,6 +129,7 @@ Usage: niminst [options] command[;command2...] ini-file[.ini] [compile_options] Command: csource build C source code for source based installations + scripts build install and deinstall scripts zip build the ZIP file inno build the Inno Setup installer deb create files for debhelper @@ -153,6 +155,7 @@ proc parseCmdLine(c: var TConfigData) = for a in split(normalize(key.string), {';', ','}): case a of "csource": incl(c.actions, actionCSource) + of "scripts": incl(c.actions, actionScripts) of "zip": incl(c.actions, actionZip) of "inno": incl(c.actions, actionInno) of "deb": incl(c.actions, actionDeb) @@ -382,6 +385,12 @@ proc removeDuplicateFiles(c: var TConfigData) = RemoveFile(dup) c.cfiles[osA][cpuA][i] = orig +proc writeInstallScripts(c: var TConfigData) = + if c.installScript: + writeFile(installShFile, GenerateInstallScript(c), "\10") + if c.uninstallScript: + writeFile(deinstallShFile, GenerateDeinstallScript(c), "\10") + proc srcdist(c: var TConfigData) = for x in walkFiles(c.libpath / "lib/*.h"): CopyFile(dest="build" / extractFilename(x), source=x) @@ -408,10 +417,7 @@ proc srcdist(c: var TConfigData) = writeFile(buildShFile, GenerateBuildShellScript(c), "\10") writeFile(buildBatFile32, GenerateBuildBatchScript(c, tWin32), "\13\10") writeFile(buildBatFile64, GenerateBuildBatchScript(c, tWin64), "\13\10") - if c.installScript: - writeFile(installShFile, GenerateInstallScript(c), "\10") - if c.uninstallScript: - writeFile(deinstallShFile, GenerateDeinstallScript(c), "\10") + writeInstallScripts(c) # --------------------- generate inno setup ----------------------------------- proc setupDist(c: var TConfigData) = @@ -514,6 +520,8 @@ if actionInno in c.actions: setupDist(c) if actionCSource in c.actions: srcdist(c) +if actionScripts in c.actions: + writeInstallScripts(c) if actionZip in c.actions: when haveZipLib: zipdist(c) diff --git a/tools/nimrepl.nim b/tools/nimrepl.nim old mode 100755 new mode 100644 diff --git a/tools/nimweb.nim b/tools/nimweb.nim old mode 100755 new mode 100644 diff --git a/tools/noprefix.nim b/tools/noprefix.nim old mode 100755 new mode 100644 diff --git a/tools/sunset.tmpl b/tools/sunset.tmpl old mode 100755 new mode 100644 diff --git a/tools/trimcc.nim b/tools/trimcc.nim old mode 100755 new mode 100644 diff --git a/web/community.txt b/web/community.txt old mode 100755 new mode 100644 diff --git a/web/documentation.txt b/web/documentation.txt old mode 100755 new mode 100644 diff --git a/web/download.txt b/web/download.txt old mode 100755 new mode 100644 diff --git a/web/index.txt b/web/index.txt old mode 100755 new mode 100644 diff --git a/web/links.txt b/web/links.txt old mode 100755 new mode 100644 diff --git a/web/news.txt b/web/news.txt old mode 100755 new mode 100644 index efe215f93..26c681d95 --- a/web/news.txt +++ b/web/news.txt @@ -15,23 +15,30 @@ Bugfixes with ``GC_disableMarkAndSweep`` and run it explicitly at an appropriate time or not at all. There is also a new GC you can activate with ``--gc:markAndSweep`` which does not have this problem but is slower in - general. + general and has no realtime guarantees. Library Additions ----------------- -- Added ``system.onRaise`` to support a condition system. -- Added ``macros.quote`` for AST quasi-quoting. -- Added ``system.unsafeNew`` to support hacky variable length objects. - There is a new experimental mark&sweep GC which can be faster (or much slower) than the default GC. Enable with ``--gc:markAndSweep``. +- Added ``system.onRaise`` to support a condition system. +- Added ``system.locals`` that provides access to a proc's locals. +- Added ``macros.quote`` for AST quasi-quoting. +- Added ``system.unsafeNew`` to support hacky variable length objects. +- ``system.fields`` and ``system.fieldPairs`` support ``object`` too; they + used to only support tuples. +- Added ``system.CurrentSourcePath`` returning the full file-system path of + the current source file Changes affecting backwards compatibility ----------------------------------------- - ``shared`` is a keyword now. +- Deprecated ``sockets.recvLine`` and ``asyncio.recvLine``, added + ``readLine`` instead. Compiler Additions @@ -66,6 +73,7 @@ Language Additions dependencies explicitly. - Overloading based on ASTs has been implemented. - Generics are now supported for multi methods. +- Objects can be initialized via an *object constructor expression*. 2012-09-23 Version 0.9.0 released diff --git a/web/nimrod.ini b/web/nimrod.ini old mode 100755 new mode 100644 index 0a26138a6..565a02117 --- a/web/nimrod.ini +++ b/web/nimrod.ini @@ -28,7 +28,7 @@ pdf: "manual;lib;tut1;tut2;nimrodc;c2nim;niminst;gc" srcdoc2: "system.nim;impure/graphics;wrappers/sdl" srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned" srcdoc2: "impure/re;pure/sockets" -srcdoc: "system/threads.nim;system/channels.nim;ecmas/dom" +srcdoc: "system/threads.nim;system/channels.nim;js/dom" srcdoc2: "pure/os;pure/strutils;pure/math;pure/matchers;pure/algorithm" srcdoc2: "pure/complex;pure/times;pure/osproc;pure/pegs;pure/dynlib" srcdoc2: "pure/parseopt;pure/hashes;pure/strtabs;pure/lexbase" diff --git a/web/question.txt b/web/question.txt old mode 100755 new mode 100644 diff --git a/web/snippets/snippet1.nim b/web/snippets/snippet1.nim old mode 100755 new mode 100644 diff --git a/web/ticker.txt b/web/ticker.txt old mode 100755 new mode 100644