Merge remote-tracking branch 'upstream/master' into standalone

This commit is contained in:
Simon Hafner 2013-06-29 20:29:37 -05:00
commit 7dd9dbe4dd
119 changed files with 4593 additions and 1580 deletions

11
.gitignore vendored
View file

@ -167,4 +167,15 @@ examples/cross_calculator/android/tags
/run.json
/testresults.html
/testresults.json
/tests/caas/SymbolProcRun.*/
/tests/caas/absurd_nesting
/tests/caas/forward_declarations
/tests/caas/idetools_api
/tests/caas/imported
/tests/caas/issue_416_template_shift
/tests/caas/issue_452_export_shift
/tests/caas/issue_477_dynamic_dispatch
/tests/caas/its_full_of_procs
/tests/caas/main
/tests/caasdriver
/tools/nimgrep

View file

@ -278,11 +278,12 @@ const
# the compiler will avoid printing such names
# in user messages.
sfHoist* = sfVolatile ## proc return value can be hoisted
sfNoForward* = sfRegister
# forward declarations are not required (per module)
sfNoRoot* = sfBorrow # a local variable is provably no root so it doesn't
# require RC ops
const
# getting ready for the future expr/stmt merge
nkWhen* = nkWhenStmt
@ -353,7 +354,7 @@ type
nfSem # node has been checked for semantics
TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 19)
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 23)
tfVarargs, # procedure has C styled varargs
tfNoSideEffect, # procedure type does not allow side effects
tfFinal, # is the object final?
@ -380,7 +381,13 @@ type
tfByRef, # pass object/tuple by reference (C backend)
tfIterator, # type is really an iterator, not a tyProc
tfShared, # type is 'shared'
tfNotNil # type cannot be 'nil'
tfNotNil, # type cannot be 'nil'
tfNeedsInit, # type constains a "not nil" constraint somewhere or some
# other type so that it requires inititalization
tfHasShared, # type constains a "shared" constraint modifier somewhere
tfHasMeta, # type has "typedesc" or "expr" somewhere
tfHasGCedMem, # type contains GC'ed memory
TTypeFlags* = set[TTypeFlag]
@ -659,12 +666,15 @@ type
# (or not in symbol table)
# for modules, an unique index corresponding
# to the module's fileIdx
# for variables a slot index for the evaluator
offset*: int # offset of record field
loc*: TLoc
annex*: PLib # additional fields (seldom used, so we use a
# reference to another object to safe space)
constraint*: PNode # additional constraints like 'lit|result'
constraint*: PNode # additional constraints like 'lit|result'; also
# misused for the codegenDecl pragma in the hope
# it won't cause problems
TTypeSeq* = seq[PType]
TType* {.acyclic.} = object of TIdObj # \
@ -920,7 +930,10 @@ proc discardSons(father: PNode) =
father.sons = nil
when defined(useNodeIds):
const nodeIdToDebug = 140600
const nodeIdToDebug = 612777 # 612794
#612840 # 612905 # 614635 # 614637 # 614641
# 423408
#429107 # 430443 # 441048 # 441090 # 441153
var gNodeId: int
proc newNode(kind: TNodeKind): PNode =
@ -933,6 +946,7 @@ proc newNode(kind: TNodeKind): PNode =
when defined(useNodeIds):
result.id = gNodeId
if result.id == nodeIdToDebug:
echo "KIND ", result.kind
writeStackTrace()
inc gNodeId
@ -973,6 +987,12 @@ proc newNodeI(kind: TNodeKind, info: TLineInfo): PNode =
new(result)
result.kind = kind
result.info = info
when defined(useNodeIds):
result.id = gNodeId
if result.id == nodeIdToDebug:
echo "KIND ", result.kind
writeStackTrace()
inc gNodeId
proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode =
new(result)
@ -980,6 +1000,12 @@ proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode =
result.info = info
if children > 0:
newSeq(result.sons, children)
when defined(useNodeIds):
result.id = gNodeId
if result.id == nodeIdToDebug:
echo "KIND ", result.kind
writeStackTrace()
inc gNodeId
proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[],
typ: PType = nil): PNode =
@ -989,6 +1015,12 @@ proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[],
result.typ = typ
# XXX use shallowCopy here for ownership transfer:
result.sons = sons
when defined(useNodeIds):
result.id = gNodeId
if result.id == nodeIdToDebug:
echo "KIND ", result.kind
writeStackTrace()
inc gNodeId
proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
result = newNode(kind)
@ -1146,14 +1178,33 @@ proc newSons(father: PNode, length: int) =
else:
setlen(father.sons, length)
proc addSon*(father, son: PType) {.deprecated.} =
if isNil(father.sons): father.sons = @[]
add(father.sons, son)
#assert((father.kind != tyGenericInvokation) or (son.kind != tyGenericInst))
proc propagateToOwner*(owner, elem: PType) =
const HaveTheirOwnEmpty = {tySequence, tySet}
owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta,
tfHasGCedMem})
if tfNotNil in elem.flags:
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}:
owner.flags.incl tfNotNil
elif owner.kind notin HaveTheirOwnEmpty:
owner.flags.incl tfNeedsInit
if tfNeedsInit in elem.flags:
if owner.kind in HaveTheirOwnEmpty: nil
else: owner.flags.incl tfNeedsInit
if tfShared in elem.flags:
owner.flags.incl tfHasShared
if elem.kind in {tyExpr, tyTypeDesc}:
owner.flags.incl tfHasMeta
elif elem.kind in {tyString, tyRef, tySequence} or
elem.kind == tyProc and elem.callConv == ccClosure:
owner.flags.incl tfHasGCedMem
proc rawAddSon*(father, son: PType) =
if isNil(father.sons): father.sons = @[]
add(father.sons, son)
if not son.isNil: propagateToOwner(father, son)
proc addSon(father, son: PNode) =
assert son != nil
@ -1178,6 +1229,9 @@ proc copyNode(src: PNode): PNode =
result.info = src.info
result.typ = src.typ
result.flags = src.flags * PersistentNodeFlags
when defined(useNodeIds):
if result.id == nodeIdToDebug:
echo "COMES FROM ", src.id
case src.Kind
of nkCharLit..nkUInt64Lit: result.intVal = src.intVal
of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal
@ -1193,6 +1247,9 @@ proc shallowCopy*(src: PNode): PNode =
result.info = src.info
result.typ = src.typ
result.flags = src.flags * PersistentNodeFlags
when defined(useNodeIds):
if result.id == nodeIdToDebug:
echo "COMES FROM ", src.id
case src.Kind
of nkCharLit..nkUInt64Lit: result.intVal = src.intVal
of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal
@ -1209,6 +1266,9 @@ proc copyTree(src: PNode): PNode =
result.info = src.info
result.typ = src.typ
result.flags = src.flags * PersistentNodeFlags
when defined(useNodeIds):
if result.id == nodeIdToDebug:
echo "COMES FROM ", src.id
case src.Kind
of nkCharLit..nkUInt64Lit: result.intVal = src.intVal
of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal

View file

@ -652,6 +652,8 @@ proc finishTypeDescriptions(m: BModule) =
discard getTypeDesc(m, m.typeStack[i])
inc(i)
template cgDeclFrmt*(s: PSym): string = s.constraint.strVal
proc genProcHeader(m: BModule, prc: PSym): PRope =
var
rettype, params: PRope
@ -669,12 +671,16 @@ proc genProcHeader(m: BModule, prc: PSym): PRope =
genProcParams(m, prc.typ, rettype, params, check)
# careful here! don't access ``prc.ast`` as that could reload large parts of
# the object graph!
appf(result, "$1($2, $3)$4",
[toRope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r, params])
if prc.constraint.isNil:
appf(result, "$1($2, $3)$4",
[toRope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r,
params])
else:
result = ropef(prc.cgDeclFrmt, [rettype, prc.loc.r, params])
# ------------------ type info generation -------------------------------------
proc genTypeInfo(m: BModule, typ: PType): PRope
proc genTypeInfo(m: BModule, t: PType): PRope
proc getNimNode(m: BModule): PRope =
result = ropef("$1[$2]", [m.typeNodesName, toRope(m.typeNodes)])
inc(m.typeNodes)
@ -901,21 +907,21 @@ type
include ccgtrav
proc genTypeInfo(m: BModule, typ: PType): PRope =
var t = getUniqueType(typ)
proc genTypeInfo(m: BModule, t: PType): PRope =
var t = getUniqueType(t)
result = ropef("NTI$1", [toRope(t.id)])
let owner = typ.skipTypes(typedescPtrs).owner.getModule
if ContainsOrIncl(m.typeInfoMarker, t.id):
return con("(&".toRope, result, ")".toRope)
let owner = t.skipTypes(typedescPtrs).owner.getModule
if owner != m.module:
# make sure the type info is created in the owner module
discard genTypeInfo(owner.bmod, typ)
# refenrece the type info as extern here
discard genTypeInfo(owner.bmod, t)
# reference the type info as extern here
discard cgsym(m, "TNimType")
discard cgsym(m, "TNimNode")
appf(m.s[cfsVars], "extern TNimType $1; /* $2 */$n",
[result, toRope(typeToString(t))])
return con("(&".toRope, result, ")".toRope)
if ContainsOrIncl(m.typeInfoMarker, t.id):
return con("(&".toRope, result, ")".toRope)
case t.kind
of tyEmpty: result = toRope"0"
of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar:

View file

@ -488,12 +488,15 @@ proc assignLocalVar(p: BProc, s: PSym) =
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack)
if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
var decl = getTypeDesc(p.module, s.loc.t)
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:
app(decl, " volatile")
appf(decl, " $1;$n", [s.loc.r])
if s.constraint.isNil:
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:
app(decl, " volatile")
appf(decl, " $1;$n", [s.loc.r])
else:
decl = ropef(s.cgDeclFrmt & ";$n", decl, s.loc.r)
line(p, cpsLocals, decl)
localDebugInfo(p, s)
@ -518,11 +521,17 @@ proc assignGlobalVar(p: BProc, s: PSym) =
if sfThread in s.flags:
declareThreadVar(p.module, s, sfImportc in s.flags)
else:
if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ")
app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
if sfRegister in s.flags: app(p.module.s[cfsVars], " register")
if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile")
appf(p.module.s[cfsVars], " $1;$n", [s.loc.r])
var decl: PRope = nil
var td = getTypeDesc(p.module, s.loc.t)
if s.constraint.isNil:
if sfImportc in s.flags: app(decl, "extern ")
app(decl, td)
if sfRegister in s.flags: app(decl, " register")
if sfVolatile in s.flags: app(decl, " volatile")
appf(decl, " $1;$n", [s.loc.r])
else:
decl = ropef(s.cgDeclFrmt & ";$n", td, s.loc.r)
app(p.module.s[cfsVars], decl)
if p.withinLoop > 0:
# fixes tests/run/tzeroarray:
resetLoc(p, s.loc)
@ -1031,7 +1040,7 @@ proc genInitCode(m: BModule) =
var procname = CStringLit(m.initProc, prc, m.module.name.s)
app(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
else:
app(prc, ~"\tvolatile TFrame F; F.len = 0;$N")
app(prc, ~"\tTFrame F; F.len = 0;$N")
app(prc, genSectionStart(cpsInit))
app(prc, m.preInitProc.s(cpsInit))

View file

@ -23,12 +23,12 @@ when hasFFI:
type
PStackFrame* = ref TStackFrame
TStackFrame*{.final.} = object
mapping*: TIdNodeTable # mapping from symbols to nodes
prc*: PSym # current prc; proc that is evaluated
call*: PNode
next*: PStackFrame # for stacking
params*: TNodeSeq # parameters passed to the proc
TStackFrame* = object
prc: PSym # current prc; proc that is evaluated
slots: TNodeSeq # parameters passed to the proc + locals;
# parameters come first
call: PNode
next: PStackFrame # for stacking
TEvalMode* = enum ## reason for evaluation
emRepl, ## evaluate because in REPL mode
@ -69,8 +69,7 @@ const
proc newStackFrame*(): PStackFrame =
new(result)
initIdNodeTable(result.mapping)
result.params = @[]
result.slots = @[]
proc newEvalContext*(module: PSym, mode: TEvalMode): PEvalContext =
new(result)
@ -287,41 +286,66 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
result = newNodeIT(nkCurly, info, t)
else: InternalError("getNullValue: " & $t.kind)
proc evalVarValue(c: PEvalContext, n: PNode): PNode =
result = evalAux(c, n, {})
if result.kind in {nkType..nkNilLit}: result = result.copyNode
proc setSlot(c: PStackFrame, sym: PSym, val: PNode) =
assert sym.owner == c.prc
var idx = sym.position
if idx == 0:
idx = c.slots.len
if idx == 0: idx = 1
sym.position = idx
setLen(c.slots, max(idx+1, c.slots.len))
c.slots[idx] = val
proc setVar(c: PEvalContext, v: PSym, n: PNode) =
if sfGlobal notin v.flags: setSlot(c.tos, v, n)
else: IdNodeTablePut(c.globals, v, n)
proc evalVar(c: PEvalContext, n: PNode): PNode =
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
let a = n.sons[i]
if a.kind == nkCommentStmt: continue
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:
result = evalAux(c, a.sons[2], {})
if a.kind == nkVarTuple:
result = evalVarValue(c, a.lastSon)
if result.kind in {nkType..nkNilLit}:
result = result.copyNode
if isSpecial(result): return
if result.kind != nkPar:
return raiseCannotEval(c, n.info)
for i in 0 .. a.len-3:
var v = a.sons[i].sym
setVar(c, v, result.sons[i])
else:
result = getNullValue(a.sons[0].typ, a.sons[0].info)
if a.sons[0].kind == nkSym:
var v = a.sons[0].sym
IdNodeTablePut(c.tos.mapping, v, result)
else:
# assign to a.sons[0]:
var x = result
result = evalAux(c, a.sons[0], {})
if isSpecial(result): return
myreset(x)
x.kind = result.kind
x.typ = result.typ
case x.kind
of nkCharLit..nkInt64Lit: x.intVal = result.intVal
of nkFloatLit..nkFloat64Lit: x.floatVal = result.floatVal
of nkStrLit..nkTripleStrLit: x.strVal = result.strVal
of nkIdent: x.ident = result.ident
of nkSym: x.sym = result.sym
if a.sons[2].kind != nkEmpty:
result = evalVarValue(c, a.sons[2])
if isSpecial(result): return
else:
if x.kind notin {nkEmpty..nkNilLit}:
discardSons(x)
for j in countup(0, sonsLen(result) - 1): addSon(x, result.sons[j])
result = getNullValue(a.sons[0].typ, a.sons[0].info)
if a.sons[0].kind == nkSym:
var v = a.sons[0].sym
setVar(c, v, result)
else:
# assign to a.sons[0]:
var x = result
result = evalAux(c, a.sons[0], {})
if isSpecial(result): return
myreset(x)
x.kind = result.kind
x.typ = result.typ
case x.kind
of nkCharLit..nkInt64Lit: x.intVal = result.intVal
of nkFloatLit..nkFloat64Lit: x.floatVal = result.floatVal
of nkStrLit..nkTripleStrLit: x.strVal = result.strVal
of nkIdent: x.ident = result.ident
of nkSym: x.sym = result.sym
else:
if x.kind notin {nkEmpty..nkNilLit}:
discardSons(x)
for j in countup(0, sonsLen(result) - 1): addSon(x, result.sons[j])
result = emptyNode
proc aliasNeeded(n: PNode, flags: TEvalFlags): bool =
@ -331,18 +355,17 @@ proc aliasNeeded(n: PNode, flags: TEvalFlags): bool =
proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode =
# We need to return a node to the actual value,
# which can be modified.
assert sym.position != 0 or skResult == sym.kind
var x = c
while x != nil:
if sym.kind == skResult and x.params.len > 0:
result = x.params[0]
if result == nil: result = emptyNode
if sym.owner == x.prc:
result = x.slots[sym.position]
assert result != nil
if not aliasNeeded(result, flags):
result = copyTree(result)
return
result = IdNodeTableGet(x.mapping, sym)
if result != nil and not aliasNeeded(result, flags):
result = copyTree(result)
if result != nil: return
x = x.next
#internalError(sym.info, "cannot eval " & sym.name.s)
#internalError(sym.info, "cannot eval " & sym.name.s & " " & $sym.position)
result = raiseCannotEval(nil, sym.info)
#result = emptyNode
@ -374,12 +397,12 @@ proc evalCall(c: PEvalContext, n: PNode): PNode =
d.call = n
var prc = n.sons[0]
let isClosure = prc.kind == nkClosure
setlen(d.params, sonsLen(n) + ord(isClosure))
setlen(d.slots, sonsLen(n) + ord(isClosure))
if isClosure:
#debug prc
result = evalAux(c, prc.sons[1], {efLValue})
if isSpecial(result): return
d.params[sonsLen(n)] = result
d.slots[sonsLen(n)] = result
result = evalAux(c, prc.sons[0], {})
else:
result = evalAux(c, prc, {})
@ -397,21 +420,21 @@ proc evalCall(c: PEvalContext, n: PNode): PNode =
for i in countup(1, sonsLen(n) - 1):
result = evalAux(c, n.sons[i], {})
if isSpecial(result): return
d.params[i] = result
if n.typ != nil: d.params[0] = getNullValue(n.typ, n.info)
d.slots[i] = result
if n.typ != nil: d.slots[0] = getNullValue(n.typ, n.info)
when hasFFI:
if sfImportc in prc.sym.flags and allowFFI in c.features:
var newCall = newNodeI(nkCall, n.info, n.len)
newCall.sons[0] = evalGlobalVar(c, prc.sym, {})
for i in 1 .. <n.len:
newCall.sons[i] = d.params[i]
newCall.sons[i] = d.slots[i]
return callForeignFunction(newCall)
pushStackFrame(c, d)
result = evalAux(c, prc.sym.getBody, {})
if result.kind == nkExceptBranch: return
if n.typ != nil: result = d.params[0]
if n.typ != nil: result = d.slots[0]
popStackFrame(c)
proc evalArrayAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
@ -553,8 +576,8 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
result = evalGlobalVar(c, s, flags)
of skParam:
# XXX what about LValue?
if s.position + 1 <% c.tos.params.len:
result = c.tos.params[s.position + 1]
if s.position + 1 <% c.tos.slots.len:
result = c.tos.slots[s.position + 1]
of skConst: result = s.ast
of skEnumField: result = newIntNodeT(s.position, n)
else: result = nil
@ -739,10 +762,12 @@ proc evalProc(c: PEvalContext, n: PNode): PNode =
if (resultPos < sonsLen(n)) and (n.sons[resultPos].kind != nkEmpty):
var v = n.sons[resultPos].sym
result = getNullValue(v.typ, n.info)
IdNodeTablePut(c.tos.mapping, v, result)
if c.tos.slots.len == 0: setLen(c.tos.slots, 1)
c.tos.slots[0] = result
#IdNodeTablePut(c.tos.mapping, v, result)
result = evalAux(c, s.getBody, {})
if result.kind == nkReturnToken:
result = IdNodeTableGet(c.tos.mapping, v)
result = c.tos.slots[0]
else:
result = evalAux(c, s.getBody, {})
if result.kind == nkReturnToken:
@ -922,7 +947,7 @@ proc evalTypeTrait*(n: PNode, context: PSym): PNode =
let typ = n.sons[1].sym.typ.skipTypes({tyTypeDesc})
case n.sons[0].sym.name.s.normalize
of "name":
result = newStrNode(nkStrLit, typ.typeToString(preferName))
result = newStrNode(nkStrLit, typ.typeToString(preferExported))
result.typ = newType(tyString, context)
result.info = n.info
else:
@ -1468,20 +1493,21 @@ proc eval*(c: PEvalContext, n: PNode): PNode =
else:
stackTrace(c, result, errCannotInterpretNodeX, renderTree(n))
proc evalConstExprAux(module: PSym, e: PNode, mode: TEvalMode): PNode =
proc evalConstExprAux(module, prc: PSym, e: PNode, mode: TEvalMode): PNode =
var p = newEvalContext(module, mode)
var s = newStackFrame()
s.call = e
s.prc = prc
pushStackFrame(p, s)
result = tryEval(p, e)
if result != nil and result.kind == nkExceptBranch: result = nil
popStackFrame(p)
proc evalConstExpr*(module: PSym, e: PNode): PNode =
result = evalConstExprAux(module, e, emConst)
result = evalConstExprAux(module, nil, e, emConst)
proc evalStaticExpr*(module: PSym, e: PNode): PNode =
result = evalConstExprAux(module, e, emStatic)
proc evalStaticExpr*(module: PSym, e: PNode, prc: PSym): PNode =
result = evalConstExprAux(module, prc, e, emStatic)
proc setupMacroParam(x: PNode): PNode =
result = x
@ -1496,16 +1522,17 @@ proc evalMacroCall(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode =
c.callsite = nOrig
var s = newStackFrame()
s.call = n
s.prc = sym
var L = n.safeLen
if L == 0: L = 1
setlen(s.params, L)
setlen(s.slots, L)
# return value:
s.params[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0])
s.slots[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0])
# setup parameters:
for i in 1 .. < L: s.params[i] = setupMacroParam(n.sons[i])
for i in 1 .. < L: s.slots[i] = setupMacroParam(n.sons[i])
pushStackFrame(c, s)
discard eval(c, sym.getBody)
result = s.params[0]
result = s.slots[0]
popStackFrame(c)
if cyclicTree(result): GlobalError(n.info, errCyclicTree)
dec(evalTemplateCounter)
@ -1521,10 +1548,11 @@ 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)
if oldErrorCount == msgs.gErrorCounter:
result = eval(PEvalContext(c), n)
else:
result = n
oldErrorCount = msgs.gErrorCounter
const evalPass* = makePass(myOpen, nil, myProcess, myProcess)

584
compiler/guards.nim Normal file
View file

@ -0,0 +1,584 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements the 'implies' relation for guards.
import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer
const
someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
mEqUntracedRef, mEqStr, mEqSet, mEqCString}
# set excluded here as the semantics are vastly different:
someLe = {mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum,
mLeCh, mLeB, mLePtr, mLeStr}
someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
mLtCh, mLtB, mLtPtr, mLtStr}
someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}
someIn = {mInRange, mInSet}
proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
proc isLocation(n: PNode): bool = not n.isValue
proc isLet(n: PNode): bool =
if n.kind == nkSym:
if n.sym.kind in {skLet, skTemp, skForVar}:
result = true
elif n.sym.kind == skParam and skipTypes(n.sym.typ,
abstractInst).kind != tyVar:
result = true
proc isVar(n: PNode): bool =
n.kind == nkSym and n.sym.kind in {skResult, skVar} and
{sfGlobal, sfAddrTaken} * n.sym.flags == {}
proc isLetLocation(m: PNode, isApprox: bool): bool =
# consider: 'n[].kind' --> we really need to support 1 deref op even if this
# is technically wrong due to aliasing :-( We could introduce "soft" facts
# for this; this would still be very useful for warnings and also nicely
# solves the 'var' problems. For now we fix this by requiring much more
# restrictive expressions for the 'not nil' checking.
var n = m
var derefs = 0
while true:
case n.kind
of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv:
n = n.sons[0]
of nkDerefExpr, nkHiddenDeref:
n = n.sons[0]
inc derefs
of nkBracketExpr:
if isConstExpr(n.sons[1]) or isLet(n.sons[1]):
n = n.sons[0]
else: return
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
n = n.sons[1]
else:
break
result = n.isLet and derefs <= ord(isApprox)
if not result and isApprox:
result = isVar(n)
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
proc swapArgs(fact: PNode, newOp: string, m: TMagic): PNode =
result = newNodeI(nkCall, fact.info, 3)
result.sons[0] = newSymNode(getSysMagic(newOp, m))
result.sons[1] = fact.sons[2]
result.sons[2] = fact.sons[1]
proc neg(n: PNode): PNode =
if n == nil: return nil
case n.getMagic
of mNot:
result = n.sons[1]
of someLt:
# not (a < b) == a >= b == b <= a
result = swapArgs(n, "<=", mLeI)
of someLe:
result = swapArgs(n, "<", mLtI)
of mInSet:
if n.sons[1].kind != nkCurly: return nil
let t = n.sons[2].typ.skipTypes(abstractInst)
result = newNodeI(nkCall, n.info, 3)
result.sons[0] = n.sons[0]
result.sons[2] = n.sons[2]
if t.kind == tyEnum:
var s = newNodeIT(nkCurly, n.info, n.sons[1].typ)
for e in t.n:
let eAsNode = newIntNode(nkIntLit, e.sym.position)
if not inSet(n.sons[1], eAsNode): s.add eAsNode
result.sons[1] = s
elif lengthOrd(t) < 1000:
result.sons[1] = complement(n.sons[1])
else:
# not ({2, 3, 4}.contains(x)) x != 2 and x != 3 and x != 4
# XXX todo
result = nil
of mOr:
# not (a or b) --> not a and not b
let
a = n.sons[1].neg
b = n.sons[2].neg
if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(getSysMagic("and", mAnd))
result.sons[1] = a
result.sons[2] = b
elif a != nil:
result = a
elif b != nil:
result = b
else:
# leave not (a == 4) as it is
result = newNodeI(nkCall, n.info, 2)
result.sons[0] = newSymNode(getSysMagic("not", mNot))
result.sons[1] = n
proc buildIsNil(arg: PNode): PNode =
result = newNodeI(nkCall, arg.info, 2)
result.sons[0] = newSymNode(getSysMagic("isNil", mIsNil))
result.sons[1] = arg
proc usefulFact(n: PNode): PNode =
case n.getMagic
of someEq:
if skipConv(n.sons[2]).kind == nkNilLit and (
isLetLocation(n.sons[1], false) or isVar(n.sons[1])):
result = buildIsNil(n.sons[1])
else:
if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
result = n
of someLe+someLt:
if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
result = n
of mIsNil:
if isLetLocation(n.sons[1], false) or isVar(n.sons[1]):
result = n
of someIn:
if isLetLocation(n.sons[1], true):
result = n
of mAnd:
let
a = usefulFact(n.sons[1])
b = usefulFact(n.sons[2])
if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(getSysMagic("and", mAnd))
result.sons[1] = a
result.sons[2] = b
elif a != nil:
result = a
elif b != nil:
result = b
of mNot:
let a = usefulFact(n.sons[1])
if a != nil:
result = a.neg
of mOr:
# 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
# with that knowledge...
# DeMorgan helps a little though:
# not a or not b --> not (a and b)
# (x == 3) or (y == 2) ---> not ( not (x==3) and not (y == 2))
# not (x != 3 and y != 2)
let
a = usefulFact(n.sons[1]).neg
b = usefulFact(n.sons[2]).neg
if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(getSysMagic("and", mAnd))
result.sons[1] = a
result.sons[2] = b
result = result.neg
elif n.kind == nkSym and n.sym.kind == skLet:
# consider:
# let a = 2 < x
# if a:
# ...
# We make can easily replace 'a' by '2 < x' here:
result = usefulFact(n.sym.ast)
elif n.kind == nkStmtListExpr:
result = usefulFact(n.lastSon)
type
TModel* = seq[PNode] # the "knowledge base"
proc addFact*(m: var TModel, n: PNode) =
let n = usefulFact(n)
if n != nil: m.add n
proc addFactNeg*(m: var TModel, n: PNode) =
let n = n.neg
if n != nil: addFact(m, n)
proc sameTree(a, b: PNode): bool =
result = false
if a == b:
result = true
elif (a != nil) and (b != nil) and (a.kind == b.kind):
case a.kind
of nkSym: result = a.sym == b.sym
of nkIdent: result = a.ident.id == b.ident.id
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
of nkType: result = a.typ == b.typ
of nkEmpty, nkNilLit: result = true
else:
if sonsLen(a) == sonsLen(b):
for i in countup(0, sonsLen(a) - 1):
if not sameTree(a.sons[i], b.sons[i]): return
result = true
proc hasSubTree(n, x: PNode): bool =
if n.sameTree(x): result = true
else:
for i in 0..safeLen(n)-1:
if hasSubTree(n.sons[i], x): return true
proc invalidateFacts*(m: var TModel, n: PNode) =
# We are able to guard local vars (as opposed to 'let' variables)!
# 'while p != nil: f(p); p = p.next'
# This is actually quite easy to do:
# Re-assignments (incl. pass to a 'var' param) trigger an invalidation
# of every fact that contains 'v'.
#
# if x < 4:
# if y < 5
# x = unknown()
# # we invalidate 'x' here but it's known that x >= 4
# # for the else anyway
# else:
# echo x
#
# The same mechanism could be used for more complex data stored on the heap;
# procs that 'write: []' cannot invalidate 'n.kind' for instance. In fact, we
# could CSE these expressions then and help C's optimizer.
for i in 0..high(m):
if m[i] != nil and m[i].hasSubTree(n): m[i] = nil
proc valuesUnequal(a, b: PNode): bool =
if a.isValue and b.isValue:
result = not SameValue(a, b)
proc pred(n: PNode): PNode =
if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(biggestInt):
result = copyNode(n)
dec result.intVal
else:
result = n
type
TImplication* = enum
impUnknown, impNo, impYes
proc impliesEq(fact, eq: PNode): TImplication =
let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1)
case fact.sons[0].sym.magic
of someEq:
if sameTree(fact.sons[1], eq.sons[loc]):
# this is not correct; consider: a == b; a == 1 --> unknown!
if sameTree(fact.sons[2], eq.sons[val]): result = impYes
elif valuesUnequal(fact.sons[2], eq.sons[val]): result = impNo
elif sameTree(fact.sons[2], eq.sons[loc]):
if sameTree(fact.sons[1], eq.sons[val]): result = impYes
elif valuesUnequal(fact.sons[1], eq.sons[val]): result = impNo
of mInSet:
# remember: mInSet is 'contains' so the set comes first!
if sameTree(fact.sons[2], eq.sons[loc]) and isValue(eq.sons[val]):
if inSet(fact.sons[1], eq.sons[val]): result = impYes
else: result = impNo
of mNot, mOr, mAnd: internalError(eq.info, "impliesEq")
else: discard
proc leImpliesIn(x, c, aSet: PNode): TImplication =
if c.kind in {nkCharLit..nkUInt64Lit}:
# fact: x <= 4; question x in {56}?
# --> true if every value <= 4 is in the set {56}
#
var value = newIntNode(c.kind, firstOrd(x.typ))
# don't iterate too often:
if c.intVal - value.intVal < 1000:
var i, pos, neg: int
while value.intVal <= c.intVal:
if inSet(aSet, value): inc pos
else: inc neg
inc i; inc value.intVal
if pos == i: result = impYes
elif neg == i: result = impNo
proc geImpliesIn(x, c, aSet: PNode): TImplication =
if c.kind in {nkCharLit..nkUInt64Lit}:
# fact: x >= 4; question x in {56}?
# --> true iff every value >= 4 is in the set {56}
#
var value = newIntNode(c.kind, c.intVal)
let max = lastOrd(x.typ)
# don't iterate too often:
if max - value.intVal < 1000:
var i, pos, neg: int
while value.intVal <= max:
if inSet(aSet, value): inc pos
else: inc neg
inc i; inc value.intVal
if pos == i: result = impYes
elif neg == i: result = impNo
proc compareSets(a, b: PNode): TImplication =
if equalSets(a, b): result = impYes
elif intersectSets(a, b).len == 0: result = impNo
proc impliesIn(fact, loc, aSet: PNode): TImplication =
case fact.sons[0].sym.magic
of someEq:
if sameTree(fact.sons[1], loc):
if inSet(aSet, fact.sons[2]): result = impYes
else: result = impNo
elif sameTree(fact.sons[2], loc):
if inSet(aSet, fact.sons[1]): result = impYes
else: result = impNo
of mInSet:
if sameTree(fact.sons[2], loc):
result = compareSets(fact.sons[1], aSet)
of someLe:
if sameTree(fact.sons[1], loc):
result = leImpliesIn(fact.sons[1], fact.sons[2], aSet)
elif sameTree(fact.sons[2], loc):
result = geImpliesIn(fact.sons[2], fact.sons[1], aSet)
of someLt:
if sameTree(fact.sons[1], loc):
result = leImpliesIn(fact.sons[1], fact.sons[2].pred, aSet)
elif sameTree(fact.sons[2], loc):
# 4 < x --> 3 <= x
result = geImpliesIn(fact.sons[2], fact.sons[1].pred, aSet)
of mNot, mOr, mAnd: internalError(loc.info, "impliesIn")
else: discard
proc valueIsNil(n: PNode): TImplication =
if n.kind == nkNilLit: impYes
elif n.kind in {nkStrLit..nkTripleStrLit, nkBracket, nkObjConstr}: impNo
else: impUnknown
proc impliesIsNil(fact, eq: PNode): TImplication =
case fact.sons[0].sym.magic
of mIsNil:
if sameTree(fact.sons[1], eq.sons[1]):
result = impYes
of someEq:
if sameTree(fact.sons[1], eq.sons[1]):
result = valueIsNil(fact.sons[2].skipConv)
elif sameTree(fact.sons[2], eq.sons[1]):
result = valueIsNil(fact.sons[1].skipConv)
of mNot, mOr, mAnd: internalError(eq.info, "impliesIsNil")
else: discard
proc impliesGe(fact, x, c: PNode): TImplication =
InternalAssert isLocation(x)
case fact.sons[0].sym.magic
of someEq:
if sameTree(fact.sons[1], x):
if isValue(fact.sons[2]) and isValue(c):
# fact: x = 4; question x >= 56? --> true iff 4 >= 56
if leValue(c, fact.sons[2]): result = impYes
else: result = impNo
elif sameTree(fact.sons[2], x):
if isValue(fact.sons[1]) and isValue(c):
if leValue(c, fact.sons[1]): result = impYes
else: result = impNo
of someLt:
if sameTree(fact.sons[1], x):
if isValue(fact.sons[2]) and isValue(c):
# fact: x < 4; question N <= x? --> false iff N <= 4
if leValue(fact.sons[2], c): result = impNo
# fact: x < 4; question 2 <= x? --> we don't know
elif sameTree(fact.sons[2], x):
# fact: 3 < x; question: N-1 < x ? --> true iff N-1 <= 3
if isValue(fact.sons[1]) and isValue(c):
if leValue(c.pred, fact.sons[1]): result = impYes
of someLe:
if sameTree(fact.sons[1], x):
if isValue(fact.sons[2]) and isValue(c):
# fact: x <= 4; question x >= 56? --> false iff 4 <= 56
if leValue(fact.sons[2], c): result = impNo
# fact: x <= 4; question x >= 2? --> we don't know
elif sameTree(fact.sons[2], x):
# fact: 3 <= x; question: x >= 2 ? --> true iff 2 <= 3
if isValue(fact.sons[1]) and isValue(c):
if leValue(c, fact.sons[1]): result = impYes
of mNot, mOr, mAnd: internalError(x.info, "impliesGe")
else: discard
proc impliesLe(fact, x, c: PNode): TImplication =
if not isLocation(x):
return impliesGe(fact, c, x)
case fact.sons[0].sym.magic
of someEq:
if sameTree(fact.sons[1], x):
if isValue(fact.sons[2]) and isValue(c):
# fact: x = 4; question x <= 56? --> true iff 4 <= 56
if leValue(fact.sons[2], c): result = impYes
else: result = impNo
elif sameTree(fact.sons[2], x):
if isValue(fact.sons[1]) and isValue(c):
if leValue(fact.sons[1], c): result = impYes
else: result = impNo
of someLt:
if sameTree(fact.sons[1], x):
if isValue(fact.sons[2]) and isValue(c):
# fact: x < 4; question x <= N? --> true iff N-1 <= 4
if leValue(fact.sons[2], c.pred): result = impYes
# fact: x < 4; question x <= 2? --> we don't know
elif sameTree(fact.sons[2], x):
# fact: 3 < x; question: x <= 1 ? --> false iff 1 <= 3
if isValue(fact.sons[1]) and isValue(c):
if leValue(c, fact.sons[1]): result = impNo
of someLe:
if sameTree(fact.sons[1], x):
if isValue(fact.sons[2]) and isValue(c):
# fact: x <= 4; question x <= 56? --> true iff 4 <= 56
if leValue(fact.sons[2], c): result = impYes
# fact: x <= 4; question x <= 2? --> we don't know
elif sameTree(fact.sons[2], x):
# fact: 3 <= x; question: x <= 2 ? --> false iff 2 < 3
if isValue(fact.sons[1]) and isValue(c):
if leValue(c, fact.sons[1].pred): result = impNo
of mNot, mOr, mAnd: internalError(x.info, "impliesLe")
else: nil
proc impliesLt(fact, x, c: PNode): TImplication =
# x < 3 same as x <= 2:
let p = c.pred
if p != c:
result = impliesLe(fact, x, p)
else:
# 4 < x same as 3 <= x
let q = x.pred
if q != x:
result = impliesLe(fact, q, c)
proc `~`(x: TImplication): TImplication =
case x
of impUnknown: impUnknown
of impNo: impYes
of impYes: impNo
proc factImplies(fact, prop: PNode): TImplication =
case fact.getMagic
of mNot:
# Consider:
# enum nkBinary, nkTernary, nkStr
# fact: not (k <= nkBinary)
# question: k in {nkStr}
# --> 'not' for facts is entirely different than 'not' for questions!
# it's provably wrong if every value > 4 is in the set {56}
# That's because we compute the implication and 'a -> not b' cannot
# be treated the same as 'not a -> b'
# (not a) -> b compute as not (a -> b) ???
# == not a or not b == not (a and b)
let arg = fact.sons[1]
case arg.getMagic
of mIsNil:
return ~factImplies(arg, prop)
of mAnd:
# not (a and b) means not a or not b:
# a or b --> both need to imply 'prop'
let a = factImplies(arg.sons[1], prop)
let b = factImplies(arg.sons[2], prop)
if a == b: return ~a
return impUnknown
else:
InternalError(fact.info, "invalid fact")
of mAnd:
result = factImplies(fact.sons[1], prop)
if result != impUnknown: return result
return factImplies(fact.sons[2], prop)
else: discard
case prop.sons[0].sym.magic
of mNot:
result = ~fact.factImplies(prop.sons[1])
of mIsNil:
result = impliesIsNil(fact, prop)
of someEq:
result = impliesEq(fact, prop)
of someLe:
result = impliesLe(fact, prop.sons[1], prop.sons[2])
of someLt:
result = impliesLt(fact, prop.sons[1], prop.sons[2])
of mInSet:
result = impliesIn(fact, prop.sons[2], prop.sons[1])
else:
internalError(prop.info, "invalid proposition")
proc doesImply*(facts: TModel, prop: PNode): TImplication =
assert prop.kind in nkCallKinds
for f in facts:
# facts can be invalidated, in which case they are 'nil':
if not f.isNil:
result = f.factImplies(prop)
if result != impUnknown: return
proc impliesNotNil*(facts: TModel, arg: PNode): TImplication =
result = doesImply(facts, buildIsNil(arg).neg)
proc settype(n: PNode): PType =
result = newType(tySet, n.typ.owner)
addSonSkipIntLit(result, n.typ)
proc buildOf(it, loc: PNode): PNode =
var s = newNodeI(nkCurly, it.info, it.len-1)
s.typ = settype(loc)
for i in 0..it.len-2: s.sons[i] = it.sons[i]
result = newNodeI(nkCall, it.info, 3)
result.sons[0] = newSymNode(getSysMagic("contains", mInSet))
result.sons[1] = s
result.sons[2] = loc
proc buildElse(n: PNode): PNode =
var s = newNodeIT(nkCurly, n.info, settype(n.sons[0]))
for i in 1..n.len-2:
let branch = n.sons[i]
assert branch.kind == nkOfBranch
for j in 0..branch.len-2:
s.add(branch.sons[j])
result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(getSysMagic("contains", mInSet))
result.sons[1] = s
result.sons[2] = n.sons[0]
proc addDiscriminantFact*(m: var TModel, n: PNode) =
var fact = newNodeI(nkCall, n.info, 3)
fact.sons[0] = newSymNode(getSysMagic("==", mEqI))
fact.sons[1] = n.sons[0]
fact.sons[2] = n.sons[1]
m.add fact
proc addAsgnFact*(m: var TModel, key, value: PNode) =
var fact = newNodeI(nkCall, key.info, 3)
fact.sons[0] = newSymNode(getSysMagic("==", mEqI))
fact.sons[1] = key
fact.sons[2] = value
m.add fact
proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
let branch = n.sons[i]
if branch.kind == nkOfBranch:
m.add buildOf(branch, n.sons[0])
else:
m.add n.buildElse.neg
proc buildProperFieldCheck(access, check: PNode): PNode =
if check.sons[1].kind == nkCurly:
result = copyTree(check)
if access.kind == nkDotExpr:
var a = copyTree(access)
a.sons[1] = check.sons[2]
result.sons[2] = a
# 'access.kind != nkDotExpr' can happen for object constructors
# which we don't check yet
else:
# it is some 'not'
assert check.getMagic == mNot
result = buildProperFieldCheck(access, check.sons[1]).neg
proc checkFieldAccess*(m: TModel, n: PNode) =
for i in 1..n.len-1:
let check = buildProperFieldCheck(n.sons[0], n.sons[i])
if m.doesImply(check) != impYes:
Message(n.info, warnProveField, renderTree(n.sons[0])); break

View file

@ -236,7 +236,7 @@ proc matchTwoChars(L: TLexer, first: Char, second: TCharSet): bool =
result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in Second)
proc isFloatLiteral(s: string): bool =
for i in countup(0, len(s) + 0 - 1):
for i in countup(0, len(s) - 1):
if s[i] in {'.', 'e', 'E'}:
return true
result = false
@ -250,25 +250,26 @@ proc GetNumber(L: var TLexer): TToken =
result.literal = ""
result.base = base10 # BUGFIX
pos = L.bufpos # make sure the literal is correct for error messages:
var eallowed = false
if L.buf[pos] == '0' and L.buf[pos+1] in {'X', 'x'}:
matchUnderscoreChars(L, result, {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x'})
else:
matchUnderscoreChars(L, result, {'0'..'9', 'b', 'B', 'o', 'c', 'C'})
eallowed = true
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
add(result.literal, '.')
inc(L.bufpos)
#matchUnderscoreChars(L, result, ['A'..'Z', 'a'..'z', '0'..'9'])
matchUnderscoreChars(L, result, {'0'..'9'})
if L.buf[L.bufpos] in {'e', 'E'}:
add(result.literal, 'e')
eallowed = true
if eallowed and L.buf[L.bufpos] in {'e', 'E'}:
add(result.literal, 'e')
inc(L.bufpos)
if L.buf[L.bufpos] in {'+', '-'}:
add(result.literal, L.buf[L.bufpos])
inc(L.bufpos)
if L.buf[L.bufpos] in {'+', '-'}:
add(result.literal, L.buf[L.bufpos])
inc(L.bufpos)
matchUnderscoreChars(L, result, {'0'..'9'})
matchUnderscoreChars(L, result, {'0'..'9'})
endpos = L.bufpos
if L.buf[endpos] in {'\'', 'f', 'F', 'i', 'I', 'u', 'U'}:
#matchUnderscoreChars(L, result, ['''', 'f', 'F', 'i', 'I', '0'..'9']);
if L.buf[endpos] == '\'': inc(endpos)
L.bufpos = pos # restore position
case L.buf[endpos]

View file

@ -91,7 +91,8 @@ proc errorSym*(c: PContext, n: PNode): PSym =
result.typ = errorType(c)
incl(result.flags, sfDiscardable)
# pretend it's imported from some unknown module to prevent cascading errors:
c.importTable.addSym(result)
if gCmd != cmdInteractive:
c.importTable.addSym(result)
type
TOverloadIterMode* = enum

View file

@ -43,6 +43,18 @@ proc getSysSym(name: string): PSym =
result.typ = newType(tyError, systemModule)
if result.kind == skStub: loadStub(result)
proc getSysMagic*(name: string, m: TMagic): PSym =
var ti: TIdentIter
let id = getIdent(name)
result = InitIdentIter(ti, systemModule.tab, id)
while result != nil:
if result.kind == skStub: loadStub(result)
if result.magic == m: return result
result = NextIdentIter(ti, systemModule.tab)
rawMessage(errSystemNeeds, name)
result = newSym(skError, id, systemModule, systemModule.info)
result.typ = newType(tyError, systemModule)
proc sysTypeFromName*(name: string): PType =
result = getSysSym(name).typ
@ -111,7 +123,9 @@ proc skipIntLit*(t: PType): PType {.inline.} =
proc AddSonSkipIntLit*(father, son: PType) =
if isNil(father.sons): father.sons = @[]
add(father.sons, son.skipIntLit)
let s = son.skipIntLit
add(father.sons, s)
propagateToOwner(father, s)
proc setIntLitType*(result: PNode) =
let i = result.intVal

View file

@ -106,7 +106,9 @@ type
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored,
warnNilStatement, warnAnalysisLoophole,
warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure,
warnEachIdentIsTuple, warnShadowIdent, warnUser,
warnEachIdentIsTuple, warnShadowIdent,
warnProveInit, warnProveField, warnProveIndex,
warnUninit, warnUser,
hintSuccess, hintSuccessX,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
@ -355,6 +357,10 @@ const
warnImplicitClosure: "implicit closure convention: '$1' [ImplicitClosure]",
warnEachIdentIsTuple: "each identifier is a tuple [EachIdentIsTuple]",
warnShadowIdent: "shadowed identifier: '$1' [ShadowIdent]",
warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future. [ProveInit]",
warnProveField: "cannot prove that field '$1' is accessible [ProveField]",
warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]",
warnUninit: "'$1' might not have been initialized [Uninit]",
warnUser: "$1 [User]",
hintSuccess: "operation successful [Success]",
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]",
@ -374,14 +380,15 @@ const
hintUser: "$1 [User]"]
const
WarningsToStr*: array[0..19, string] = ["CannotOpenFile", "OctalEscape",
WarningsToStr*: array[0..23, string] = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit",
"Deprecated", "ConfigDeprecated",
"SmallLshouldNotBeUsed", "UnknownMagic",
"RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported",
"CommentXIgnored", "NilStmt",
"AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
"ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", "User"]
"ImplicitClosure", "EachIdentIsTuple", "ShadowIdent",
"ProveInit", "ProveField", "ProveIndex", "Uninit", "User"]
HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
@ -512,7 +519,9 @@ proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
proc sourceLine*(i: TLineInfo): PRope
var
gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent}
gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} -
{warnShadowIdent, warnUninit,
warnProveField, warnProveIndex}
gErrorCounter*: int = 0 # counts the number of errors
gHintCounter*: int = 0
gWarnCounter*: int = 0
@ -789,11 +798,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
if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0:
try:
for line in lines(i.toFullPath):
addSourceLine i.fileIndex, line.string
except EIO:
discard
InternalAssert i.fileIndex < fileInfos.len
# can happen if the error points to EOF:
if i.line > fileInfos[i.fileIndex].lines.len: return nil

View file

@ -64,7 +64,6 @@ Files: "bin/empty.txt"
[Lib]
Files: "lib/nimbase.h"
Files: "lib/copying.txt"
Files: "lib/*.nim"
Files: "lib/*.cfg"

View file

@ -156,6 +156,12 @@ proc equalSets(a, b: PNode): bool =
toBitSet(b, y)
result = bitSetEquals(x, y)
proc complement*(a: PNode): PNode =
var x: TBitSet
toBitSet(a, x)
for i in countup(0, high(x)): x[i] = not x[i]
result = toTreeSet(x, a.typ, a.info)
proc cardSet(s: PNode): BiggestInt =
# here we can do better than converting it into a compact set
# we just count the elements directly

View file

@ -14,7 +14,7 @@ const
hasTinyCBackend* = defined(tinyc)
useEffectSystem* = true
hasFFI* = defined(useFFI)
newScopeForIf* = false # XXX activate for 0.9.4
newScopeForIf* = true
type # please make sure we have under 32 options
# (improves code efficiency a lot!)

View file

@ -50,6 +50,8 @@ proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
proc whichAlias*(p: PSym): TAliasRequest =
if p.constraint != nil:
result = TAliasRequest(p.constraint.strVal[0].ord)
else:
result = aqNone
proc compileConstraints(p: PNode, result: var TPatternCode) =
case p.kind

View file

@ -1748,7 +1748,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
proc parseStmt(p: var TParser): PNode =
#| stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED)
#| / simpleStmt
#| / simpleStmt ^+ ';'
if p.tok.indent > p.currInd:
result = newNodeP(nkStmtList, p)
withInd(p):
@ -1779,10 +1779,14 @@ proc parseStmt(p: var TParser): PNode =
parMessage(p, errComplexStmtRequiresInd)
result = ast.emptyNode
else:
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
result = simpleStmt(p)
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
#while p.tok.tokType == tkSemicolon: getTok(p)
result = newNodeP(nkStmtList, p)
while true:
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
let a = simpleStmt(p)
if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
result.add(a)
if p.tok.tokType != tkSemicolon: break
getTok(p)
proc parseAll(p: var TParser): PNode =
result = newNodeP(nkStmtList, p)

View file

@ -23,7 +23,7 @@ const
wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wHoist,
wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
wGenSym, wInject, wRaises, wTags}
converterPragmas* = procPragmas
methodPragmas* = procPragmas
@ -50,13 +50,13 @@ const
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow,
wImportcpp, wImportobjc, wError, wIncompleteStruct, wByCopy, wByRef,
wInheritable, wGenSym, wInject}
wInheritable, wGenSym, wInject, wRequiresInit}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportcpp, wImportobjc, wError}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
wMagic, wHeader, wDeprecated, wCompilerProc, wDynLib, wExtern,
wImportcpp, wImportobjc, wError, wNoInit, wCompileTime, wGlobal,
wGenSym, wInject}
wGenSym, wInject, wCodegenDecl}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
wExtern, wImportcpp, wImportobjc, wError, wGenSym, wInject}
letPragmas* = varPragmas
@ -148,6 +148,9 @@ proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
if n.kind == nkExprColonExpr: result = expectStrLit(c, n)
else: result = defaultStr
proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) =
sym.constraint = getStrLitNode(c, n)
proc processMagic(c: PContext, n: PNode, s: PSym) =
#if sfSystemModule notin c.module.flags:
# liMessage(n.info, errMagicOnlyInSystem)
@ -253,11 +256,11 @@ proc processNote(c: PContext, n: PNode) =
of wHint:
var x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s)
if x >= 0: nk = TNoteKind(x + ord(hintMin))
else: invalidPragma(n)
else: invalidPragma(n); return
of wWarning:
var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s)
if x >= 0: nk = TNoteKind(x + ord(warnMin))
else: InvalidPragma(n)
else: InvalidPragma(n); return
else:
invalidPragma(n)
return
@ -669,9 +672,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
of wNoInit:
noVal(it)
if sym != nil: incl(sym.flags, sfNoInit)
of wHoist:
noVal(it)
if sym != nil: incl(sym.flags, sfHoist)
of wCodegenDecl: processCodegenDecl(c, it, sym)
of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization,
@ -695,6 +696,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfIncompleteStruct)
of wRequiresInit:
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfNeedsInit)
of wByRef:
noVal(it)
if sym == nil or sym.typ == nil:

View file

@ -33,6 +33,7 @@ type
# indentation value
comStack*: seq[PNode] # comment stack
flags*: TRenderFlags
checkAnon: bool # we're in a context that can contain sfAnon
proc renderModule*(n: PNode, filename: string, renderFlags: TRenderFlags = {})
@ -70,7 +71,8 @@ proc InitSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) =
g.idx = 0
g.buf = ""
g.flags = renderFlags
g.pendingNL = - 1
g.pendingNL = -1
g.checkAnon = false
proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
var length = len(g.tokens)
@ -497,9 +499,11 @@ proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0,
var c = i < sonsLen(n) + theEnd
var sublen = lsub(n.sons[i]) + ord(c)
if not fits(g, sublen) and (ind + sublen < maxLineLen): optNL(g, ind)
let oldLen = g.tokens.len
gsub(g, n.sons[i])
if c:
putWithSpace(g, separator, TokTypeToStr[separator])
if g.tokens.len > oldLen:
putWithSpace(g, separator, TokTypeToStr[separator])
if hasCom(n.sons[i]):
gcoms(g)
optNL(g, ind)
@ -673,7 +677,10 @@ proc gproc(g: var TSrcGen, n: PNode) =
if n.sons[patternPos].kind != nkEmpty:
gpattern(g, n.sons[patternPos])
let oldCheckAnon = g.checkAnon
g.checkAnon = true
gsub(g, n.sons[genericParamsPos])
g.checkAnon = oldCheckAnon
gsub(g, n.sons[paramsPos])
gsub(g, n.sons[pragmasPos])
if renderNoBody notin g.flags:
@ -725,6 +732,7 @@ proc gasm(g: var TSrcGen, n: PNode) =
gsub(g, n.sons[1])
proc gident(g: var TSrcGen, n: PNode) =
if g.checkAnon and n.kind == nkSym and sfAnon in n.sym.flags: return
var t: TTokType
var s = atom(n)
if (s[0] in lexer.SymChars):
@ -898,7 +906,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkColon, ":")
gsub(g, n.sons[bodyPos])
of nkConstDef, nkIdentDefs:
gcomma(g, n, 0, - 3)
gcomma(g, n, 0, -3)
var L = sonsLen(n)
if n.sons[L - 2].kind != nkEmpty:
putWithSpace(g, tkColon, ":")
@ -909,7 +917,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gsub(g, n.sons[L - 1], c)
of nkVarTuple:
put(g, tkParLe, "(")
gcomma(g, n, 0, - 3)
gcomma(g, n, 0, -3)
put(g, tkParRi, ")")
put(g, tkSpaces, Space)
putWithSpace(g, tkEquals, "=")

View file

@ -590,7 +590,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
call.add(a)
#echo "NOW evaluating at compile time: ", call.renderTree
if sfCompileTime in callee.flags:
result = evalStaticExpr(c.module, call)
result = evalStaticExpr(c.module, call, c.p.owner)
if result.isNil:
LocalError(n.info, errCannotInterpretNodeX, renderTree(call))
else:
@ -601,9 +601,10 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
proc semStaticExpr(c: PContext, n: PNode): PNode =
let a = semExpr(c, n.sons[0])
result = evalStaticExpr(c.module, a)
result = evalStaticExpr(c.module, a, c.p.owner)
if result.isNil:
LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
result = emptyNode
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
flags: TExprFlags): PNode =
@ -1290,6 +1291,7 @@ proc newAnonSym(kind: TSymKind, info: TLineInfo,
proc semExpandToAst(c: PContext, n: PNode): PNode =
var macroCall = n[1]
var expandedSym = expectMacroOrTemplateCall(c, macroCall)
if expandedSym.kind == skError: return n
macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
markUsed(n, expandedSym)
@ -1601,6 +1603,25 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
addSonSkipIntLit(typ, n.sons[i].typ)
result.typ = typ
proc checkInitialized(n: PNode, ids: TIntSet, info: TLineInfo) =
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
checkInitialized(n.sons[i], ids, info)
of nkRecCase:
if (n.sons[0].kind != nkSym): InternalError(info, "checkInitialized")
checkInitialized(n.sons[0], ids, info)
when false:
# XXX we cannot check here, as we don't know the branch!
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse: checkInitialized(lastSon(n.sons[i]), ids, info)
else: internalError(info, "checkInitialized")
of nkSym:
if tfNeedsInit in n.sym.typ.flags and n.sym.name.id notin ids:
Message(info, errGenerated, "field not initialized: " & n.sym.name.s)
else: internalError(info, "checkInitialized")
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
var t = semTypeNode(c, n.sons[0], nil)
result = n
@ -1611,6 +1632,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
if t.kind != tyObject:
localError(n.info, errGenerated, "object constructor needs an object type")
return
var objType = t
var ids = initIntSet()
for i in 1.. <n.len:
let it = n.sons[i]
@ -1637,11 +1659,18 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
e = fitNode(c, f.typ, e)
# small hack here in a nkObjConstr the ``nkExprColonExpr`` node can have
# 3 childen the last being the field check
if check != nil: it.add(check)
if check != nil:
check.sons[0] = it.sons[0]
it.add(check)
else:
localError(it.info, errUndeclaredFieldX, id.s)
it.sons[1] = e
# XXX object field name check for 'case objects' if the kind is static?
if tfNeedsInit in objType.flags:
while true:
checkInitialized(objType.n, ids, n.info)
if objType.sons[0] == nil: break
objType = skipTypes(objType.sons[0], {tyGenericInst})
proc semBlock(c: PContext, n: PNode): PNode =
result = n

View file

@ -9,12 +9,13 @@
import
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
wordrecg, strutils, options
wordrecg, strutils, options, guards
# Second semantic checking pass over the AST. Necessary because the old
# way had some inherent problems. Performs:
#
# * effect+exception tracking
# * "usage before definition" checking
# * 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
@ -43,14 +44,6 @@ import
# done: It essentially requires a built-in 'indexSplit' operation and dependent
# typing.
when false:
proc sem2call(c: PContext, n: PNode): PNode =
assert n.kind in nkCallKinds
proc sem2sym(c: PContext, n: PNode): PNode =
assert n.kind == nkSym
# ------------------------ exception and tag tracking -------------------------
discard """
@ -64,14 +57,6 @@ discard """
c()
--> we need a stack of scopes for this analysis
Effect tracking:
We track the effects per proc; forward declarations and indirect calls cause
problems: Forward declarations are computed lazily (we do this pass after
a whole module) and indirect calls are assumed the worst, unless they have
an effect annotation.
"""
type
@ -80,9 +65,55 @@ type
tags: PNode # list of tags
bottom: int
owner: PSym
init: seq[int] # list of initialized variables
guards: TModel # nested guards
locked: seq[PNode] # locked locations
PEffects = var TEffects
proc isLocalVar(a: PEffects, s: PSym): bool =
s.kind in {skVar, skResult} and sfGlobal notin s.flags and s.owner == a.owner
proc initVar(a: PEffects, n: PNode) =
if n.kind != nkSym: return
let s = n.sym
if isLocalVar(a, s):
for x in a.init:
if x == s.id: return
a.init.add s.id
proc initVarViaNew(a: PEffects, n: PNode) =
if n.kind != nkSym: return
let s = n.sym
if {tfNeedsInit, tfNotNil} * s.typ.flags == {tfNotNil}:
# 'x' is not nil, but that doesn't mean it's not nil children
# are initialized:
initVarViaNew(a, n)
proc useVar(a: PEffects, n: PNode) =
let s = n.sym
if isLocalVar(a, s):
if s.id notin a.init:
if {tfNeedsInit, tfNotNil} * s.typ.flags != {}:
when true:
Message(n.info, warnProveInit, s.name.s)
else:
Message(n.info, errGenerated,
"'$1' might not have been initialized" % s.name.s)
else:
Message(n.info, warnUninit, s.name.s)
# prevent superfluous warnings about the same variable:
a.init.add s.id
type
TIntersection = seq[tuple[id, count: int]] # a simple count table
proc addToIntersection(inter: var TIntersection, s: int) =
for j in 0.. <inter.len:
if s == inter[j].id:
inc inter[j].count
return
inter.add((id: s, count: 1))
proc throws(tracked, n: PNode) =
if n.typ == nil or n.typ.kind != tyError: tracked.add n
@ -158,21 +189,43 @@ proc track(tracked: PEffects, n: PNode)
proc trackTryStmt(tracked: PEffects, n: PNode) =
let oldBottom = tracked.bottom
tracked.bottom = tracked.exc.len
let oldState = tracked.init.len
var inter: TIntersection = @[]
track(tracked, n.sons[0])
for i in oldState.. <tracked.init.len:
addToIntersection(inter, tracked.init[i])
var branches = 1
var hasFinally = false
for i in 1 .. < n.len:
let b = n.sons[i]
let blen = sonsLen(b)
if b.kind == nkExceptBranch:
inc branches
if blen == 1:
catchesAll(tracked)
else:
for j in countup(0, blen - 2):
assert(b.sons[j].kind == nkType)
catches(tracked, b.sons[j].typ)
setLen(tracked.init, oldState)
track(tracked, b.sons[blen-1])
for i in oldState.. <tracked.init.len:
addToIntersection(inter, tracked.init[i])
else:
assert b.kind == nkFinally
track(tracked, b.sons[blen-1])
setLen(tracked.init, oldState)
track(tracked, b.sons[blen-1])
hasFinally = true
tracked.bottom = oldBottom
if not hasFinally:
setLen(tracked.init, oldState)
for id, count in items(inter):
if count == branches: tracked.init.add id
proc isIndirectCall(n: PNode, owner: PSym): bool =
# we don't count f(...) as an indirect call if 'f' is an parameter.
@ -245,7 +298,25 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
let tagSpec = effectSpec(pragma, wTags)
mergeTags(tracked, tagSpec, n)
proc trackOperand(tracked: PEffects, n: PNode) =
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
let n = n.skipConv
if paramType != nil and tfNotNil in paramType.flags and
n.typ != nil and tfNotNil notin n.typ.flags:
if n.kind == nkAddr:
# addr(x[]) can't be proven, but addr(x) can:
if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
elif n.kind == nkSym and n.sym.kind in RoutineKinds:
# 'p' is not nil obviously:
return
case impliesNotNil(tracked.guards, n)
of impUnknown:
Message(n.info, errGenerated,
"cannot prove '$1' is not nil" % n.renderTree)
of impNo:
Message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree)
of impYes: discard
proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
let op = n.typ
if op != nil and op.kind == tyProc and n.kind != nkNilLit:
InternalAssert op.n.sons[0].kind == nkEffectList
@ -262,12 +333,117 @@ proc trackOperand(tracked: PEffects, n: PNode) =
else:
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n)
notNilCheck(tracked, n, paramType)
proc breaksBlock(n: PNode): bool =
case n.kind
of nkStmtList, nkStmtListExpr:
for c in n:
if breaksBlock(c): return true
of nkBreakStmt, nkReturnStmt, nkRaiseStmt:
return true
of nkCallKinds:
if n.sons[0].kind == nkSym and sfNoReturn in n.sons[0].sym.flags:
return true
else:
discard
proc trackCase(tracked: PEffects, n: PNode) =
track(tracked, n.sons[0])
let oldState = tracked.init.len
let oldFacts = tracked.guards.len
let interesting = interestingCaseExpr(n.sons[0]) and warnProveField in gNotes
var inter: TIntersection = @[]
var toCover = 0
for i in 1.. <n.len:
let branch = n.sons[i]
setLen(tracked.init, oldState)
if interesting:
setLen(tracked.guards, oldFacts)
addCaseBranchFacts(tracked.guards, n, i)
for i in 0 .. <branch.len:
track(tracked, branch.sons[i])
if not breaksBlock(branch.lastSon): inc toCover
for i in oldState.. <tracked.init.len:
addToIntersection(inter, tracked.init[i])
let exh = case skipTypes(n.sons[0].Typ, abstractVarRange-{tyTypeDesc}).Kind
of tyFloat..tyFloat128, tyString:
lastSon(n).kind == nkElse
else:
true
setLen(tracked.init, oldState)
if exh:
for id, count in items(inter):
if count >= toCover: tracked.init.add id
# else we can't merge
setLen(tracked.guards, oldFacts)
proc trackIf(tracked: PEffects, n: PNode) =
track(tracked, n.sons[0].sons[0])
let oldFacts = tracked.guards.len
addFact(tracked.guards, n.sons[0].sons[0])
let oldState = tracked.init.len
var inter: TIntersection = @[]
var toCover = 0
track(tracked, n.sons[0].sons[1])
if not breaksBlock(n.sons[0].sons[1]): inc toCover
for i in oldState.. <tracked.init.len:
addToIntersection(inter, tracked.init[i])
for i in 1.. <n.len:
let branch = n.sons[i]
setLen(tracked.guards, oldFacts)
for j in 0..i-1:
addFactNeg(tracked.guards, n.sons[j].sons[0])
if branch.len > 1:
addFact(tracked.guards, branch.sons[0])
setLen(tracked.init, oldState)
for i in 0 .. <branch.len:
track(tracked, branch.sons[i])
if not breaksBlock(branch.lastSon): inc toCover
for i in oldState.. <tracked.init.len:
addToIntersection(inter, tracked.init[i])
setLen(tracked.init, oldState)
if lastSon(n).len == 1:
for id, count in items(inter):
if count >= toCover: tracked.init.add id
# else we can't merge as it is not exhaustive
setLen(tracked.guards, oldFacts)
proc trackBlock(tracked: PEffects, n: PNode) =
if n.kind in {nkStmtList, nkStmtListExpr}:
var oldState = -1
for i in 0.. <n.len:
if hasSubnodeWith(n.sons[i], nkBreakStmt):
# block:
# x = def
# if ...: ... break # some nested break
# y = def
# --> 'y' not defined after block!
if oldState < 0: oldState = tracked.init.len
track(tracked, n.sons[i])
if oldState > 0: setLen(tracked.init, oldState)
else:
track(tracked, n)
proc isTrue(n: PNode): bool =
n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
n.kind == nkIntLit and n.intVal != 0
proc paramType(op: PType, i: int): PType =
if op != nil and i < op.len: result = op.sons[i]
proc track(tracked: PEffects, n: PNode) =
case n.kind
of nkSym:
useVar(tracked, n)
of nkRaiseStmt:
n.sons[0].info = n.info
throws(tracked.exc, n.sons[0])
for i in 0 .. <safeLen(n):
track(tracked, n.sons[i])
of nkCallKinds:
# p's effects are ours too:
let a = n.sons[0]
@ -286,17 +462,71 @@ proc track(tracked: PEffects, n: PNode) =
else:
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n)
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i])
of nkTryStmt:
trackTryStmt(tracked, n)
return
of nkPragma:
trackPragmaStmt(tracked, n)
return
of nkMacroDef, nkTemplateDef: return
else: nil
for i in 0 .. <safeLen(n):
track(tracked, n.sons[i])
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize,
mNewSeq, mShallowCopy}:
# may not look like an assignment, but it is:
initVarViaNew(tracked, n.sons[1])
for i in 0 .. <safeLen(n):
track(tracked, n.sons[i])
of nkCheckedFieldExpr:
track(tracked, n.sons[0])
if warnProveField in gNotes: checkFieldAccess(tracked.guards, n)
of nkTryStmt: trackTryStmt(tracked, n)
of nkPragma: trackPragmaStmt(tracked, n)
of nkMacroDef, nkTemplateDef: discard
of nkAsgn, nkFastAsgn:
track(tracked, n.sons[1])
initVar(tracked, n.sons[0])
invalidateFacts(tracked.guards, n.sons[0])
track(tracked, n.sons[0])
addAsgnFact(tracked.guards, n.sons[0], n.sons[1])
notNilCheck(tracked, n.sons[1], n.sons[0].typ)
of nkVarSection:
for child in n:
let last = lastSon(child)
if child.kind == nkIdentDefs and last.kind != nkEmpty:
track(tracked, last)
for i in 0 .. child.len-3:
initVar(tracked, child.sons[i])
addAsgnFact(tracked.guards, child.sons[i], last)
notNilCheck(tracked, last, child.sons[i].typ)
# since 'var (a, b): T = ()' is not even allowed, there is always type
# inference for (a, b) and thus no nil checking is necessary.
of nkCaseStmt: trackCase(tracked, n)
of nkIfStmt, nkIfExpr: trackIf(tracked, n)
of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n.sons[1])
of nkWhileStmt:
track(tracked, n.sons[0])
# 'while true' loop?
if isTrue(n.sons[0]):
trackBlock(tracked, n.sons[1])
else:
# loop may never execute:
let oldState = tracked.init.len
let oldFacts = tracked.guards.len
addFact(tracked.guards, n.sons[0])
track(tracked, n.sons[1])
setLen(tracked.init, oldState)
setLen(tracked.guards, oldFacts)
of nkForStmt, nkParForStmt:
# we are very conservative here and assume the loop is never executed:
let oldState = tracked.init.len
for i in 0 .. <len(n):
track(tracked, n.sons[i])
setLen(tracked.init, oldState)
of nkObjConstr:
track(tracked, n.sons[0])
let oldFacts = tracked.guards.len
for i in 1 .. <len(n):
let x = n.sons[i]
track(tracked, x)
if sfDiscriminant in x.sons[0].sym.flags:
addDiscriminantFact(tracked.guards, x)
setLen(tracked.guards, oldFacts)
of nkTypeSection: discard
else:
for i in 0 .. <safeLen(n): track(tracked, n.sons[i])
proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool) =
# check that any real exception is listed in 'spec'; mark those as used;
@ -362,8 +592,15 @@ proc trackProc*(s: PSym, body: PNode) =
t.exc = effects.sons[exceptionEffects]
t.tags = effects.sons[tagEffects]
t.owner = s
t.init = @[]
t.guards = @[]
track(t, body)
if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and
s.kind in {skProc, skConverter, skMethod}:
var res = s.ast.sons[resultPos].sym # get result symbol
if res.id notin t.init:
Message(body.info, warnProveInit, "result")
let p = s.ast.sons[pragmasPos]
let raisesSpec = effectSpec(p, wRaises)
if not isNil(raisesSpec):

View file

@ -146,7 +146,9 @@ proc discardCheck(result: PNode) =
if result.typ.kind == tyNil:
fixNilType(result)
else:
localError(result.info, errDiscardValue)
var n = result
while n.kind in skipForDiscardable: n = n.lastSon
localError(n.info, errDiscardValue)
proc semIf(c: PContext, n: PNode): PNode =
result = n
@ -188,7 +190,7 @@ proc semCase(c: PContext, n: PNode): PNode =
var typ = CommonTypeBegin
var hasElse = false
case skipTypes(n.sons[0].Typ, abstractVarRange-{tyTypeDesc}).Kind
of tyInt..tyInt64, tyChar, tyEnum:
of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32:
chckCovered = true
of tyFloat..tyFloat128, tyString, tyError:
nil
@ -313,6 +315,13 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
result = semIdentWithPragma(c, kind, n, {})
suggestSym(n, result)
proc checkNilable(v: PSym) =
if sfGlobal in v.flags and {tfNotNil, tfNeedsInit} * v.typ.flags != {}:
if v.ast.isNil:
Message(v.info, warnProveInit, v.name.s)
elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
Message(v.info, warnProveInit, v.name.s)
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var b: PNode
result = copyNode(n)
@ -371,11 +380,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
# side of the '=':
if warnShadowIdent in gNotes and not identWithin(def, v.name):
Message(a.info, warnShadowIdent, v.name.s)
if def != nil and def.kind != nkEmpty:
# this is only needed for the evaluation pass:
v.ast = def
if sfThread in v.flags: LocalError(def.info, errThreadvarCannotInit)
if a.kind != nkVarTuple:
if def != nil and def.kind != nkEmpty:
# this is needed for the evaluation pass and for the guard checking:
v.ast = def
if sfThread in v.flags: LocalError(def.info, errThreadvarCannotInit)
v.typ = typ
b = newNodeI(nkIdentDefs, a.info)
if importantComments():
@ -386,8 +395,10 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
addSon(b, copyTree(def))
addSon(result, b)
else:
if def.kind == nkPar: v.ast = def[j]
v.typ = tup.sons[j]
b.sons[j] = newSymNode(v)
checkNilable(v)
proc semConst(c: PContext, n: PNode): PNode =
result = copyNode(n)
@ -1128,9 +1139,10 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
proc semStaticStmt(c: PContext, n: PNode): PNode =
let a = semStmt(c, n.sons[0])
result = evalStaticExpr(c.module, a)
result = evalStaticExpr(c.module, a, c.p.owner)
if result.isNil:
LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
result = emptyNode
elif result.kind == nkEmpty:
result = newNodeI(nkDiscardStmt, n.info, 1)
result.sons[0] = emptyNode

View file

@ -39,6 +39,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
counter = lastOrd(base) + 1
rawAddSon(result, base)
let isPure = result.sym != nil and sfPure in result.sym.flags
var hasNull = false
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkEnumFieldDef:
@ -74,6 +75,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
else: illFormedAst(n)
e.typ = result
e.position = int(counter)
if e.position == 0: hasNull = true
if result.sym != nil and sfExported in result.sym.flags:
incl(e.flags, sfUsed)
incl(e.flags, sfExported)
@ -81,6 +83,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
addSon(result.n, newSymNode(e))
if sfGenSym notin e.flags and not isPure: addDecl(c, e)
inc(counter)
if not hasNull: incl(result.flags, tfNeedsInit)
proc semSet(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tySet, prev, c)
@ -168,7 +171,14 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
proc semRange(c: PContext, n: PNode, prev: PType): PType =
result = nil
if sonsLen(n) == 2:
if isRange(n[1]): result = semRangeAux(c, n[1], prev)
if isRange(n[1]):
result = semRangeAux(c, n[1], prev)
let n = result.n
if n.sons[0].kind in {nkCharLit..nkUInt64Lit}:
if n.sons[0].intVal > 0 or n.sons[1].intVal < 0:
incl(result.flags, tfNeedsInit)
elif n.sons[0].floatVal > 0.0 or n.sons[1].floatVal < 0.0:
incl(result.flags, tfNeedsInit)
else:
LocalError(n.sons[0].info, errRangeExpected)
result = newOrPrevType(tyError, prev, c)
@ -387,9 +397,9 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
checkForOverlap(c, t, i, branchIndex)
proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
father: PNode, rectype: PSym)
father: PNode, rectype: PType)
proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int,
father: PNode, rectype: PSym) =
father: PNode, rectype: PType) =
var a = copyNode(n)
checkMinSonsLen(n, 2)
semRecordNodeAux(c, n.sons[0], check, pos, a, rectype)
@ -424,7 +434,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int,
addSon(father, a)
proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
father: PNode, rectype: PSym) =
father: PNode, rectype: PType) =
if n == nil: return
case n.kind
of nkRecWhen:
@ -473,7 +483,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
checkMinSonsLen(n, 3)
var length = sonsLen(n)
var a: PNode
if father.kind != nkRecList and length >= 4: a = newNodeI(nkRecList, n.info)
if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info)
else: a = ast.emptyNode
if n.sons[length-1].kind != nkEmpty:
localError(n.sons[length-1].info, errInitHereNotAllowed)
@ -483,15 +493,17 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
typ = errorType(c)
else:
typ = semTypeNode(c, n.sons[length-2], nil)
propagateToOwner(rectype, typ)
let rec = rectype.sym
for i in countup(0, sonsLen(n)-3):
var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported})
suggestSym(n.sons[i], f)
f.typ = typ
f.position = pos
if (rectype != nil) and ({sfImportc, sfExportc} * rectype.flags != {}) and
if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and
(f.loc.r == nil):
f.loc.r = toRope(f.name.s)
f.flags = f.flags + ({sfImportc, sfExportc} * rectype.flags)
f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags)
inc(pos)
if ContainsOrIncl(check, f.name.id):
localError(n.sons[i].info, errAttemptToRedefine, f.name.s)
@ -553,7 +565,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tyObject, prev, c)
rawAddSon(result, base)
result.n = newNodeI(nkRecList, n.info)
semRecordNodeAux(c, n.sons[2], check, pos, result.n, result.sym)
semRecordNodeAux(c, n.sons[2], check, pos, result.n, result)
if n.sons[0].kind != nkEmpty:
# dummy symbol for `pragma`:
var s = newSymS(skType, newIdentNode(getIdent("dummy"), n.info), c)

View file

@ -152,6 +152,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
x = lookupTypeVar(cl, x)
if header == nil: header = copyType(t, t.owner, false)
header.sons[i] = x
propagateToOwner(header, x)
#idTablePut(cl.typeMap, body.sons[i-1], x)
if header != nil:
# search again after first pass:
@ -170,6 +171,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
var x = replaceTypeVarsT(cl, t.sons[i])
assert x.kind != tyGenericInvokation
header.sons[i] = x
propagateToOwner(header, x)
idTablePut(cl.typeMap, body.sons[i-1], x)
for i in countup(1, sonsLen(t) - 1):

View file

@ -48,6 +48,9 @@ type
isFromIntLit, # conversion *from* int literal; proven safe
isEqual
const
isNilConversion = isConvertible # maybe 'isIntConv' fits better?
proc markUsed*(n: PNode, s: PSym)
proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} =
@ -163,6 +166,15 @@ proc writeMatches*(c: TCandidate) =
Writeln(stdout, "intconv matches: " & $c.intConvMatches)
Writeln(stdout, "generic matches: " & $c.genericMatches)
proc argTypeToString(arg: PNode): string =
if arg.kind in nkSymChoices:
result = typeToString(arg[0].typ)
for i in 1 .. <arg.len:
result.add(" | ")
result.add typeToString(arg[i].typ)
else:
result = arg.typ.typeToString
proc NotFoundError*(c: PContext, n: PNode) =
# Gives a detailed error message; this is separated from semOverloadedCall,
# as semOverlodedCall is already pretty slow (and we need this information
@ -172,20 +184,20 @@ proc NotFoundError*(c: PContext, n: PNode) =
GlobalError(n.info, errTypeMismatch, "")
var result = msgKindToString(errTypeMismatch)
for i in countup(1, sonsLen(n) - 1):
var nt = n.sons[i].typ
var arg = n.sons[i]
if n.sons[i].kind == nkExprEqExpr:
add(result, renderTree(n.sons[i].sons[0]))
add(result, ": ")
if nt.isNil:
n.sons[i].sons[1] = c.semOperand(c, n.sons[i].sons[1])
nt = n.sons[i].sons[1].typ
n.sons[i].typ = nt
if arg.typ.isNil:
arg = c.semOperand(c, n.sons[i].sons[1])
n.sons[i].typ = arg.typ
n.sons[i].sons[1] = arg
else:
if nt.isNil:
n.sons[i] = c.semOperand(c, n.sons[i])
nt = n.sons[i].typ
if nt.kind == tyError: return
add(result, typeToString(nt))
if arg.typ.isNil:
arg = c.semOperand(c, n.sons[i])
n.sons[i] = arg
if arg.typ.kind == tyError: return
add(result, argTypeToString(arg))
if i != sonsLen(n) - 1: add(result, ", ")
add(result, ')')
var candidates = ""
@ -471,6 +483,8 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
else:
result = typeRel(c, f.sons[0], a.sons[0])
if result < isGeneric: result = isNone
elif tfNotNil in f.flags and tfNotNil notin a.flags:
result = isNilConversion
of tyNil: result = f.allowsNil
else: nil
of tyOrdinal:
@ -506,6 +520,8 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
of tyPtr:
result = typeRel(c, base(f), base(a))
if result <= isConvertible: result = isNone
elif tfNotNil in f.flags and tfNotNil notin a.flags:
result = isNilConversion
of tyNil: result = f.allowsNil
else: nil
of tyRef:
@ -513,13 +529,21 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
of tyRef:
result = typeRel(c, base(f), base(a))
if result <= isConvertible: result = isNone
elif tfNotNil in f.flags and tfNotNil notin a.flags:
result = isNilConversion
of tyNil: result = f.allowsNil
else: nil
of tyProc:
result = procTypeRel(c, f, a)
if result != isNone and tfNotNil in f.flags and tfNotNil notin a.flags:
result = isNilConversion
of tyPointer:
case a.kind
of tyPointer: result = isEqual
of tyPointer:
if tfNotNil in f.flags and tfNotNil notin a.flags:
result = isNilConversion
else:
result = isEqual
of tyNil: result = f.allowsNil
of tyProc:
if a.callConv != ccClosure: result = isConvertible
@ -527,13 +551,21 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
else: nil
of tyString:
case a.kind
of tyString: result = isEqual
of tyString:
if tfNotNil in f.flags and tfNotNil notin a.flags:
result = isNilConversion
else:
result = isEqual
of tyNil: result = f.allowsNil
else: nil
of tyCString:
# conversion from string to cstring is automatic:
case a.Kind
of tyCString: result = isEqual
of tyCString:
if tfNotNil in f.flags and tfNotNil notin a.flags:
result = isNilConversion
else:
result = isEqual
of tyNil: result = f.allowsNil
of tyString: result = isConvertible
of tyPtr:
@ -851,6 +883,20 @@ proc prepareNamedParam(a: PNode) =
var info = a.sons[0].info
a.sons[0] = newIdentNode(considerAcc(a.sons[0]), info)
proc arrayConstr(c: PContext, n: PNode): PType =
result = newTypeS(tyArrayConstr, c)
rawAddSon(result, makeRangeType(c, 0, 0, n.info))
addSonSkipIntLit(result, skipTypes(n.typ, {tyGenericInst, tyVar, tyOrdinal}))
proc arrayConstr(c: PContext, info: TLineInfo): PType =
result = newTypeS(tyArrayConstr, c)
rawAddSon(result, makeRangeType(c, 0, -1, info))
rawAddSon(result, newTypeS(tyEmpty, c)) # needs an empty basetype!
proc incrIndexType(t: PType) =
assert t.kind == tyArrayConstr
inc t.sons[0].n.sons[1].intVal
proc matchesAux(c: PContext, n, nOrig: PNode,
m: var TCandidate, marker: var TIntSet) =
template checkConstraint(n: expr) {.immediate, dirty.} =
@ -901,7 +947,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
checkConstraint(n.sons[a].sons[1])
if m.baseTypeMatch:
assert(container == nil)
container = newNodeI(nkBracket, n.sons[a].info)
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
addSon(container, arg)
setSon(m.call, formal.position + 1, container)
if f != formalLen - 1: container = nil
@ -927,6 +973,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
n.sons[a], nOrig.sons[a])
if (arg != nil) and m.baseTypeMatch and (container != nil):
addSon(container, arg)
incrIndexType(container.typ)
else:
m.state = csNoMatch
return
@ -952,7 +999,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
return
if m.baseTypeMatch:
assert(container == nil)
container = newNodeI(nkBracket, n.sons[a].info)
container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
addSon(container, arg)
setSon(m.call, formal.position + 1,
implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
@ -985,7 +1032,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
if not ContainsOrIncl(marker, formal.position):
if formal.ast == nil:
if formal.typ.kind == tyVarargs:
var container = newNodeI(nkBracket, n.info)
var container = newNodeIT(nkBracket, n.info, arrayConstr(c, n.info))
addSon(m.call, implicitConv(nkHiddenStdConv, formal.typ,
container, m, c))
else:

View file

@ -417,6 +417,15 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
if skipTypes(formal, abstractInst).kind == tyVar: result = paVarAsgn
else: result = paFastAsgn
proc findWrongOwners(c: PTransf, n: PNode) =
if n.kind == nkVarSection:
let x = n.sons[0].sons[0]
if x.kind == nkSym and x.sym.owner != getCurrOwner(c):
internalError(x.info, "bah " & x.sym.name.s & " " &
x.sym.owner.name.s & " " & getCurrOwner(c).name.s)
else:
for i in 0 .. <safeLen(n): findWrongOwners(c, n.sons[i])
proc transformFor(c: PTransf, n: PNode): PTransNode =
# generate access statements for the parameters (unless they are constant)
# put mapping from formal parameters to actual parameters
@ -468,6 +477,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
pushInfoContext(n.info)
inc(c.inlining)
add(result, transform(c, body))
#findWrongOwners(c, result.pnode)
dec(c.inlining)
popInfoContext()
popTransCon(c)
@ -504,7 +514,7 @@ proc transformCase(c: PTransf, n: PNode): PTransNode =
result.add(elseBranch)
elif result.Pnode.lastSon.kind != nkElse and not (
skipTypes(n.sons[0].Typ, abstractVarRange).Kind in
{tyInt..tyInt64, tyChar, tyEnum}):
{tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32}):
# fix a stupid code gen bug by normalizing:
var elseBranch = newTransNode(nkElse, n.info, 1)
elseBranch[0] = newTransNode(nkNilLit, n.info, 0)
@ -675,12 +685,12 @@ proc transform(c: PTransf, n: PNode): PTransNode =
if cnst != nil and not dontInlineConstant(n, cnst):
result = PTransNode(cnst) # do not miss an optimization
proc processTransf(c: PTransf, n: PNode): PNode =
proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
# Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
# this step! We have to rely that the semantic pass transforms too errornous
# nodes into an empty node.
if passes.skipCodegen(n) or c.fromCache or nfTransf in n.flags: return n
pushTransCon(c, newTransCon(getCurrOwner(c)))
pushTransCon(c, newTransCon(owner))
result = PNode(transform(c, n))
popTransCon(c)
incl(result.flags, nfTransf)
@ -691,35 +701,27 @@ proc openTransf(module: PSym, filename: string): PTransf =
result.breakSyms = @[]
result.module = module
when false:
proc openTransfCached(module: PSym, filename: string,
rd: PRodReader): PPassContext =
result = openTransf(module, filename)
for m in items(rd.methods): methodDef(m, true)
const transfPass* = makePass(openTransf, openTransfCached,
processTransf, processTransf) # we need to process generics too!
proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
if nfTransf in n.flags or prc.kind in {skTemplate, skMacro}:
if nfTransf in n.flags or prc.kind in {skTemplate}:
result = n
else:
when useEffectSystem: trackProc(prc, n)
#when useEffectSystem: trackProc(prc, n)
var c = openTransf(module, "")
result = processTransf(c, n)
result = processTransf(c, n, prc)
if prc.kind != skMacro:
# XXX no closures yet for macros:
result = liftLambdas(prc, result)
if prc.kind == skIterator and prc.typ.callConv == ccClosure:
result = lambdalifting.liftIterator(prc, result)
incl(result.flags, nfTransf)
when useEffectSystem: trackProc(prc, result)
proc transformStmt*(module: PSym, n: PNode): PNode =
if nfTransf in n.flags:
result = n
else:
var c = openTransf(module, "")
result = processTransf(c, n)
result = processTransf(c, n, module)
result = liftLambdasForTopLevel(module, result)
incl(result.flags, nfTransf)
@ -728,5 +730,5 @@ proc transformExpr*(module: PSym, n: PNode): PNode =
result = n
else:
var c = openTransf(module, "")
result = processTransf(c, n)
result = processTransf(c, n, module)
incl(result.flags, nfTransf)

View file

@ -93,8 +93,7 @@ proc getOpSym*(op: PNode): PSym =
proc getMagic*(op: PNode): TMagic =
case op.kind
of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit, nkPrefix, nkPostfix,
nkInfix:
of nkCallKinds:
case op.sons[0].Kind
of nkSym: result = op.sons[0].sym.magic
else: result = mNone

View file

@ -41,7 +41,7 @@ type
wImmediate, wDestructor, wImportCpp, wImportObjC,
wImportCompilerProc,
wImportc, wExportc, wIncompleteStruct,
wImportc, wExportc, wIncompleteStruct, wRequiresInit,
wAlign, wNodecl, wPure, wSideeffect, wHeader,
wNosideeffect, wNoreturn, wMerge, wLib, wDynlib, wCompilerproc, wProcVar,
wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
@ -62,7 +62,7 @@ type
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
wNoStackFrame,
wImplicitStatic, wGlobal, wHoist,
wImplicitStatic, wGlobal, wCodegenDecl,
wAuto, wBool, wCatch, wChar, wClass,
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
@ -122,7 +122,7 @@ const
"immediate", "destructor", "importcpp", "importobjc",
"importcompilerproc", "importc", "exportc", "incompletestruct",
"align", "nodecl", "pure", "sideeffect",
"requiresinit", "align", "nodecl", "pure", "sideeffect",
"header", "nosideeffect", "noreturn", "merge", "lib", "dynlib",
"compilerproc", "procvar", "fatal", "error", "warning", "hint", "line",
"push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace",
@ -142,7 +142,7 @@ const
"watchpoint",
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
"nostackframe", "implicitstatic", "global", "hoist",
"nostackframe", "implicitstatic", "global", "codegendecl",
"auto", "bool", "catch", "char", "class",
"const_cast", "default", "delete", "double",

View file

@ -93,10 +93,10 @@ hint[LineTooLong]=off
cc = clang
tlsEmulation:on
gcc.options.always = "-w -fasm-blocks"
gpp.options.always = "-w -fasm-blocks"
gpp.options.always = "-w -fasm-blocks -fpermissive"
@else:
gcc.options.always = "-w"
gpp.options.always = "-w"
gpp.options.always = "-w -fpermissive"
@end
gcc.options.speed = "-O3 -fno-strict-aliasing"

View file

@ -12,11 +12,17 @@ Advanced commands:
//check checks the project for syntax and semantic
//idetools compiler support for IDEs: possible options:
--track:FILE,LINE,COL track a file/cursor position
--trackDirty:DIRTY_FILE,ORIG_FILE,LINE,COL
track a file, currently not saved to disk
--suggest suggest all possible symbols at position
--def list all possible definitions at position
--context list possible invokation context
--usages list all usages of the symbol at position
--eval evaluates an expression
//serve start the compiler as a service mode (CAAS)
--server.type:TYPE either stdin or tcp
--server.port:PORT port for tcp mode, by default 6000
--server.address:HOST binds to that address, by default ""
Advanced options:
-m, --mainmodule:FILE set the project main module

View file

@ -186,4 +186,4 @@ complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
| bindStmt | mixinStmt)
/ simpleStmt
stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED)
/ simpleStmt
/ simpleStmt ^+ ';'

467
doc/idetools.txt Normal file
View file

@ -0,0 +1,467 @@
================================
Nimrod IDE Integration Guide
================================
:Author: Britney Spears
:Version: |nimrodversion|
.. contents::
Nimrod differs from many other compilers in that it is really fast,
and being so fast makes it suited to provide external queries for
text editors about the source code being written. Through the
``idetools`` command of `the compiler <nimrodc.html>`_, any IDE can
query a ``.nim`` source file and obtain useful information like
definition of symbols or suggestions for completion.
This document will guide you through the available options. If you
want to look at practical examples of idetools support you can look
at the test files found in ``tests/caas/*.txt`` or `various editor
integrations <https://github.com/Araq/Nimrod/wiki/Editor-Support>`_
already available.
Idetools invokation
===================
Specifying the location of the query
------------------------------------
All of the available idetools commands require you to specify a
query location through the ``--track`` or ``--trackDirty`` switches.
The general idetools invokations are::
nimrod idetools --track:FILE,LINE,COL <switches> proj.nim
Or::
nimrod idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL <switches> proj.nim
``proj.nim``
This is the main *project* filename. Most of the time you will
pass in the same as **FILE**, but for bigger projects this is
the file which is used as main entry point for the program, the
one which users compile to generate a final binary.
``<switches>``
This would be any of the other idetools available options, like
``--def`` or ``--suggest`` explained in the following sections.
``COL``
An integer with the column you are going to query. For the
compiler columns start at zero, so the first column will be
**0** and the last in an 80 column terminal will be **79**.
``LINE``
An integer with the line you are going to query. For the compiler
lines start at **1**.
``FILE``
The file you want to perform the query on. Usually you will
pass in the same value as **proj.nim**.
``DIRTY_FILE``
The **FILE** paramater is enough for static analysis, but IDEs
tend to have *unsaved buffers* where the user may still be in
the middle of typing a line. In such situations the IDE can
save the current contents to a temporary file and then use the
``--trackDirty`` switch.
Dirty files are likely to contain errors and they are usually
compiled partially only to the point needed to service the
idetool request. The compiler discriminates them to ensure that
**a)** they won't be cached and **b)** they won't invalidate
the cached contents of the original module.
The other reason is that the dirty file can appear anywhere on
disk (e.g. in tmpfs), but it must be treated as having a path
matching the original module when it comes to usage of relative
paths, etc. Queries, however, will refer to the dirty module
name in their answers instead of the normal filename.
Definitions
-----------
The ``--def`` idetools switch performs a query about the definition
of a specific symbol. If available, idetools will answer with the
type, source file, line/column information and other accessory data
if available like a docstring. With this information an IDE can
provide the typical *Jump to definition* where a user puts the
cursor on a symbol or uses the mouse to select it and is redirected
to the place where the symbol is located.
Since Nimrod is implemented in Nimrod, one of the nice things of
this feature is that any user with an IDE supporting it can quickly
jump around the standard library implementation and see exactly
what a proc does, learning about the language and seeing real life
examples of how to write/implement specific features.
Idetools will always answer with a single definition or none if it
can't find any valid symbol matching the position of the query.
Suggestions
-----------
The ``--suggest`` idetools switch performs a query about possible
completion symbols at some point in the file. IDEs can easily provide
an autocompletion feature where the IDE scans the current file (and
related ones, if it knows about the language being edited and follows
includes/imports) and when the user starts typing something a
completion box with different options appears.
However such features are not context sensitive and work simply on
string matching, which can be problematic in Nimrod especially due
to the case insensitiveness of the language (plus underscores as
separators!).
The typical usage scenario for this option is to call it after the
user has typed the dot character for `the object oriented call
syntax <tut2.html#method-call-syntax>`_.
Invokation context
------------------
The ``--context`` idetools switch is very similar to the suggestions
switch, but instead of being used after the user has typed a dot
character, this one is meant to be used after the user has typed
an opening brace to start typing parameters.
Symbol usages
-------------
The ``--usages`` idetools switch lists all usages of the symbol at
a position. IDEs can use this to find all the places in the file
where the symbol is used and offer the user to rename it in all
places at the same time. Again, a pure string based search and
replace may catch symbols out of the scope of a funcion/loop.
For this kind of query the IDE will most likely ignore all the
type/signature info provided by idetools and concentrate on the
filename, line and column position of the multiple returned answers.
Expression evaluation
---------------------
This feature is still under development. In the future it will allow
an IDE to evaluate an expression in the context of the currently
running/debugged user project.
Compiler as a service (CAAS)
============================
The ocasional use of idetools is acceptable for things like
definitions, where the user puts the cursor on a symbol or double
clicks it and after a second or two the IDE displays where that
symbol is defined. Such latencies would be terrible for features
like symbol suggestion, plus why wait at all if we can avoid it?
The idetools command can be run as a compiler service, where you
first launch the compiler and it will stay online as a server,
accepting queries in a telnet like fashion. The advantage of staying
on is that for many queries the compiler can cache the results of
the compilation, and subsequent queries should be fast in the
millisecond range, thus being responsive enough for IDEs.
If you want to start the server using stdin/stdout as communication
you need to type::
nimrod serve --server.type:stdin proj.nim
If you want to start the server using tcp and a port, you need to type::
nimrod serve --server.type:tcp --server.port:6000 \
--server.address:hostname proj.nim
In both cases the server will start up and await further commands.
The syntax of the commands you can now send to the server is
practically the same as running the nimrod compiler on the commandline,
you only need to remove the name of the compiler since you are
already talking to it. The server will answer with as many lines
of text it thinks necessary plus an empty line to indicate the end
of the answer.
You can find examples of client/server communication in the idetools
tests found in ``tests/caas/*.txt``.
Parsing idetools output
=======================
Idetools outputs is always returned on single lines separated by
tab characters (``\t``). The values of each column are:
1. Three characters indicating the type of returned answer (e.g.
def for definition, ``sug`` for suggestion, etc).
2. Type of the symbol. This can be ``skProc``, ``skLet``, and just
about any of the enums defined in the module ``compiler/ast.nim``.
3. Full qualitifed path of the symbol. If you are querying a symbol
defined in the ``proj.nim`` file, this would have the form
``proj.symbolName``.
4. Type/signature. For variables and enums this will contain the
type of the symbol, for procs, methods and templates this will
contain the full unique signature (e.g. ``proc (TFile)``).
5. Full path to the file containing the symbol.
6. Line where the symbol is located in the file. Lines start to
count at **1**.
7. Column where the symbol is located in the file. Columns start
to count at **0**.
8. Docstring for the symbol if available or the empty string. To
differentiate the docstring from end of answer in server mode,
the docstring is always provided enclosed in double quotes, and
if the docstring spans multiple lines, all following lines of the
docstring will start with a blank space to align visually with
the starting quote.
Also, you won't find raw ``\n`` characters breaking the one
answer per line format. Instead you will need to parse sequences
in the form ``\xHH``, where *HH* is a hexadecimal value (e.g.
newlines generate the sequence ``\x0A``).
The following sections define the expected output for each kind of
symbol for which idetools returns valid output.
skConst
-------
| **Third column**: module + [n scope nesting] + const name.
| **Fourth column**: the type of the const value.
| **Docstring**: always the empty string.
.. code-block:: nimrod
const SOME_SEQUENCE = @[1, 2]
--> col 2: $MODULE.SOME_SEQUENCE
col 3: seq[int]
col 7: ""
skEnumField
-----------
| **Third column**: module + [n scope nesting] + enum type + enum field name.
| **Fourth column**: enum type grouping other enum fields.
| **Docstring**: always the empty string.
.. code-block:: nimrod
Open(filename, fmWrite)
--> col 2: system.TFileMode.fmWrite
col 3: TFileMode
col 7: ""
skForVar
--------
| **Third column**: module + [n scope nesting] + var name.
| **Fourth column**: type of the var.
| **Docstring**: always the empty string.
.. code-block:: nimrod
proc looper(filename = "tests.nim") =
for letter in filename:
echo letter
--> col 2: $MODULE.looper.letter
col 3: char
col 7: ""
skIterator
----------
The fourth column will be the empty string if the iterator is being
defined, since at that point in the file the parser hasn't processed
the full line yet. The signature will be returned complete in
posterior instances of the iterator.
| **Third column**: module + [n scope nesting] + iterator name.
| **Fourth column**: signature of the iterator including return type.
| **Docstring**: docstring if available.
.. code-block:: nimrod
let
text = "some text"
letters = toSeq(runes(text))
--> col 2: unicode.runes
col 3: iterator (string): TRune
col 7: "iterates over any unicode character of the string `s`."
skLet
-----
| **Third column**: module + [n scope nesting] + let name.
| **Fourth column**: the type of the let variable.
| **Docstring**: always the empty string.
.. code-block:: nimrod
let
text = "some text"
--> col 2: $MODULE.text
col 3: TaintedString
col 7: ""
skMethod
--------
The fourth column will be the empty string if the method is being
defined, since at that point in the file the parser hasn't processed
the full line yet. The signature will be returned complete in
posterior instances of the method.
Methods imply `dynamic dispatch <tut2.html#dynamic-dispatch>`_ and
idetools performs a static analysis on the code. For this reason
idetools may not return the definition of the correct method you
are querying because it may be impossible to know until the code
is executed. It will try to return the method which covers the most
possible cases (i.e. for variations of different classes in a
hierarchy it will prefer methods using the base class).
While at the language level a method is differentiated from others
by the parameters and return value, the signature of the method
returned by idetools returns also the pragmas for the method.
Note that at the moment the word ``proc`` is returned for the
signature of the found method instead of the expected ``method``.
This may change in the future.
| **Third column**: module + [n scope nesting] + method name.
| **Fourth column**: signature of the method including return type.
| **Docstring**: docstring if available.
.. code-block:: nimrod
method eval(e: PExpr): int = quit "to override!"
method eval(e: PLiteral): int = e.x
method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b)
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
--> col 2: $MODULE.eval
col 3: proc (PPlusExpr): int
col 7: ""
skParam
-------
| **Third column**: module + [n scope nesting] + param name.
| **Fourth column**: the type of the parameter.
| **Docstring**: always the empty string.
.. code-block:: nimrod
proc reader(filename = "tests.nim") =
let text = readFile(filename)
--> col 2: $MODULE.reader.filename
col 3: string
col 7: ""
skProc
------
The fourth column will be the empty string if the proc is being
defined, since at that point in the file the parser hasn't processed
the full line yet. The signature will be returned complete in
posterior instances of the proc.
While at the language level a proc is differentiated from others
by the parameters and return value, the signature of the proc
returned by idetools returns also the pragmas for the proc.
| **Third column**: module + [n scope nesting] + proc name.
| **Fourth column**: signature of the proc including return type.
| **Docstring**: docstring if available.
.. code-block:: nimrod
Open(filename, fmWrite)
--> col 2: system.Open
col 3: proc (var TFile, string, TFileMode, int): bool
col 7:
"Opens a file named `filename` with given `mode`.
Default mode is readonly. Returns true iff the file could be opened.
This throws no exception if the file could not be opened."
skResult
--------
| **Third column**: module + [n scope nesting] + result.
| **Fourth column**: the type of the result.
| **Docstring**: always the empty string.
.. code-block:: nimrod
proc getRandomValue() : int =
return 4
--> col 2: $MODULE.getRandomValue.result
col 3: int
col 7: ""
skTemplate
----------
The fourth column will be the empty string if the template is being
defined, since at that point in the file the parser hasn't processed
the full line yet. The signature will be returned complete in
posterior instances of the template.
| **Third column**: module + [n scope nesting] + template name.
| **Fourth column**: signature of the template including return type.
| **Docstring**: docstring if available.
.. code-block:: nimrod
let
text = "some text"
letters = toSeq(runes(text))
--> col 2: sequtils.toSeq
col 3: proc (expr): expr
col 7:
"Transforms any iterator into a sequence.
Example:
.. code-block:: nimrod
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]"
skType
------
| **Third column**: module + [n scope nesting] + type name.
| **Fourth column**: the type.
| **Docstring**: always the empty string.
.. code-block:: nimrod
proc writeTempFile() =
var output: TFile
--> col 2: system.TFile
col 3: TFile
col 7: ""
skVar
-----
| **Third column**: module + [n scope nesting] + var name.
| **Fourth column**: the type of the var.
| **Docstring**: always the empty string.
.. code-block:: nimrod
proc writeTempFile() =
var output: TFile
output.open("/tmp/somefile", fmWrite)
output.write("test")
--> col 2: $MODULE.writeTempFile.output
col 3: TFile
col 7: ""

View file

@ -255,6 +255,22 @@ Parsers
This is a low level module that implements an extremely efficient buffering
scheme for lexers and parsers. This is used by the diverse parsing modules.
* `highlite <highlite.html>`_
Source highlighter for programming or markup languages. Currently
only few languages are supported, other languages may be added.
The interface supports one language nested in another.
* `rst <rst.html>`_
This module implements a reStructuredText parser. A large subset
is implemented. Some features of the markdown wiki syntax are
also supported.
* `rstast <rstast.html>`_
This module implements an AST for the reStructuredText parser.
* `rstgen <rstgen.html>`_
This module implements a generator of HTML/Latex from reStructuredText.
XML Processing
--------------

View file

@ -327,36 +327,36 @@ Numerical constants
`Numerical constants`:idx: are of a single type and have the form::
hexdigit ::= digit | 'A'..'F' | 'a'..'f'
octdigit ::= '0'..'7'
bindigit ::= '0'..'1'
HEX_LIT ::= '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
DEC_LIT ::= digit ( ['_'] digit )*
OCT_LIT ::= '0o' octdigit ( ['_'] octdigit )*
BIN_LIT ::= '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
hexdigit = digit | 'A'..'F' | 'a'..'f'
octdigit = '0'..'7'
bindigit = '0'..'1'
HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
DEC_LIT = digit ( ['_'] digit )*
OCT_LIT = '0o' octdigit ( ['_'] octdigit )*
BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
INT_LIT ::= HEX_LIT
| DEC_LIT
| OCT_LIT
| BIN_LIT
INT_LIT = HEX_LIT
| DEC_LIT
| OCT_LIT
| BIN_LIT
INT8_LIT ::= INT_LIT ['\''] ('i' | 'I') '8'
INT16_LIT ::= INT_LIT ['\''] ('i' | 'I') '16'
INT32_LIT ::= INT_LIT ['\''] ('i' | 'I') '32'
INT64_LIT ::= INT_LIT ['\''] ('i' | 'I') '64'
INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8'
INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16'
INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32'
INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64'
UINT8_LIT ::= INT_LIT ['\''] ('u' | 'U')
UINT8_LIT ::= INT_LIT ['\''] ('u' | 'U') '8'
UINT16_LIT ::= INT_LIT ['\''] ('u' | 'U') '16'
UINT32_LIT ::= INT_LIT ['\''] ('u' | 'U') '32'
UINT64_LIT ::= INT_LIT ['\''] ('u' | 'U') '64'
UINT8_LIT = INT_LIT ['\''] ('u' | 'U')
UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8'
UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16'
UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32'
UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64'
exponent ::= ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
FLOAT_LIT ::= digit (['_'] digit)* ('.' (['_'] digit)* [exponent] |exponent)
FLOAT32_LIT ::= HEX_LIT '\'' ('f'|'F') '32'
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '32'
FLOAT64_LIT ::= HEX_LIT '\'' ('f'|'F') '64'
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '64'
exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
FLOAT_LIT = digit (['_'] digit)* (('.' (['_'] digit)* [exponent]) |exponent)
FLOAT32_LIT = HEX_LIT '\'' ('f'|'F') '32'
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '32'
FLOAT64_LIT = HEX_LIT '\'' ('f'|'F') '64'
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '64'
As can be seen in the productions, numerical constants can contain underscores
@ -1388,7 +1388,7 @@ accesses its environment. If it does so, it has the calling convention
Distinct type
-------------
A distinct type is new type derived from a `base type`:idx: that is
A `distinct type`:idx: is new type derived from a `base type`:idx: that is
incompatible with its base type. In particular, it is an essential property
of a distinct type that it **does not** imply a subtype relation between it
and its base type. Explicit type conversions from a distinct type to its
@ -1435,7 +1435,7 @@ number without unit; and the same holds for division:
This quickly gets tedious. The implementations are trivial and the compiler
should not generate all this code only to optimize it away later - after all
``+`` for dollars should produce the same binary code as ``+`` for ints.
The pragma ``borrow`` has been designed to solve this problem; in principle
The pragma `borrow`:idx: has been designed to solve this problem; in principle
it generates the above trivial implementations:
.. code-block:: nimrod
@ -1818,6 +1818,24 @@ If a proc is annotated with the ``noinit`` pragma this refers to its implicit
proc returnUndefinedValue: int {.noinit.} = nil
The implicit initialization can be also prevented by the `requiresInit`:idx:
type pragma. The compiler requires an explicit initialization then. However
it does a `control flow analysis`:idx: to prove the variable has been
initialized and does not rely on syntactic properties:
.. code-block:: nimrod
type
TMyObject = object {.requiresInit.}
proc p() =
# the following is valid:
var x: TMyObject
if someCondition():
x = a()
else:
x = a()
use x
let statement
-------------
@ -1920,8 +1938,6 @@ the corresponding *then* block:
In the example the scopes have been enclosed in ``{| |}``.
**Note**: These scoping rules will be active in 0.9.4.
Case statement
--------------

View file

@ -20,7 +20,7 @@ on the different supported platforms. It is not a definition of the Nimrod
programming language (therefore is the `manual <manual.html>`_).
Nimrod is free software; it is licensed under the
`GNU General Public License <gpl.html>`_.
`MIT License <http://www.opensource.org/licenses/mit-license.php>`_.
Compiler Usage
@ -401,6 +401,21 @@ this to work. The conditional symbol ``objc`` is defined when the compiler
emits Objective C code.
CodegenDecl pragma
------------------
The `codegenDecl`:idx: pragma can be used to directly influence Nimrod's code
generator. It receives a format string that determines how the variable or
proc is declared in the generated code:
.. code-block:: nimrod
var
a {.codegenDecl: "$# progmem $#".}: int
proc myinterrupt() {.codegenDecl: "__interrupt $# $#$#".} =
echo "realistic interrupt handler"
LineDir option
--------------
The `lineDir`:idx: option can be turned on or off. If turned on the
@ -443,6 +458,14 @@ in C/C++).
**Note**: This pragma will not exist for the LLVM backend.
Nimrod idetools integration
=======================
Nimrod provides language integration with external IDEs through the
idetools command. See the documentation of `idetools <idetools.html>`_
for further information.
Nimrod interactive mode
=======================

View file

@ -1182,6 +1182,21 @@ type and instead write it embedded directly as the type of the first dimension:
type
TLightTower = array[1..10, array[north..west, TBlinkLights]]
It is quite frequent to have arrays start at zero, so there's a shortcut syntax
to specify a range from zero to the specified index minus one:
.. code-block:: nimrod
type
TIntArray = array[0..5, int] # an array that is indexed with 0..5
TQuickArray = array[6, int] # an array that is indexed with 0..5
var
x: TIntArray
y: TQuickArray
x = [1, 2, 3, 4, 5, 6]
y = x
for i in low(x)..high(x):
echo(x[i], y[i])
Sequences
---------

View file

@ -64,13 +64,16 @@ proc tryExec(cmd: string): bool =
echo(cmd)
result = execShellCmd(cmd) == 0
const
compileNimInst = "-d:useLibzipSrc tools/niminst/niminst"
proc csource(args: string) =
exec("nimrod cc $1 -r tools/niminst/niminst --var:version=$2 csource compiler/nimrod.ini $1" %
[args, NimrodVersion])
exec("nimrod cc $1 -r $3 --var:version=$2 csource compiler/nimrod.ini $1" %
[args, NimrodVersion, compileNimInst])
proc zip(args: string) =
exec("nimrod cc -r tools/niminst/niminst --var:version=$# zip compiler/nimrod.ini" %
NimrodVersion)
exec("nimrod cc -r $2 --var:version=$1 zip compiler/nimrod.ini" %
[NimrodVersion, compileNimInst])
proc buildTool(toolname, args: string) =
exec("nimrod cc $# $#" % [args, toolname])
@ -85,8 +88,8 @@ proc inno(args: string) =
NimrodVersion)
proc install(args: string) =
exec("nimrod cc -r tools/niminst/niminst --var:version=$# scripts compiler/nimrod.ini" %
NimrodVersion)
exec("nimrod cc -r $# --var:version=$# scripts compiler/nimrod.ini" %
[compileNimInst, NimrodVersion])
exec("sh ./install.sh $#" % args)
proc web(args: string) =

View file

@ -1,24 +0,0 @@
===============================================================================
Nimrod -- a Compiler for Nimrod. http://nimrod-code.org/
Copyright (C) 2004-2013 Andreas Rumpf. All rights reserved.
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.
[ MIT license: http://www.opensource.org/licenses/mit-license.php ]

View file

@ -390,7 +390,7 @@ proc lispRepr*(n: PNimrodNode): string {.compileTime.} =
add(result, ")")
macro dumpTree*(s: stmt): stmt = echo s.treeRepr
macro dumpTree*(s: stmt): stmt {.immediate.} = echo s.treeRepr
## Accepts a block of nimrod code and prints the parsed abstract syntax
## tree using the `toTree` function. Printing is done *at compile time*.
##
@ -398,16 +398,16 @@ macro dumpTree*(s: stmt): stmt = echo s.treeRepr
## tree and to discover what kind of nodes must be created to represent
## a certain expression/statement.
macro dumpLisp*(s: stmt): stmt = echo s.lispRepr
macro dumpLisp*(s: stmt): stmt {.immediate.} = echo s.lispRepr
## Accepts a block of nimrod code and prints the parsed abstract syntax
## tree using the `toLisp` function. Printing is done *at compile time*.
##
## See `dumpTree`.
macro dumpTreeImm*(s: stmt): stmt {.immediate.} = echo s.treeRepr
macro dumpTreeImm*(s: stmt): stmt {.immediate, deprecated.} = echo s.treeRepr
## The ``immediate`` version of `dumpTree`.
macro dumpLispImm*(s: stmt): stmt {.immediate.} = echo s.lispRepr
macro dumpLispImm*(s: stmt): stmt {.immediate, deprecated.} = echo s.lispRepr
## The ``immediate`` version of `dumpLisp`.

View file

@ -379,7 +379,7 @@ proc getBiggestInt*(x: TAny): biggestInt =
of tyInt8: result = biggestInt(cast[ptr int8](x.value)[])
of tyInt16: result = biggestInt(cast[ptr int16](x.value)[])
of tyInt32: result = biggestInt(cast[ptr int32](x.value)[])
of tyInt64: result = biggestInt(cast[ptr int64](x.value)[])
of tyInt64, tyUInt64: result = biggestInt(cast[ptr int64](x.value)[])
of tyBool: result = biggestInt(cast[ptr bool](x.value)[])
of tyChar: result = biggestInt(cast[ptr char](x.value)[])
of tyEnum, tySet:
@ -389,6 +389,10 @@ proc getBiggestInt*(x: TAny): biggestInt =
of 4: result = biggestInt(cast[ptr int32](x.value)[])
of 8: result = biggestInt(cast[ptr int64](x.value)[])
else: assert false
of tyUInt: result = biggestInt(cast[ptr uint](x.value)[])
of tyUInt8: result = biggestInt(cast[ptr uint8](x.value)[])
of tyUInt16: result = biggestInt(cast[ptr uint16](x.value)[])
of tyUInt32: result = biggestInt(cast[ptr uint32](x.value)[])
else: assert false
proc setBiggestInt*(x: TAny, y: biggestInt) =
@ -400,7 +404,7 @@ proc setBiggestInt*(x: TAny, y: biggestInt) =
of tyInt8: cast[ptr int8](x.value)[] = int8(y)
of tyInt16: cast[ptr int16](x.value)[] = int16(y)
of tyInt32: cast[ptr int32](x.value)[] = int32(y)
of tyInt64: cast[ptr int64](x.value)[] = int64(y)
of tyInt64, tyUInt64: cast[ptr int64](x.value)[] = int64(y)
of tyBool: cast[ptr bool](x.value)[] = y != 0
of tyChar: cast[ptr char](x.value)[] = chr(y.int)
of tyEnum, tySet:
@ -410,6 +414,10 @@ proc setBiggestInt*(x: TAny, y: biggestInt) =
of 4: cast[ptr int32](x.value)[] = int32(y)
of 8: cast[ptr int64](x.value)[] = y
else: assert false
of tyUInt: cast[ptr uint](x.value)[] = uint(y)
of tyUInt8: cast[ptr uint8](x.value)[] = uint8(y)
of tyUInt16: cast[ptr uint16](x.value)[] = uint16(y)
of tyUInt32: cast[ptr uint32](x.value)[] = uint32(y)
else: assert false
proc getChar*(x: TAny): char =

View file

@ -242,7 +242,6 @@ template `=~` *(s: string, pattern: TRegEx): expr =
bind maxSubPatterns
when not definedInScope(matches):
var matches {.inject.}: array[0..maxSubPatterns-1, string]
{.warning: "injected 'matches' might be affected by new scoping rules in 0.9.4".}
match(s, pattern, matches)
# ------------------------- more string handling ------------------------------

View file

@ -443,11 +443,11 @@ struct TFrame {
};
#define nimfr(proc, file) \
volatile TFrame F; \
TFrame F; \
F.procname = proc; F.filename = file; F.line = 0; F.len = 0; nimFrame(&F);
#define nimfrs(proc, file, slots, length) \
volatile struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; TVarSlot s[slots];} F; \
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 = length; nimFrame((TFrame*)&F);
#define nimln(n, file) \

View file

@ -44,7 +44,17 @@ const
"Assembler", "Preprocessor", "Directive", "Command", "Rule", "Hyperlink",
"Label", "Reference", "Other"]
nimrodKeywords = slurp("doc/keywords.txt").split
# The following list comes from doc/keywords.txt, make sure it is
# synchronized with this array by running the module itself as a test case.
nimrodKeywords = ["addr", "and", "as", "asm", "atomic", "bind", "block",
"break", "case", "cast", "const", "continue", "converter", "discard",
"distinct", "div", "do", "elif", "else", "end", "enum", "except", "export",
"finally", "for", "from", "generic", "if", "import", "in", "include",
"interface", "is", "isnot", "iterator", "lambda", "let", "macro", "method",
"mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc",
"ptr", "raise", "ref", "return", "shared", "shl", "shr", "static",
"template", "try", "tuple", "type", "var", "when", "while", "with",
"without", "xor", "yield"]
proc getSourceLanguage*(name: string): TSourceLanguage =
for i in countup(succ(low(TSourceLanguage)), high(TSourceLanguage)):
@ -535,3 +545,16 @@ proc getNextToken*(g: var TGeneralTokenizer, lang: TSourceLanguage) =
of langC: cNextToken(g)
of langJava: javaNextToken(g)
when isMainModule:
var keywords: seq[string]
# Try to work running in both the subdir or at the root.
for filename in ["doc/keywords.txt", "../../../doc/keywords.txt"]:
except: echo filename, " not found"
let input = string(readFile(filename))
keywords = input.split()
break
doAssert (not keywords.isNil, "Couldn't read any keywords.txt file!")
doAssert keywords.len == nimrodKeywords.len, "No matching lengths"
for i in 0..keywords.len-1:
#echo keywords[i], " == ", nimrodKeywords[i]
doAssert keywords[i] == nimrodKeywords[i], "Unexpected keyword"

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
## This module implements a `reStructuredText`:idx parser. A large
## This module implements a `reStructuredText`:idx: parser. A large
## subset is implemented. Some features of the `markdown`:idx: wiki syntax are
## also supported.

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
## This module implements an AST for the `reStructuredText`:idx parser.
## This module implements an AST for the `reStructuredText`:idx: parser.
import strutils

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
## This module implements a generator of HTML/Latex from `reStructuredText`:idx.
## This module implements a generator of HTML/Latex from `reStructuredText`:idx:.
import strutils, os, hashes, strtabs, rstast, rst, highlite

View file

@ -167,7 +167,7 @@ proc AsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
result = newAsyncSocket()
result.socket = socket(domain, typ, protocol, buffered)
result.proto = protocol
if result.socket == InvalidSocket: OSError()
if result.socket == InvalidSocket: OSError(OSLastError())
result.socket.setBlocking(false)
proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket =
@ -349,7 +349,7 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket,
client.sslNeedAccept = false
client.info = SockConnected
if c == InvalidSocket: OSError()
if c == InvalidSocket: SocketError(server.socket)
c.setBlocking(false) # TODO: Needs to be tested.
# deleg.open is set in ``toDelegate``.
@ -423,6 +423,10 @@ proc isConnecting*(s: PAsyncSocket): bool =
proc isClosed*(s: PAsyncSocket): bool =
## Determines whether ``s`` has been closed.
return s.info == SockClosed
proc isSendDataBuffered*(s: PAsyncSocket): bool =
## Determines whether ``s`` has data waiting to be sent, i.e. whether this
## socket's sendBuffer contains data.
return s.sendBuffer.len != 0
proc setHandleWrite*(s: PAsyncSocket,
handleWrite: proc (s: PAsyncSocket) {.closure.}) =
@ -638,8 +642,7 @@ when isMainModule:
proc testRead(s: PAsyncSocket, no: int) =
echo("Reading! " & $no)
var data = ""
if not s.readLine(data):
OSError()
if not s.readLine(data): return
if data == "":
echo("Closing connection. " & $no)
s.close()

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -35,8 +35,12 @@ type
tagAddress, ## the HTML ``address`` element
tagApplet, ## the deprecated HTML ``applet`` element
tagArea, ## the HTML ``area`` element
tagArticle, ## the HTML ``article`` element
tagAside, ## the HTML ``aside`` element
tagAudio, ## the HTML ``audio`` element
tagB, ## the HTML ``b`` element
tagBase, ## the HTML ``base`` element
tagBdi, ## the HTML ``bdi`` element
tagBdo, ## the deprecated HTML ``dbo`` element
tagBasefont, ## the deprecated HTML ``basefont`` element
tagBig, ## the HTML ``big`` element
@ -44,22 +48,31 @@ type
tagBody, ## the HTML ``body`` element
tagBr, ## the HTML ``br`` element
tagButton, ## the HTML ``button`` element
tagCanvas, ## the HTML ``canvas`` element
tagCaption, ## the HTML ``caption`` element
tagCenter, ## the deprecated HTML ``center`` element
tagCite, ## the HTML ``cite`` element
tagCode, ## the HTML ``code`` element
tagCol, ## the HTML ``col`` element
tagColgroup, ## the HTML ``colgroup`` element
tagCommand, ## the HTML ``command`` element
tagDatalist, ## the HTML ``datalist`` element
tagDd, ## the HTML ``dd`` element
tagDel, ## the HTML ``del`` element
tagDetails, ## the HTML ``details`` element
tagDfn, ## the HTML ``dfn`` element
tagDialog, ## the HTML ``dialog`` element
tagDiv, ## the HTML ``div`` element
tagDir, ## the deprecated HTLM ``dir`` element
tagDl, ## the HTML ``dl`` element
tagDt, ## the HTML ``dt`` element
tagEm, ## the HTML ``em`` element
tagEmbed, ## the HTML ``embed`` element
tagFieldset, ## the HTML ``fieldset`` element
tagFigcaption, ## the HTML ``figcaption`` element
tagFigure, ## the HTML ``figure`` element
tagFont, ## the deprecated HTML ``font`` element
tagFooter, ## the HTML ``footer`` element
tagForm, ## the HTML ``form`` element
tagFrame, ## the HTML ``frame`` element
tagFrameset, ## the deprecated HTML ``frameset`` element
@ -70,6 +83,8 @@ type
tagH5, ## the HTML ``h5`` element
tagH6, ## the HTML ``h6`` element
tagHead, ## the HTML ``head`` element
tagHeader, ## the HTML ``header`` element
tagHgroup, ## the HTML ``hgroup`` element
tagHtml, ## the HTML ``html`` element
tagHr, ## the HTML ``hr`` element
tagI, ## the HTML ``i`` element
@ -79,13 +94,17 @@ type
tagIns, ## the HTML ``ins`` element
tagIsindex, ## the deprecated HTML ``isindex`` element
tagKbd, ## the HTML ``kbd`` element
tagKeygen, ## the HTML ``keygen`` element
tagLabel, ## the HTML ``label`` element
tagLegend, ## the HTML ``legend`` element
tagLi, ## the HTML ``li`` element
tagLink, ## the HTML ``link`` element
tagMap, ## the HTML ``map`` element
tagMark, ## the HTML ``mark`` element
tagMenu, ## the deprecated HTML ``menu`` element
tagMeta, ## the HTML ``meta`` element
tagMeter, ## the HTML ``meter`` element
tagNav, ## the HTML ``nav`` element
tagNobr, ## the deprecated HTML ``nobr`` element
tagNoframes, ## the deprecated HTML ``noframes`` element
tagNoscript, ## the HTML ``noscript`` element
@ -93,20 +112,28 @@ type
tagOl, ## the HTML ``ol`` element
tagOptgroup, ## the HTML ``optgroup`` element
tagOption, ## the HTML ``option`` element
tagOutput, ## the HTML ``output`` element
tagP, ## the HTML ``p`` element
tagParam, ## the HTML ``param`` element
tagPre, ## the HTML ``pre`` element
tagProgress, ## the HTML ``progress`` element
tagQ, ## the HTML ``q`` element
tagRp, ## the HTML ``rp`` element
tagRt, ## the HTML ``rt`` element
tagRuby, ## the HTML ``ruby`` element
tagS, ## the deprecated HTML ``s`` element
tagSamp, ## the HTML ``samp`` element
tagScript, ## the HTML ``script`` element
tagSection, ## the HTML ``section`` element
tagSelect, ## the HTML ``select`` element
tagSmall, ## the HTML ``small`` element
tagSource, ## the HTML ``source`` element
tagSpan, ## the HTML ``span`` element
tagStrike, ## the deprecated HTML ``strike`` element
tagStrong, ## the HTML ``strong`` element
tagStyle, ## the HTML ``style`` element
tagSub, ## the HTML ``sub`` element
tagSummary, ## the HTML ``summary`` element
tagSup, ## the HTML ``sup`` element
tagTable, ## the HTML ``table`` element
tagTbody, ## the HTML ``tbody`` element
@ -115,37 +142,46 @@ type
tagTfoot, ## the HTML ``tfoot`` element
tagTh, ## the HTML ``th`` element
tagThead, ## the HTML ``thead`` element
tagTime, ## the HTML ``time`` element
tagTitle, ## the HTML ``title`` element
tagTr, ## the HTML ``tr`` element
tagTrack, ## the HTML ``track`` element
tagTt, ## the HTML ``tt`` element
tagU, ## the deprecated HTML ``u`` element
tagUl, ## the HTML ``ul`` element
tagVar ## the HTML ``var`` element
tagVar, ## the HTML ``var`` element
tagVideo, ## the HTML ``video`` element
tagWbr ## the HTML ``wbr`` element
const
tagToStr* = [
"a", "abbr", "acronym", "address", "applet", "area",
"b", "base", "basefont", "bdo", "big", "blockquote", "body",
"br", "button", "caption", "center", "cite", "code",
"col", "colgroup", "dd", "del", "dfn", "div",
"dir", "dl", "dt", "em", "fieldset", "font",
"a", "abbr", "acronym", "address", "applet", "area", "article",
"aside", "audio",
"b", "base", "basefont", "bdi", "bdo", "big", "blockquote", "body",
"br", "button", "canvas", "caption", "center", "cite", "code",
"col", "colgroup", "command",
"datalist", "dd", "del", "details", "dfn", "dialog", "div",
"dir", "dl", "dt", "em", "embed", "fieldset",
"figcaption", "figure", "font", "footer",
"form", "frame", "frameset", "h1", "h2", "h3",
"h4", "h5", "h6", "head", "html", "hr",
"h4", "h5", "h6", "head", "header", "hgroup", "html", "hr",
"i", "iframe", "img", "input", "ins", "isindex",
"kbd", "label", "legend", "li", "link", "map",
"menu", "meta", "nobr", "noframes", "noscript", "object", "ol",
"optgroup", "option", "p", "param", "pre", "q",
"s", "samp", "script", "select", "small", "span",
"strike", "strong", "style", "sub", "sup", "table",
"tbody", "td", "textarea", "tfoot", "th", "thead",
"title", "tr", "tt", "u", "ul", "var"]
"kbd", "keygen", "label", "legend", "li", "link", "map", "mark",
"menu", "meta", "meter", "nav", "nobr", "noframes", "noscript",
"object", "ol",
"optgroup", "option", "output", "p", "param", "pre", "progress", "q",
"rp", "rt", "ruby", "s", "samp", "script", "section", "select", "small",
"source", "span", "strike", "strong", "style",
"sub", "summary", "sup", "table",
"tbody", "td", "textarea", "tfoot", "th", "thead", "time",
"title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"]
InlineTags* = {tagA, tagAbbr, tagAcronym, tagApplet, tagB, tagBasefont,
tagBdo, tagBig, tagBr, tagButton, tagCite, tagCode, tagDel, tagDfn,
tagEm, tagFont, tagI, tagImg, tagIns, tagInput, tagIframe, tagKbd,
tagLabel, tagMap, tagObject, tagQ, tagSamp, tagScript, tagSelect,
tagSmall, tagSpan, tagStrong, tagSub, tagSup, tagTextarea, tagTt,
tagVar, tagApplet, tagBasefont, tagFont, tagIframe, tagU, tagS,
tagStrike}
tagStrike, tagWbr}
BlockTags* = {tagAddress, tagBlockquote, tagCenter, tagDel, tagDir, tagDiv,
tagDl, tagFieldset, tagForm, tagH1, tagH2, tagH3, tagH4,
tagH5, tagH6, tagHr, tagIns, tagIsindex, tagMenu, tagNoframes, tagNoscript,
@ -153,7 +189,7 @@ const
tagMenu, tagNoframes}
SingleTags* = {tagArea, tagBase, tagBasefont,
tagBr, tagCol, tagFrame, tagHr, tagImg, tagIsindex,
tagLink, tagMeta, tagParam}
tagLink, tagMeta, tagParam, tagWbr}
Entities = [
("nbsp", 0x00A0), ("iexcl", 0x00A1), ("cent", 0x00A2), ("pound", 0x00A3),
@ -228,28 +264,149 @@ const
("loz", 0x25CA), ("spades", 0x2660), ("clubs", 0x2663),
("hearts", 0x2665), ("diams", 0x2666)]
proc binaryStrSearch(x: openarray[string], y: string): int =
## XXX put this into the library somewhere!
var a = 0
var b = len(x) - 1
while a <= b:
var mid = (a + b) div 2
var c = cmp(x[mid], y)
if c < 0: a = mid + 1
elif c > 0: b = mid - 1
else: return mid
result = - 1
proc allLower(s: string): bool =
for c in s:
if c < 'a' or c > 'z': return false
return true
proc toHtmlTag(s: string): THtmlTag =
case s
of "a": tagA
of "abbr": tagAbbr
of "acronym": tagAcronym
of "address": tagAddress
of "applet": tagApplet
of "area": tagArea
of "article": tagArticle
of "aside": tagAside
of "audio": tagAudio
of "b": tagB
of "base": tagBase
of "basefont": tagBasefont
of "bdi": tagBdi
of "bdo": tagBdo
of "big": tagBig
of "blockquote": tagBlockquote
of "body": tagBody
of "br": tagBr
of "button": tagButton
of "canvas": tagCanvas
of "caption": tagCaption
of "center": tagCenter
of "cite": tagCite
of "code": tagCode
of "col": tagCol
of "colgroup": tagColgroup
of "command": tagCommand
of "datalist": tagDatalist
of "dd": tagDd
of "del": tagDel
of "details": tagDetails
of "dfn": tagDfn
of "dialog": tagDialog
of "div": tagDiv
of "dir": tagDir
of "dl": tagDl
of "dt": tagDt
of "em": tagEm
of "embed": tagEmbed
of "fieldset": tagFieldset
of "figcaption": tagFigcaption
of "figure": tagFigure
of "font": tagFont
of "footer": tagFooter
of "form": tagForm
of "frame": tagFrame
of "frameset": tagFrameset
of "h1": tagH1
of "h2": tagH2
of "h3": tagH3
of "h4": tagH4
of "h5": tagH5
of "h6": tagH6
of "head": tagHead
of "header": tagHeader
of "hgroup": tagHgroup
of "html": tagHtml
of "hr": tagHr
of "i": tagI
of "iframe": tagIframe
of "img": tagImg
of "input": tagInput
of "ins": tagIns
of "isindex": tagIsindex
of "kbd": tagKbd
of "keygen": tagKeygen
of "label": tagLabel
of "legend": tagLegend
of "li": tagLi
of "link": tagLink
of "map": tagMap
of "mark": tagMark
of "menu": tagMenu
of "meta": tagMeta
of "meter": tagMeter
of "nav": tagNav
of "nobr": tagNobr
of "noframes": tagNoframes
of "noscript": tagNoscript
of "object": tagObject
of "ol": tagOl
of "optgroup": tagOptgroup
of "option": tagOption
of "output": tagOutput
of "p": tagP
of "param": tagParam
of "pre": tagPre
of "progress": tagProgress
of "q": tagQ
of "rp": tagRp
of "rt": tagRt
of "ruby": tagRuby
of "s": tagS
of "samp": tagSamp
of "script": tagScript
of "section": tagSection
of "select": tagSelect
of "small": tagSmall
of "source": tagSource
of "span": tagSpan
of "strike": tagStrike
of "strong": tagStrong
of "style": tagStyle
of "sub": tagSub
of "summary": tagSummary
of "sup": tagSup
of "table": tagTable
of "tbody": tagTbody
of "td": tagTd
of "textarea": tagTextarea
of "tfoot": tagTfoot
of "th": tagTh
of "thead": tagThead
of "time": tagTime
of "title": tagTitle
of "tr": tagTr
of "track": tagTrack
of "tt": tagTt
of "u": tagU
of "ul": tagUl
of "var": tagVar
of "video": tagVideo
of "wbr": tagWbr
else: tagUnknown
proc htmlTag*(n: PXmlNode): THtmlTag =
## gets `n`'s tag as a ``THtmlTag``.
if n.clientData == 0:
n.clientData = binaryStrSearch(tagToStr, n.tag)+1
n.clientData = toHtmlTag(n.tag).ord
result = THtmlTag(n.clientData)
proc htmlTag*(s: string): THtmlTag =
## converts `s` to a ``THtmlTag``. If `s` is no HTML tag, ``tagUnknown`` is
## returned.
result = THtmlTag(binaryStrSearch(tagToStr, s.toLower)+1)
let s = if allLower(s): s else: s.toLower
result = toHtmlTag(s)
proc entityToUtf8*(entity: string): string =
## converts an HTML entity name like ``&Uuml;`` to its UTF-8 equivalent.
@ -267,11 +424,13 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode
proc expected(x: var TXmlParser, n: PXmlNode): string =
result = errorMsg(x, "</" & n.tag & "> expected")
template elemName(x: expr): expr = rawData(x)
proc untilElementEnd(x: var TXmlParser, result: PXmlNode,
errors: var seq[string]) =
# we parsed e.g. ``<br>`` and don't really expect a ``</br>``:
if result.htmlTag in singleTags:
if x.kind != xmlElementEnd or cmpIgnoreCase(x.elementName, result.tag) != 0:
if x.kind != xmlElementEnd or cmpIgnoreCase(x.elemName, result.tag) != 0:
return
while true:
case x.kind
@ -279,28 +438,29 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode,
case result.htmlTag
of tagLi, tagP, tagDt, tagDd, tagInput, tagOption:
# some tags are common to have no ``</end>``, like ``<li>``:
if htmlTag(x.elementName) in {tagLi, tagP, tagDt, tagDd, tagInput,
tagOption}:
if htmlTag(x.elemName) in {tagLi, tagP, tagDt, tagDd, tagInput,
tagOption}:
errors.add(expected(x, result))
break
when false:
if htmlTag(x.elementName) notin InlineTags:
errors.add(expected(x, result))
break
of tagTr, tagTd, tagTh, tagTfoot, tagThead:
if htmlTag(x.elementName) in {tagTr, tagTd, tagTh, tagTfoot, tagThead}:
of tagTd, tagTh, tagTfoot, tagThead:
if htmlTag(x.elemName) in {tagTr, tagTd, tagTh, tagTfoot, tagThead}:
errors.add(expected(x, result))
break
of tagTr:
if htmlTag(x.elemName) == tagTr:
errors.add(expected(x, result))
break
of tagOptgroup:
if htmlTag(x.elementName) in {tagOption, tagOptgroup}:
if htmlTag(x.elemName) in {tagOption, tagOptgroup}:
errors.add(expected(x, result))
break
else: nil
result.addNode(parse(x, errors))
of xmlElementEnd:
if cmpIgnoreCase(x.elementName, result.tag) == 0:
if cmpIgnoreCase(x.elemName, result.tag) == 0:
next(x)
else:
echo "5; expected: ", result.htmltag, " ", x.elemName
errors.add(expected(x, result))
# do not skip it here!
break
@ -313,10 +473,10 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode,
proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
case x.kind
of xmlComment:
result = newComment(x.charData)
result = newComment(x.rawData)
next(x)
of xmlCharData, xmlWhitespace:
result = newText(x.charData)
result = newText(x.rawData)
next(x)
of xmlPI, xmlSpecial:
# we just ignore processing instructions for now
@ -325,19 +485,19 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
errors.add(errorMsg(x))
next(x)
of xmlElementStart:
result = newElement(x.elementName.toLower)
result = newElement(x.elemName.toLower)
next(x)
untilElementEnd(x, result, errors)
of xmlElementEnd:
errors.add(errorMsg(x, "unexpected ending tag: " & x.elementName))
errors.add(errorMsg(x, "unexpected ending tag: " & x.elemName))
of xmlElementOpen:
result = newElement(x.elementName.toLower)
result = newElement(x.elemName.toLower)
next(x)
result.attrs = newStringTable()
while true:
case x.kind
of xmlAttribute:
result.attrs[x.attrKey] = x.attrValue
result.attrs[x.rawData] = x.rawData2
next(x)
of xmlElementClose:
next(x)
@ -355,10 +515,10 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
errors.add(errorMsg(x, "<some_tag> expected"))
next(x)
of xmlCData:
result = newCData(x.charData)
result = newCData(x.rawData)
next(x)
of xmlEntity:
var u = entityToUtf8(x.entityName)
var u = entityToUtf8(x.rawData)
if u.len != 0: result = newText(u)
next(x)
of xmlEof: nil
@ -372,9 +532,11 @@ proc parseHtml*(s: PStream, filename: string,
next(x)
# skip the DOCTYPE:
if x.kind == xmlSpecial: next(x)
result = parse(x, errors)
if x.kind != xmlEof:
errors.add(errorMsg(x, "EOF expected"))
result = newElement("document")
result.addNode(parse(x, errors))
#if x.kind != xmlEof:
# errors.add(errorMsg(x, "EOF expected"))
while x.kind != xmlEof:
var oldPos = x.bufpos # little hack to see if we made any progess
result.addNode(parse(x, errors))
@ -382,6 +544,8 @@ proc parseHtml*(s: PStream, filename: string,
# force progress!
next(x)
close(x)
if result.len == 1:
result = result[0]
proc parseHtml*(s: PStream): PXmlNode =
## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing
@ -403,19 +567,6 @@ proc loadHtml*(path: string): PXmlNode =
var errors: seq[string] = @[]
result = loadHtml(path, errors)
when true:
nil
else:
proc checkHtmlAux(n: PXmlNode, errors: var seq[string]) =
nil
proc checkHtmlStructure*(n: PXmlNode, errors: var seq[string]) =
## checks the HTML structure after parsing for other errors like
## a ``<h1>`` element within a ``<p>`` element.
if n == nil or n.htmlTag != tagHtml:
errors.add("<html> tag expected")
checkHtmlAux(n, errors)
when isMainModule:
import os
@ -429,4 +580,3 @@ when isMainModule:
f.close()
else:
quit("cannot write test.txt")

View file

@ -47,10 +47,11 @@ proc badRequest(client: TSocket) =
send(client, "<p>Your browser sent a bad request, " &
"such as a POST without a Content-Length.</p>" & wwwNL)
proc cannotExec(client: TSocket) =
send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL)
sendTextContentType(client)
send(client, "<P>Error prohibited CGI execution." & wwwNL)
when false:
proc cannotExec(client: TSocket) =
send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL)
sendTextContentType(client)
send(client, "<P>Error prohibited CGI execution." & wwwNL)
proc headers(client: TSocket, filename: string) =
# XXX could use filename to determine file type
@ -79,11 +80,11 @@ proc unimplemented(client: TSocket) =
# ----------------- file serving ---------------------------------------------
proc discardHeaders(client: TSocket) = skip(client)
when false:
proc discardHeaders(client: TSocket) = skip(client)
proc serveFile*(client: TSocket, filename: string) =
## serves a file to the client.
when false: discardHeaders(client)
var f: TFile
if open(f, filename):
headers(client, filename)
@ -96,7 +97,7 @@ proc serveFile*(client: TSocket, filename: string) =
if bytesread != bytesWritten:
dealloc(buf)
close(f)
OSError()
OSError(OSLastError())
if bytesread != bufSize: break
dealloc(buf)
close(f)
@ -104,108 +105,109 @@ proc serveFile*(client: TSocket, filename: string) =
notFound(client)
# ------------------ CGI execution -------------------------------------------
when false:
# TODO: Fix this, or get rid of it.
type
TRequestMethod = enum reqGet, reqPost
type
TRequestMethod = enum reqGet, reqPost
proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) =
var env = newStringTable(modeCaseInsensitive)
var contentLength = -1
case meth
of reqGet:
discardHeaders(client)
proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) =
var env = newStringTable(modeCaseInsensitive)
var contentLength = -1
case meth
of reqGet:
discardHeaders(client)
env["REQUEST_METHOD"] = "GET"
env["QUERY_STRING"] = query
of reqPost:
var buf = TaintedString""
var dataAvail = false
while dataAvail:
dataAvail = recvLine(client, buf) # TODO: This is incorrect.
var L = toLower(buf.string)
if L.startsWith("content-length:"):
var i = len("content-length:")
while L[i] in Whitespace: inc(i)
contentLength = parseInt(substr(L, i))
env["REQUEST_METHOD"] = "GET"
env["QUERY_STRING"] = query
of reqPost:
var buf = TaintedString""
var dataAvail = false
while dataAvail:
dataAvail = recvLine(client, buf) # TODO: This is incorrect.
var L = toLower(buf.string)
if L.startsWith("content-length:"):
var i = len("content-length:")
while L[i] in Whitespace: inc(i)
contentLength = parseInt(substr(L, i))
if contentLength < 0:
badRequest(client)
return
if contentLength < 0:
badRequest(client)
return
env["REQUEST_METHOD"] = "POST"
env["CONTENT_LENGTH"] = $contentLength
env["REQUEST_METHOD"] = "POST"
env["CONTENT_LENGTH"] = $contentLength
send(client, "HTTP/1.0 200 OK" & wwwNL)
send(client, "HTTP/1.0 200 OK" & wwwNL)
var process = startProcess(command=path, env=env)
if meth == reqPost:
# get from client and post to CGI program:
var buf = alloc(contentLength)
if recv(client, buf, contentLength) != contentLength:
var process = startProcess(command=path, env=env)
if meth == reqPost:
# get from client and post to CGI program:
var buf = alloc(contentLength)
if recv(client, buf, contentLength) != contentLength:
dealloc(buf)
OSError()
var inp = process.inputStream
inp.writeData(buf, contentLength)
dealloc(buf)
OSError()
var inp = process.inputStream
inp.writeData(buf, contentLength)
dealloc(buf)
var outp = process.outputStream
var line = newStringOfCap(120).TaintedString
while true:
if outp.readLine(line):
send(client, line.string)
send(client, wwwNL)
elif not running(process): break
var outp = process.outputStream
var line = newStringOfCap(120).TaintedString
while true:
if outp.readLine(line):
send(client, line.string)
send(client, wwwNL)
elif not running(process): break
# --------------- Server Setup -----------------------------------------------
# --------------- Server Setup -----------------------------------------------
proc acceptRequest(client: TSocket) =
var cgi = false
var query = ""
var buf = TaintedString""
discard recvLine(client, buf)
var path = ""
var data = buf.string.split()
var meth = reqGet
proc acceptRequest(client: TSocket) =
var cgi = false
var query = ""
var buf = TaintedString""
discard recvLine(client, buf)
var path = ""
var data = buf.string.split()
var meth = reqGet
var q = find(data[1], '?')
var q = find(data[1], '?')
# extract path
if q >= 0:
# strip "?..." from path, this may be found in both POST and GET
path = "." & data[1].substr(0, q-1)
else:
path = "." & data[1]
# path starts with "/", by adding "." in front of it we serve files from cwd
if cmpIgnoreCase(data[0], "GET") == 0:
# extract path
if q >= 0:
# strip "?..." from path, this may be found in both POST and GET
path = "." & data[1].substr(0, q-1)
else:
path = "." & data[1]
# path starts with "/", by adding "." in front of it we serve files from cwd
if cmpIgnoreCase(data[0], "GET") == 0:
if q >= 0:
cgi = true
query = data[1].substr(q+1)
elif cmpIgnoreCase(data[0], "POST") == 0:
cgi = true
query = data[1].substr(q+1)
elif cmpIgnoreCase(data[0], "POST") == 0:
cgi = true
meth = reqPost
else:
unimplemented(client)
if path[path.len-1] == '/' or existsDir(path):
path = path / "index.html"
if not ExistsFile(path):
discardHeaders(client)
notFound(client)
else:
when defined(Windows):
var ext = splitFile(path).ext.toLower
if ext == ".exe" or ext == ".cgi":
# XXX: extract interpreter information here?
cgi = true
meth = reqPost
else:
if {fpUserExec, fpGroupExec, fpOthersExec} * path.getFilePermissions != {}:
cgi = true
if not cgi:
serveFile(client, path)
unimplemented(client)
if path[path.len-1] == '/' or existsDir(path):
path = path / "index.html"
if not ExistsFile(path):
discardHeaders(client)
notFound(client)
else:
executeCgi(client, path, query, meth)
when defined(Windows):
var ext = splitFile(path).ext.toLower
if ext == ".exe" or ext == ".cgi":
# XXX: extract interpreter information here?
cgi = true
else:
if {fpUserExec, fpGroupExec, fpOthersExec} * path.getFilePermissions != {}:
cgi = true
if not cgi:
serveFile(client, path)
else:
executeCgi(client, path, query, meth)
type
TServer* = object of TObject ## contains the current server state
@ -226,7 +228,7 @@ proc open*(s: var TServer, port = TPort(80)) =
## creates a new server at port `port`. If ``port == 0`` a free port is
## acquired that can be accessed later by the ``port`` proc.
s.socket = socket(AF_INET)
if s.socket == InvalidSocket: OSError()
if s.socket == InvalidSocket: OSError(OSLastError())
bindAddr(s.socket, port)
listen(s.socket)

View file

@ -252,7 +252,7 @@ when isMainModule:
["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"],
["test", "1", "2", "3", "4"]]
testit(x)
var test2: tuple[name: string, s: int] = ("tuple test", 56)
var test2: tuple[name: string, s: uint] = ("tuple test", 56u)
testit(test2)
type

View file

@ -50,11 +50,11 @@ proc open*(filename: string, mode: TFileMode = fmRead,
result.size = 0
when defined(windows):
template fail(msg: expr) =
template fail(errCode: TOSErrorCode, msg: expr) =
rollback()
if result.fHandle != 0: discard CloseHandle(result.fHandle)
if result.mapHandle != 0: discard CloseHandle(result.mapHandle)
OSError()
OSError(errCode)
# return false
#raise newException(EIO, msg)
@ -74,7 +74,7 @@ proc open*(filename: string, mode: TFileMode = fmRead,
result.fHandle = callCreateFile(CreateFileA, filename)
if result.fHandle == INVALID_HANDLE_VALUE:
fail "error opening file"
fail(OSLastError(), "error opening file")
if newFileSize != -1:
var
@ -83,9 +83,10 @@ proc open*(filename: string, mode: TFileMode = fmRead,
var status = SetFilePointer(result.fHandle, sizeLow, addr(sizeHigh),
FILE_BEGIN)
if (status == INVALID_SET_FILE_POINTER and GetLastError() != NO_ERROR) or
let lastErr = OSLastError()
if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or
(SetEndOfFile(result.fHandle) == 0):
fail "error setting file size"
fail(lastErr, "error setting file size")
# since the strings are always 'nil', we simply always call
# CreateFileMappingW which should be slightly faster anyway:
@ -95,7 +96,7 @@ proc open*(filename: string, mode: TFileMode = fmRead,
0, 0, nil)
if result.mapHandle == 0:
fail "error creating mapping"
fail(OSLastError(), "error creating mapping")
result.mem = MapViewOfFileEx(
result.mapHandle,
@ -106,22 +107,22 @@ proc open*(filename: string, mode: TFileMode = fmRead,
nil)
if result.mem == nil:
fail "error mapping view"
fail(OSLastError(), "error mapping view")
var hi, low: int32
low = GetFileSize(result.fHandle, addr(hi))
if low == INVALID_FILE_SIZE:
fail "error getting file size"
fail(OSLastError(), "error getting file size")
else:
var fileSize = (int64(hi) shr 32) or low
if mappedSize != -1: result.size = min(fileSize, mappedSize).int
else: result.size = fileSize.int
else:
template fail(msg: expr) =
template fail(errCode: TOSErrorCode, msg: expr) =
rollback()
if result.handle != 0: discard close(result.handle)
OSError()
OSError(errCode)
var flags = if readonly: O_RDONLY else: O_RDWR
@ -132,11 +133,11 @@ proc open*(filename: string, mode: TFileMode = fmRead,
if result.handle == -1:
# XXX: errno is supposed to be set here
# Is there an exception that wraps it?
fail "error opening file"
fail(OSLastError(), "error opening file")
if newFileSize != -1:
if ftruncate(result.handle, newFileSize) == -1:
fail "error setting file size"
fail(OSLastError(), "error setting file size")
if mappedSize != -1:
result.size = mappedSize
@ -147,7 +148,7 @@ proc open*(filename: string, mode: TFileMode = fmRead,
# Why is mmap taking int anyway?
result.size = int(stat.st_size)
else:
fail "error getting file size"
fail(OSLastError(), "error getting file size")
result.mem = mmap(
nil,
@ -158,21 +159,24 @@ proc open*(filename: string, mode: TFileMode = fmRead,
offset)
if result.mem == cast[pointer](MAP_FAILED):
fail "file mapping failed"
fail(OSLastError(), "file mapping failed")
proc close*(f: var TMemFile) =
## closes the memory mapped file `f`. All changes are written back to the
## file system, if `f` was opened with write access.
var error = false
var lastErr: TOSErrorCode
when defined(windows):
if f.fHandle != INVALID_HANDLE_VALUE:
lastErr = OSLastError()
error = UnmapViewOfFile(f.mem) == 0
error = (CloseHandle(f.mapHandle) == 0) or error
error = (CloseHandle(f.fHandle) == 0) or error
else:
if f.handle != 0:
lastErr = OSLastError()
error = munmap(f.mem, f.size) != 0
error = (close(f.handle) != 0) or error
@ -185,5 +189,5 @@ proc close*(f: var TMemFile) =
else:
f.handle = 0
if error: OSError()
if error: OSError(lastErr)

View file

@ -39,6 +39,8 @@ type
FWriteDir* = object of FWriteIO ## effect that denotes a write operation to
## the directory structure
TOSErrorCode* = distinct int32 ## Specifies an OS Error Code.
const
doslike = defined(windows) or defined(OS2) or defined(DOS)
# DOS-like filesystem
@ -171,10 +173,13 @@ const
## The character which separates the base filename from the extension;
## for example, the '.' in ``os.nim``.
proc OSErrorMsg*(): string {.rtl, extern: "nos$1".} =
proc OSErrorMsg*(): string {.rtl, extern: "nos$1", deprecated.} =
## Retrieves the operating system's error flag, ``errno``.
## On Windows ``GetLastError`` is checked before ``errno``.
## Returns "" if no error occured.
##
## **Deprecated since version 0.9.4**: use the other ``OSErrorMsg`` proc.
result = ""
when defined(Windows):
var err = GetLastError()
@ -194,17 +199,89 @@ proc OSErrorMsg*(): string {.rtl, extern: "nos$1".} =
if errno != 0'i32:
result = $os.strerror(errno)
proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1".} =
{.push warning[deprecated]: off.}
proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1", deprecated.} =
## raises an EOS exception with the given message ``msg``.
## If ``msg == ""``, the operating system's error flag
## (``errno``) is converted to a readable error message. On Windows
## ``GetLastError`` is checked before ``errno``.
## If no error flag is set, the message ``unknown OS error`` is used.
##
## **Deprecated since version 0.9.4**: use the other ``OSError`` proc.
if len(msg) == 0:
var m = OSErrorMsg()
raise newException(EOS, if m.len > 0: m else: "unknown OS error")
else:
raise newException(EOS, msg)
{.pop.}
proc `==`*(err1, err2: TOSErrorCode): bool {.borrow.}
proc `$`*(err: TOSErrorCode): string {.borrow.}
proc OSErrorMsg*(errorCode: TOSErrorCode): string =
## Converts an OS error code into a human readable string.
##
## The error code can be retrieved using the ``OSLastError`` proc.
##
## If conversion fails, or ``errorCode`` is ``0`` then ``""`` will be
## returned.
##
## On Windows, the ``-d:useWinAnsi`` compilation flag can be used to
## make this procedure use the non-unicode Win API calls to retrieve the
## message.
result = ""
when defined(Windows):
if errorCode != TOSErrorCode(0'i32):
when useWinUnicode:
var msgbuf: widecstring
if FormatMessageW(0x00000100 or 0x00001000 or 0x00000200,
nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
result = $msgbuf
if msgbuf != nil: LocalFree(cast[pointer](msgbuf))
else:
var msgbuf: cstring
if FormatMessageA(0x00000100 or 0x00001000 or 0x00000200,
nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
result = $msgbuf
if msgbuf != nil: LocalFree(msgbuf)
else:
if errorCode != TOSErrorCode(0'i32):
result = $os.strerror(errorCode.int32)
proc OSError*(errorCode: TOSErrorCode) =
## Raises an ``EOS`` exception. The ``errorCode`` will determine the
## message, ``OSErrorMsg`` will be used to get this message.
##
## The error code can be retrieved using the ``OSLastError`` proc.
##
## If the error code is ``0`` or an error message could not be retrieved,
## the message ``unknown OS error`` will be used.
let msg = OSErrorMsg(errorCode)
if msg == "":
raise newException(EOS, "unknown OS error")
else:
raise newException(EOS, msg)
{.push stackTrace:off.}
proc OSLastError*(): TOSErrorCode =
## Retrieves the last operating system error code.
##
## This procedure is useful in the event when an OS call fails. In that case
## this procedure will return the error code describing the reason why the
## OS call failed. The ``OSErrorMsg`` procedure can then be used to convert
## this code into a string.
##
## **Warning**:
## The behaviour of this procedure varies between Windows and POSIX systems.
## On Windows some OS calls can reset the error code to ``0`` causing this
## procedure to return ``0``. It is therefore advised to call this procedure
## immediately after an OS call fails. On POSIX systems this is not a problem.
when defined(windows):
result = TOSErrorCode(GetLastError())
else:
result = TOSErrorCode(errno)
{.pop.}
proc UnixToNativePath*(path: string): string {.
noSideEffect, rtl, extern: "nos$1".} =
@ -311,12 +388,12 @@ proc getLastModificationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
## Returns the `file`'s last modification time.
when defined(posix):
var res: TStat
if stat(file, res) < 0'i32: OSError()
if stat(file, res) < 0'i32: OSError(OSLastError())
return res.st_mtime
else:
var f: TWIN32_Find_Data
var h = findfirstFile(file, f)
if h == -1'i32: OSError()
if h == -1'i32: OSError(OSLastError())
result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
findclose(h)
@ -324,12 +401,12 @@ proc getLastAccessTime*(file: string): TTime {.rtl, extern: "nos$1".} =
## Returns the `file`'s last read or write access time.
when defined(posix):
var res: TStat
if stat(file, res) < 0'i32: OSError()
if stat(file, res) < 0'i32: OSError(OSLastError())
return res.st_atime
else:
var f: TWIN32_Find_Data
var h = findfirstFile(file, f)
if h == -1'i32: OSError()
if h == -1'i32: OSError(OSLastError())
result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
findclose(h)
@ -337,12 +414,12 @@ proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
## Returns the `file`'s creation time.
when defined(posix):
var res: TStat
if stat(file, res) < 0'i32: OSError()
if stat(file, res) < 0'i32: OSError(OSLastError())
return res.st_ctime
else:
var f: TWIN32_Find_Data
var h = findfirstFile(file, f)
if h == -1'i32: OSError()
if h == -1'i32: OSError(OSLastError())
result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
findclose(h)
@ -358,30 +435,30 @@ proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
when useWinUnicode:
var res = newWideCString("", bufsize)
var L = GetCurrentDirectoryW(bufsize, res)
if L == 0'i32: OSError()
if L == 0'i32: OSError(OSLastError())
result = res$L
else:
result = newString(bufsize)
var L = GetCurrentDirectoryA(bufsize, result)
if L == 0'i32: OSError()
if L == 0'i32: OSError(OSLastError())
setLen(result, L)
else:
result = newString(bufsize)
if getcwd(result, bufsize) != nil:
setlen(result, c_strlen(result))
else:
OSError()
OSError(OSLastError())
proc setCurrentDir*(newDir: string) {.inline, tags: [].} =
## Sets the `current working directory`:idx:; `EOS` is raised if
## `newDir` cannot been set.
when defined(Windows):
when useWinUnicode:
if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32: OSError()
if SetCurrentDirectoryW(newWideCString(newDir)) == 0'i32: OSError(OSLastError())
else:
if SetCurrentDirectoryA(newDir) == 0'i32: OSError()
if SetCurrentDirectoryA(newDir) == 0'i32: OSError(OSLastError())
else:
if chdir(newDir) != 0'i32: OSError()
if chdir(newDir) != 0'i32: OSError(OSLastError())
proc JoinPath*(head, tail: string): string {.
noSideEffect, rtl, extern: "nos$1".} =
@ -571,23 +648,23 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
var res = newWideCString("", bufsize div 2)
var L = GetFullPathNameW(newWideCString(filename), bufsize, res, unused)
if L <= 0'i32 or L >= bufsize:
OSError()
OSError(OSLastError())
result = res$L
else:
var unused: cstring
result = newString(bufsize)
var L = GetFullPathNameA(filename, bufsize, result, unused)
if L <= 0'i32 or L >= bufsize: OSError()
if L <= 0'i32 or L >= bufsize: OSError(OSLastError())
setLen(result, L)
elif defined(macosx) or defined(bsd):
# On Mac OS X 10.5, realpath does not allocate the buffer on its own
var pathBuffer: cstring = newString(pathMax)
var resultBuffer = realpath(filename, pathBuffer)
if resultBuffer == nil: OSError()
if resultBuffer == nil: OSError(OSLastError())
result = $resultBuffer
else:
var res = realpath(filename, nil)
if res == nil: OSError()
if res == nil: OSError(OSLastError())
result = $res
c_free(res)
@ -677,6 +754,7 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
var f1 = OpenHandle(path1)
var f2 = OpenHandle(path2)
var lastErr: TOSErrorCode
if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
var fi1, fi2: TBY_HANDLE_FILE_INFORMATION
@ -685,17 +763,21 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
fi1.nFileIndexHigh == fi2.nFileIndexHigh and
fi1.nFileIndexLow == fi2.nFileIndexLow
else: success = false
else: success = false
else:
lastErr = OSLastError()
success = false
else:
lastErr = OSLastError()
success = false
discard CloseHandle(f1)
discard CloseHandle(f2)
if not success: OSError()
if not success: OSError(lastErr)
else:
var a, b: TStat
if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
OSError()
OSError(OSLastError())
else:
result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
@ -738,17 +820,17 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
when useWinUnicode:
let s = newWideCString(source)
let d = newWideCString(dest)
if CopyFileW(s, d, 0'i32) == 0'i32: OSError()
if CopyFileW(s, d, 0'i32) == 0'i32: OSError(OSLastError())
else:
if CopyFileA(source, dest, 0'i32) == 0'i32: OSError()
if CopyFileA(source, dest, 0'i32) == 0'i32: OSError(OSLastError())
else:
# generic version of copyFile which works for any platform:
const bufSize = 8000 # better for memory manager
var d, s: TFile
if not open(s, source): OSError()
if not open(s, source): OSError(OSLastError())
if not open(d, dest, fmWrite):
close(s)
OSError()
OSError(OSLastError())
var buf = alloc(bufsize)
while True:
var bytesread = readBuffer(s, buf, bufsize)
@ -758,7 +840,7 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
dealloc(buf)
close(s)
close(d)
OSError()
OSError(OSLastError())
if bytesread != bufSize: break
dealloc(buf)
close(s)
@ -767,7 +849,8 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
tags: [FReadIO, FWriteIO].} =
## Moves a file from `source` to `dest`. If this fails, `EOS` is raised.
if crename(source, dest) != 0'i32: OSError()
if crename(source, dest) != 0'i32:
raise newException(EOS, $strerror(errno))
when not defined(ENOENT):
var ENOENT {.importc, header: "<errno.h>".}: cint
@ -775,7 +858,8 @@ when not defined(ENOENT):
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.
if cremove(file) != 0'i32 and errno != ENOENT: OSError()
if cremove(file) != 0'i32 and errno != ENOENT:
raise newException(EOS, $strerror(errno))
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
tags: [FExecIO].} =
@ -907,14 +991,14 @@ proc putEnv*(key, val: string) {.tags: [FWriteEnv].} =
indx = high(environment)
when defined(unix):
if cputenv(environment[indx]) != 0'i32:
OSError()
OSError(OSLastError())
else:
when useWinUnicode:
var k = newWideCString(key)
var v = newWideCString(val)
if SetEnvironmentVariableW(k, v) == 0'i32: OSError()
if SetEnvironmentVariableW(k, v) == 0'i32: OSError(OSLastError())
else:
if SetEnvironmentVariableA(key, val) == 0'i32: OSError()
if SetEnvironmentVariableA(key, val) == 0'i32: OSError(OSLastError())
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [FReadEnv].} =
## Iterate over all `environments variables`:idx:. In the first component
@ -1044,10 +1128,12 @@ proc rawRemoveDir(dir: string) =
wrapUnary(res, RemoveDirectoryW, dir)
else:
var res = RemoveDirectoryA(dir)
if res == 0'i32 and GetLastError() != 3'i32 and
GetLastError() != 18'i32: OSError()
let lastError = OSLastError()
if res == 0'i32 and lastError.int32 != 3'i32 and
lastError.int32 != 18'i32 and lastError.int32 != 2'i32:
OSError(lastError)
else:
if rmdir(dir) != 0'i32 and errno != ENOENT: OSError()
if rmdir(dir) != 0'i32 and errno != ENOENT: OSError(OSLastError())
proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
FWriteDir, FReadDir].} =
@ -1065,14 +1151,14 @@ proc removeDir*(dir: string) {.rtl, extern: "nos$1", tags: [
proc rawCreateDir(dir: string) =
when defined(unix):
if mkdir(dir, 0o711) != 0'i32 and errno != EEXIST:
OSError()
OSError(OSLastError())
else:
when useWinUnicode:
wrapUnary(res, CreateDirectoryW, dir)
else:
var res = CreateDirectoryA(dir)
if res == 0'i32 and GetLastError() != 183'i32:
OSError()
OSError(OSLastError())
proc createDir*(dir: string) {.rtl, extern: "nos$1", tags: [FWriteDir].} =
## Creates the `directory`:idx: `dir`.
@ -1213,7 +1299,7 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {.
## permission is available in any case.
when defined(posix):
var a: TStat
if stat(filename, a) < 0'i32: OSError()
if stat(filename, a) < 0'i32: OSError(OSLastError())
result = {}
if (a.st_mode and S_IRUSR) != 0'i32: result.incl(fpUserRead)
if (a.st_mode and S_IWUSR) != 0'i32: result.incl(fpUserWrite)
@ -1231,7 +1317,7 @@ proc getFilePermissions*(filename: string): set[TFilePermission] {.
wrapUnary(res, GetFileAttributesW, filename)
else:
var res = GetFileAttributesA(filename)
if res == -1'i32: OSError()
if res == -1'i32: OSError(OSLastError())
if (res and FILE_ATTRIBUTE_READONLY) != 0'i32:
result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead,
fpOthersExec, fpOthersRead}
@ -1257,13 +1343,13 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
if fpOthersWrite in permissions: p = p or S_IWOTH
if fpOthersExec in permissions: p = p or S_IXOTH
if chmod(filename, p) != 0: OSError()
if chmod(filename, p) != 0: OSError(OSLastError())
else:
when useWinUnicode:
wrapUnary(res, GetFileAttributesW, filename)
else:
var res = GetFileAttributesA(filename)
if res == -1'i32: OSError()
if res == -1'i32: OSError(OSLastError())
if fpUserWrite in permissions:
res = res and not FILE_ATTRIBUTE_READONLY
else:
@ -1272,7 +1358,7 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
wrapBinary(res2, SetFileAttributesW, filename, res)
else:
var res2 = SetFileAttributesA(filename, res)
if res2 == - 1'i32: OSError()
if res2 == - 1'i32: OSError(OSLastError())
proc inclFilePermissions*(filename: string,
permissions: set[TFilePermission]) {.
@ -1368,6 +1454,9 @@ when defined(macosx):
proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} =
## Returns the filename of the application's executable.
##
## This procedure will resolve symlinks.
##
## **Note**: This does not work reliably on BSD.
# Linux: /proc/<pid>/exe
@ -1397,6 +1486,8 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [FReadIO].} =
result = newString(int(size))
if getExecPath2(result, size):
result = "" # error!
if result.len > 0:
result = result.expandFilename
else:
# little heuristic that may work on other POSIX-like systems:
result = string(getEnv("_"))
@ -1443,7 +1534,7 @@ proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1",
when defined(windows):
var a: TWin32FindData
var resA = findfirstFile(file, a)
if resA == -1: OSError()
if resA == -1: OSError(OSLastError())
result = rdFileSize(a)
findclose(resA)
else:
@ -1451,7 +1542,7 @@ proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1",
if open(f, file):
result = getFileSize(f)
close(f)
else: OSError()
else: OSError(OSLastError())
proc findExe*(exe: string): string {.tags: [FReadDir, FReadEnv].} =
## Searches for `exe` in the current working directory and then

View file

@ -264,7 +264,7 @@ when defined(Windows) and not defined(useNimRtl):
# TRUE and zero bytes returned (EOF).
# TRUE and n (>0) bytes returned (good data).
# FALSE and bytes returned undefined (system error).
if a == 0 and br != 0: OSError()
if a == 0 and br != 0: OSError(OSLastError())
s.atTheEnd = br < bufLen
result = br
@ -272,7 +272,7 @@ when defined(Windows) and not defined(useNimRtl):
var s = PFileHandleStream(s)
var bytesWritten: int32
var a = winlean.writeFile(s.handle, buffer, bufLen.cint, bytesWritten, nil)
if a == 0: OSError()
if a == 0: OSError(OSLastError())
proc newFileHandleStream(handle: THandle): PFileHandleStream =
new(result)
@ -313,7 +313,7 @@ when defined(Windows) and not defined(useNimRtl):
piInheritablePipe.lpSecurityDescriptor = nil
piInheritablePipe.Binherithandle = 1
if CreatePipe(Rdhandle, Wrhandle, piInheritablePipe, 1024) == 0'i32:
OSError()
OSError(OSLastError())
proc fileClose(h: THandle) {.inline.} =
if h > 4: discard CloseHandle(h)
@ -370,6 +370,7 @@ when defined(Windows) and not defined(useNimRtl):
else:
success = winlean.CreateProcessA(nil,
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
let lastError = OSLastError()
if poParentStreams notin options:
FileClose(si.hStdInput)
@ -379,7 +380,7 @@ when defined(Windows) and not defined(useNimRtl):
if e != nil: dealloc(e)
dealloc(cmdl)
if success == 0: OSError()
if success == 0: OSError(lastError)
# Close the handle now so anyone waiting is woken:
discard closeHandle(procInfo.hThread)
result.FProcessHandle = procInfo.hProcess
@ -450,7 +451,7 @@ when defined(Windows) and not defined(useNimRtl):
var res = winlean.CreateProcessA(nil, command, nil, nil, 0,
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
if res == 0:
OSError()
OSError(OSLastError())
else:
Process = ProcInfo.hProcess
discard CloseHandle(ProcInfo.hThread)
@ -473,7 +474,7 @@ when defined(Windows) and not defined(useNimRtl):
of WAIT_TIMEOUT:
return 0
of WAIT_FAILED:
OSError()
OSError(OSLastError())
else:
var i = ret - WAIT_OBJECT_0
readfds.del(i)
@ -531,7 +532,7 @@ elif not defined(useNimRtl):
if poParentStreams notin options:
if pipe(p_stdin) != 0'i32 or pipe(p_stdout) != 0'i32 or
pipe(p_stderr) != 0'i32:
OSError()
OSError(OSLastError())
var pid: TPid
when defined(posix_spawn) and not defined(useFork):
@ -539,7 +540,7 @@ elif not defined(useNimRtl):
var fops: Tposix_spawn_file_actions
template chck(e: expr) =
if e != 0'i32: OSError()
if e != 0'i32: OSError(OSLastError())
chck posix_spawn_file_actions_init(fops)
chck posix_spawnattr_init(attr)
@ -584,20 +585,20 @@ elif not defined(useNimRtl):
else:
Pid = fork()
if Pid < 0: OSError()
if Pid < 0: OSError(OSLastError())
if pid == 0:
## child process:
if poParentStreams notin options:
discard close(p_stdin[writeIdx])
if dup2(p_stdin[readIdx], readIdx) < 0: OSError()
if dup2(p_stdin[readIdx], readIdx) < 0: OSError(OSLastError())
discard close(p_stdout[readIdx])
if dup2(p_stdout[writeIdx], writeIdx) < 0: OSError()
if dup2(p_stdout[writeIdx], writeIdx) < 0: OSError(OSLastError())
discard close(p_stderr[readIdx])
if poStdErrToStdOut in options:
if dup2(p_stdout[writeIdx], 2) < 0: OSError()
if dup2(p_stdout[writeIdx], 2) < 0: OSError(OSLastError())
else:
if dup2(p_stderr[writeIdx], 2) < 0: OSError()
if dup2(p_stderr[writeIdx], 2) < 0: OSError(OSLastError())
# Create a new process group
if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno))
@ -652,10 +653,10 @@ elif not defined(useNimRtl):
discard close(p.errorHandle)
proc suspend(p: PProcess) =
if kill(-p.id, SIGSTOP) != 0'i32: OSError()
if kill(-p.id, SIGSTOP) != 0'i32: OSError(OSLastError())
proc resume(p: PProcess) =
if kill(-p.id, SIGCONT) != 0'i32: OSError()
if kill(-p.id, SIGCONT) != 0'i32: OSError(OSLastError())
proc running(p: PProcess): bool =
var ret = waitPid(p.id, p.exitCode, WNOHANG)
@ -665,8 +666,8 @@ elif not defined(useNimRtl):
proc terminate(p: PProcess) =
if kill(-p.id, SIGTERM) == 0'i32:
if p.running():
if kill(-p.id, SIGKILL) != 0'i32: OSError()
else: OSError()
if kill(-p.id, SIGKILL) != 0'i32: OSError(OSLastError())
else: OSError(OSLastError())
proc waitForExit(p: PProcess, timeout: int = -1): int =
#if waitPid(p.id, p.exitCode, 0) == int(p.id):
@ -676,7 +677,7 @@ elif not defined(useNimRtl):
if p.exitCode != -3: return p.exitCode
if waitPid(p.id, p.exitCode, 0) < 0:
p.exitCode = -3
OSError()
OSError(OSLastError())
result = int(p.exitCode) shr 8
proc peekExitCode(p: PProcess): int =
@ -690,7 +691,7 @@ elif not defined(useNimRtl):
proc createStream(stream: var PStream, handle: var TFileHandle,
fileMode: TFileMode) =
var f: TFile
if not open(f, handle, fileMode): OSError()
if not open(f, handle, fileMode): OSError(OSLastError())
stream = newFileStream(f)
proc inputStream(p: PProcess): PStream =

View file

@ -67,7 +67,8 @@ type
# implementation
const
SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.'}
SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.',
'/', '\\'}
proc rawGetTok(c: var TCfgParser, tok: var TToken)
@ -149,7 +150,7 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
of 't', 'T':
add(tok.literal, '\t')
Inc(c.bufpos)
of '\'', '\"':
of '\'', '"':
add(tok.literal, c.buf[c.bufpos])
Inc(c.bufpos)
of '\\':
@ -178,7 +179,7 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
var pos = c.bufPos + 1 # skip "
var buf = c.buf # put `buf` in a register
tok.kind = tkSymbol
if (buf[pos] == '\"') and (buf[pos + 1] == '\"'):
if (buf[pos] == '"') and (buf[pos + 1] == '"'):
# long string literal:
inc(pos, 2) # skip ""
# skip leading newline:
@ -186,9 +187,9 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
buf = c.buf
while true:
case buf[pos]
of '\"':
if (buf[pos + 1] == '\"') and (buf[pos + 2] == '\"'): break
add(tok.literal, '\"')
of '"':
if (buf[pos + 1] == '"') and (buf[pos + 2] == '"'): break
add(tok.literal, '"')
Inc(pos)
of '\c', '\L':
pos = HandleCRLF(c, pos)
@ -205,7 +206,7 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
# ordinary string literal
while true:
var ch = buf[pos]
if ch == '\"':
if ch == '"':
inc(pos) # skip '"'
break
if ch in {'\c', '\L', lexbase.EndOfFile}:
@ -278,7 +279,7 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) =
tok.kind = tkBracketRi
Inc(c.bufpos)
tok.literal = "]"
of '\"':
of '"':
getString(c, tok, false)
of lexbase.EndOfFile:
tok.kind = tkEof
@ -351,7 +352,7 @@ proc next*(c: var TCfgParser): TCfgEvent {.rtl, extern: "npc$1".} =
rawGetTok(c, c.tok)
else:
result.kind = cfgError
result.msg = errorStr(c, "\']\' expected, but found: " & c.tok.literal)
result.msg = errorStr(c, "']' expected, but found: " & c.tok.literal)
of tkInvalid, tkEquals, tkColon, tkBracketRi:
result.kind = cfgError
result.msg = errorStr(c, "invalid token: " & c.tok.literal)

View file

@ -131,6 +131,10 @@ proc close*(my: var TXmlParser) {.inline.} =
## closes the parser `my` and its associated input stream.
lexbase.close(my)
proc kind*(my: TXmlParser): TXmlEventKind {.inline.} =
## returns the current event type for the XML parser
return my.kind
proc charData*(my: TXmlParser): string {.inline.} =
## returns the character data for the events: ``xmlCharData``,
## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial``
@ -138,10 +142,6 @@ proc charData*(my: TXmlParser): string {.inline.} =
xmlSpecial})
return my.a
proc kind*(my: TXmlParser): TXmlEventKind {.inline.} =
## returns the current event type for the XML parser
return my.kind
proc elementName*(my: TXmlParser): string {.inline.} =
## returns the element name for the events: ``xmlElementStart``,
## ``xmlElementEnd``, ``xmlElementOpen``
@ -173,6 +173,16 @@ proc PIRest*(my: TXmlParser): string {.inline.} =
assert(my.kind == xmlPI)
return my.b
proc rawData*(my: TXmlParser): string {.inline.} =
## returns the underlying 'data' string by reference.
## This is only used for speed hacks.
shallowCopy(result, my.a)
proc rawData2*(my: TXmlParser): string {.inline.} =
## returns the underlying second 'data' string by reference.
## This is only used for speed hacks.
shallowCopy(result, my.b)
proc getColumn*(my: TXmlParser): int {.inline.} =
## get the current column the parser has arrived at.
result = getColNumber(my, my.bufPos)
@ -439,6 +449,7 @@ proc parseTag(my: var TXmlParser) =
if my.buf[my.bufpos] == '/' and my.buf[my.bufpos+1] == '>':
inc(my.bufpos, 2)
my.state = stateEmptyElementTag
my.c = nil
elif my.buf[my.bufpos] == '>':
inc(my.bufpos)
else:

View file

@ -82,7 +82,7 @@ type
of pkChar, pkGreedyRepChar: ch: char
of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
of pkNonTerminal: nt: PNonTerminal
of pkBackRef..pkBackRefIgnoreStyle: index: range[1..MaxSubpatterns]
of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns]
else: sons: seq[TNode]
PNonTerminal* = ref TNonTerminal
@ -870,7 +870,6 @@ template `=~`*(s: string, pattern: TPeg): bool =
bind maxSubpatterns
when not definedInScope(matches):
var matches {.inject.}: array[0..maxSubpatterns-1, string]
{.warning: "injected 'matches' might be affected by new scoping rules in 0.9.4".}
match(s, pattern, matches)
# ------------------------- more string handling ------------------------------

View file

@ -36,7 +36,7 @@ proc open*(host = "localhost", port = 6379.TPort): TRedis =
## Opens a connection to the redis server.
result.socket = socket(buffered = false)
if result.socket == InvalidSocket:
OSError()
OSError(OSLastError())
result.socket.connect(host, port)
proc raiseInvalidReply(expected, got: char) =
@ -50,34 +50,31 @@ proc raiseNoOK(status: string) =
proc parseStatus(r: TRedis): TRedisStatus =
var line = ""
if r.socket.recvLine(line):
if line == "":
raise newException(ERedis, "Server closed connection prematurely")
r.socket.readLine(line)
if line == "":
raise newException(ERedis, "Server closed connection prematurely")
if line[0] == '-':
raise newException(ERedis, strip(line))
if line[0] != '+':
raiseInvalidReply('+', line[0])
if line[0] == '-':
raise newException(ERedis, strip(line))
if line[0] != '+':
raiseInvalidReply('+', line[0])
return line.substr(1) # Strip '+'
else:
OSError()
return line.substr(1) # Strip '+'
proc parseInteger(r: TRedis): TRedisInteger =
var line = ""
if r.socket.recvLine(line):
if line == "":
raise newException(ERedis, "Server closed connection prematurely")
r.socket.readLine(line)
if line == "":
raise newException(ERedis, "Server closed connection prematurely")
if line[0] == '-':
raise newException(ERedis, strip(line))
if line[0] != ':':
raiseInvalidReply(':', line[0])
if line[0] == '-':
raise newException(ERedis, strip(line))
if line[0] != ':':
raiseInvalidReply(':', line[0])
# Strip ':'
if parseBiggestInt(line, result, 1) == 0:
raise newException(EInvalidReply, "Unable to parse integer.")
else: OSError()
# Strip ':'
if parseBiggestInt(line, result, 1) == 0:
raise newException(EInvalidReply, "Unable to parse integer.")
proc recv(sock: TSocket, size: int): TaintedString =
result = newString(size).TaintedString
@ -86,8 +83,7 @@ proc recv(sock: TSocket, size: int): TaintedString =
proc parseBulk(r: TRedis, allowMBNil = False): TRedisString =
var line = ""
if not r.socket.recvLine(line.TaintedString):
raise newException(EInvalidReply, "recvLine failed")
r.socket.readLine(line.TaintedString)
# Error.
if line[0] == '-':
@ -110,8 +106,7 @@ proc parseBulk(r: TRedis, allowMBNil = False): TRedisString =
proc parseMultiBulk(r: TRedis): TRedisList =
var line = TaintedString""
if not r.socket.recvLine(line):
raise newException(EInvalidReply, "recvLine failed")
r.socket.readLine(line)
if line.string[0] != '*':
raiseInvalidReply('*', line.string[0])
@ -848,10 +843,8 @@ proc shutdown*(r: TRedis) =
## Synchronously save the dataset to disk and then shut down the server
r.sendCommand("SHUTDOWN")
var s = "".TaintedString
if r.socket.recvLine(s):
if s.string.len != 0: raise newException(ERedis, s.string)
else:
OSError()
r.socket.readLine(s)
if s.string.len != 0: raise newException(ERedis, s.string)
proc slaveof*(r: TRedis, host: string, port: string) =
## Make the server a slave of another instance, or promote it as master

View file

@ -175,6 +175,15 @@ proc recvBufferAsync(client: PAsyncClient, L: int): TReadLineResult =
if ret == L:
return ReadFullLine
proc checkCloseSocket(client: PAsyncClient) =
if not client.c.isClosed:
if client.c.isSendDataBuffered:
client.c.setHandleWrite do (s: PAsyncSocket):
if not s.isClosed and not s.isSendDataBuffered:
s.close()
s.delHandleWrite()
else: client.c.close()
proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
case client.mode
of ClientReadChar:
@ -206,7 +215,7 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
client.mode = ClientReadContent
else:
s.handleRequest(client.c, client.input, client.headers)
if not client.c.isClosed: client.c.close()
checkCloseSocket(client)
of ReadPartialLine, ReadDisconnected, ReadNone: return
of ClientReadContent:
let L = parseInt(client.headers["CONTENT_LENGTH"])-client.input.len
@ -215,11 +224,11 @@ proc handleClientRead(client: PAsyncClient, s: PAsyncScgiState) =
case ret
of ReadFullLine:
s.handleRequest(client.c, client.input, client.headers)
if not client.c.isClosed: client.c.close()
checkCloseSocket(client)
of ReadPartialLine, ReadDisconnected, ReadNone: return
else:
s.handleRequest(client.c, client.input, client.headers)
if not client.c.isClosed: client.c.close()
checkCloseSocket(client)
proc handleAccept(sock: PAsyncSocket, s: PAsyncScgiState) =
var client: PAsyncSocket

View file

@ -231,7 +231,7 @@ when defined(ssl):
if err == 0:
raise newException(ESSL, "No error reported.")
if err == -1:
OSError()
OSError(OSLastError())
var errStr = ErrErrorString(err, nil)
raise newException(ESSL, $errStr)
@ -347,24 +347,23 @@ proc SocketError*(socket: TSocket, err: int = -1, async = false) =
else: SSLError("Unknown Error")
if err == -1 and not (when defined(ssl): socket.isSSL else: false):
let lastError = OSLastError()
if async:
when defined(windows):
# TODO: Test on Windows
var err = WSAGetLastError()
if err == WSAEWOULDBLOCK:
if lastError.int32 == WSAEWOULDBLOCK:
return
else: OSError()
else: OSError(lastError)
else:
if errno == EAGAIN or errno == EWOULDBLOCK:
if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK:
return
else: OSError()
else: OSError()
else: OSError(lastError)
else: OSError(lastError)
proc listen*(socket: TSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} =
## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the
## queue of pending connections.
if listen(socket.fd, cint(backlog)) < 0'i32: OSError()
if listen(socket.fd, cint(backlog)) < 0'i32: OSError(OSLastError())
proc invalidIp4(s: string) {.noreturn, noinline.} =
raise newException(EInvalidValue, "invalid ip4 address: " & s)
@ -403,7 +402,7 @@ template gaiNim(a, p, h, list: expr): stmt =
var gaiResult = getAddrInfo(a, $p, addr(h), list)
if gaiResult != 0'i32:
when defined(windows):
OSError()
OSError(OSLastError())
else:
OSError($gai_strerror(gaiResult))
@ -423,7 +422,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
if bindSocket(socket.fd, cast[ptr TSockAddr](addr(name)),
sizeof(name).TSockLen) < 0'i32:
OSError()
OSError(OSLastError())
else:
var hints: TAddrInfo
var aiList: ptr TAddrInfo = nil
@ -432,21 +431,7 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
hints.ai_protocol = toInt(IPPROTO_TCP)
gaiNim(address, port, hints, aiList)
if bindSocket(socket.fd, aiList.ai_addr, aiList.ai_addrLen.TSockLen) < 0'i32:
OSError()
when false:
proc bindAddr*(socket: TSocket, port = TPort(0)) =
## binds a port number to a socket.
var name: Tsockaddr_in
when defined(Windows):
name.sin_family = int16(ord(AF_INET))
else:
name.sin_family = posix.AF_INET
name.sin_port = sockets.htons(int16(port))
name.sin_addr.s_addr = sockets.htonl(INADDR_ANY)
if bindSocket(cint(socket), cast[ptr TSockAddr](addr(name)),
sizeof(name).TSockLen) < 0'i32:
OSError()
OSError(OSLastError())
proc getSockName*(socket: TSocket): TPort =
## returns the socket's associated port number.
@ -460,7 +445,7 @@ proc getSockName*(socket: TSocket): TPort =
var namelen = sizeof(name).TSockLen
if getsockname(socket.fd, cast[ptr TSockAddr](addr(name)),
addr(namelen)) == -1'i32:
OSError()
OSError(OSLastError())
result = TPort(sockets.ntohs(name.sin_port))
template acceptAddrPlain(noClientRet, successRet: expr,
@ -472,26 +457,25 @@ template acceptAddrPlain(noClientRet, successRet: expr,
addr(addrLen))
if sock < 0:
# TODO: Test on Windows.
let err = OSLastError()
when defined(windows):
var err = WSAGetLastError()
if err == WSAEINPROGRESS:
if err.int32 == WSAEINPROGRESS:
client = InvalidSocket
address = ""
when noClientRet.int == -1:
return
else:
return noClientRet
else: OSError()
else: OSError(err)
else:
if errno == EAGAIN or errno == EWOULDBLOCK:
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
client = InvalidSocket
address = ""
when noClientRet.int == -1:
return
else:
return noClientRet
else: OSError()
else: OSError(err)
else:
client.fd = sock
client.isBuffered = server.isBuffered
@ -644,7 +628,7 @@ proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} =
var s = winlean.getservbyname(name, proto)
else:
var s = posix.getservbyname(name, proto)
if s == nil: OSError()
if s == nil: OSError(OSLastError())
result.name = $s.s_name
result.aliases = cstringArrayToSeq(s.s_aliases)
result.port = TPort(s.s_port)
@ -656,7 +640,7 @@ proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} =
var s = winlean.getservbyport(ze(int16(port)).cint, proto)
else:
var s = posix.getservbyport(ze(int16(port)).cint, proto)
if s == nil: OSError()
if s == nil: OSError(OSLastError())
result.name = $s.s_name
result.aliases = cstringArrayToSeq(s.s_aliases)
result.port = TPort(s.s_port)
@ -670,7 +654,7 @@ proc getHostByAddr*(ip: string): THostEnt {.tags: [FReadIO].} =
when defined(windows):
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
cint(sockets.AF_INET))
if s == nil: OSError()
if s == nil: OSError(OSLastError())
else:
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSockLen,
cint(posix.AF_INET))
@ -687,7 +671,7 @@ proc getHostByAddr*(ip: string): THostEnt {.tags: [FReadIO].} =
elif s.h_addrtype == posix.AF_INET6:
result.addrType = AF_INET6
else:
OSError("unknown h_addrtype")
raise newException(EOS, "unknown h_addrtype")
result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length)
@ -697,7 +681,7 @@ proc getHostByName*(name: string): THostEnt {.tags: [FReadIO].} =
var s = winlean.gethostbyname(name)
else:
var s = posix.gethostbyname(name)
if s == nil: OSError()
if s == nil: OSError(OSLastError())
result.name = $s.h_name
result.aliases = cstringArrayToSeq(s.h_aliases)
when defined(windows):
@ -708,7 +692,7 @@ proc getHostByName*(name: string): THostEnt {.tags: [FReadIO].} =
elif s.h_addrtype == posix.AF_INET6:
result.addrType = AF_INET6
else:
OSError("unknown h_addrtype")
raise newException(EOS, "unknown h_addrtype")
result.addrList = cstringArrayToSeq(s.h_addr_list)
result.length = int(s.h_length)
@ -719,7 +703,7 @@ proc getSockOptInt*(socket: TSocket, level, optname: int): int {.
var size = sizeof(res).TSockLen
if getsockopt(socket.fd, cint(level), cint(optname),
addr(res), addr(size)) < 0'i32:
OSError()
OSError(OSLastError())
result = int(res)
proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
@ -728,7 +712,7 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
var value = cint(optval)
if setsockopt(socket.fd, cint(level), cint(optname), addr(value),
sizeof(value).TSockLen) < 0'i32:
OSError()
OSError(OSLastError())
proc connect*(socket: TSocket, address: string, port = TPort(0),
af: TDomain = AF_INET) {.tags: [FReadIO].} =
@ -746,15 +730,17 @@ proc connect*(socket: TSocket, address: string, port = TPort(0),
gaiNim(address, port, hints, aiList)
# try all possibilities:
var success = false
var lastError: TOSErrorCode
var it = aiList
while it != nil:
if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSockLen) == 0'i32:
success = true
break
else: lastError = OSLastError()
it = it.ai_next
freeaddrinfo(aiList)
if not success: OSError()
if not success: OSError(lastError)
when defined(ssl):
if socket.isSSL:
@ -807,6 +793,7 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
gaiNim(name, port, hints, aiList)
# try all possibilities:
var success = false
var lastError: TOSErrorCode
var it = aiList
while it != nil:
var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSockLen)
@ -814,22 +801,21 @@ proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
success = true
break
else:
# TODO: Test on Windows.
lastError = OSLastError()
when defined(windows):
var err = WSAGetLastError()
# Windows EINTR doesn't behave same as POSIX.
if err == WSAEWOULDBLOCK:
if lastError.int32 == WSAEWOULDBLOCK:
success = true
break
else:
if errno == EINTR or errno == EINPROGRESS:
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
success = true
break
it = it.ai_next
freeaddrinfo(aiList)
if not success: OSError()
if not success: OSError(lastError)
when defined(ssl):
if socket.isSSL:
socket.sslNoHandshake = true
@ -1110,7 +1096,7 @@ proc waitFor(socket: TSocket, waited: var float, timeout, size: int,
var s = @[socket]
var startTime = epochTime()
let selRet = select(s, timeout - int(waited * 1000.0))
if selRet < 0: OSError()
if selRet < 0: OSError(OSLastError())
if selRet != 1:
raise newException(ETimeout, "Call to '" & funcName & "' timed out.")
waited += (epochTime() - startTime)
@ -1259,14 +1245,14 @@ proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {.
var c: char
discard waitFor(socket, waited, timeout, 1, "readLine")
var n = recv(socket, addr(c), 1)
if n < 0: OSError()
if n < 0: OSError(OSLastError())
elif n == 0: return
if c == '\r':
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: OSError()
elif n <= 0: OSError(OSLastError())
addNlIfEmpty()
return
elif c == '\L':
@ -1326,6 +1312,7 @@ proc readLineAsync*(socket: TSocket,
while true:
var c: char
var n = recv(socket, addr(c), 1)
#echo(n)
if n < 0:
if line.len == 0: errorOrNone else: return ReadPartialLine
elif n == 0:
@ -1352,7 +1339,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} =
var pos = 0
while true:
var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1)
if bytesRead == -1: OSError()
if bytesRead == -1: OSError(OSLastError())
setLen(result.string, pos + bytesRead)
if bytesRead != bufSize-1: break
# increase capacity:
@ -1364,7 +1351,7 @@ proc recv*(socket: TSocket): TaintedString {.tags: [FReadIO], deprecated.} =
while true:
var bytesRead = recv(socket, cstring(buf), bufSize-1)
# Error
if bytesRead == -1: OSError()
if bytesRead == -1: OSError(OSLastError())
buf[bytesRead] = '\0' # might not be necessary
setLen(buf, bytesRead)
@ -1421,16 +1408,15 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool {.
else: SSLError("Unknown Error")
if bytesRead == -1 and not (when defined(ssl): socket.isSSL else: false):
let err = OSLastError()
when defined(windows):
# TODO: Test on Windows
var err = WSAGetLastError()
if err == WSAEWOULDBLOCK:
if err.int32 == WSAEWOULDBLOCK:
return False
else: OSError()
else: OSError(err)
else:
if errno == EAGAIN or errno == EWOULDBLOCK:
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
return False
else: OSError()
else: OSError(err)
setLen(s.string, pos + bytesRead)
if bytesRead != bufSize-1: break
@ -1475,16 +1461,15 @@ proc recvFromAsync*(socket: TSocket, data: var String, length: int,
result = true
var callRes = recvFrom(socket, data, length, address, port, flags)
if callRes < 0:
let err = OSLastError()
when defined(windows):
# TODO: Test on Windows
var err = WSAGetLastError()
if err == WSAEWOULDBLOCK:
if err.int32 == WSAEWOULDBLOCK:
return False
else: OSError()
else: OSError(err)
else:
if errno == EAGAIN or errno == EWOULDBLOCK:
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
return False
else: OSError()
else: OSError(err)
proc skip*(socket: TSocket) {.tags: [FReadIO], deprecated.} =
## skips all the data that is pending for the socket
@ -1531,7 +1516,7 @@ proc send*(socket: TSocket, data: string) {.tags: [FWriteIO].} =
if socket.isSSL:
SSLError()
OSError()
OSError(OSLastError())
proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} =
## sends data to a non-blocking socket.
@ -1561,16 +1546,15 @@ proc sendAsync*(socket: TSocket, data: string): int {.tags: [FWriteIO].} =
else:
return
if result == -1:
let err = OSLastError()
when defined(windows):
var err = WSAGetLastError()
# TODO: Test on windows.
if err == WSAEINPROGRESS:
if err.int32 == WSAEINPROGRESS:
return 0
else: OSError()
else: OSError(err)
else:
if errno == EAGAIN or errno == EWOULDBLOCK:
if err.int32 == EAGAIN or err.int32 == EWOULDBLOCK:
return 0
else: OSError()
else: OSError(err)
proc trySend*(socket: TSocket, data: string): bool {.tags: [FWriteIO].} =
@ -1626,15 +1610,15 @@ proc setBlocking(s: TSocket, blocking: bool) =
when defined(Windows):
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
if ioctlsocket(TWinSocket(s.fd), FIONBIO, addr(mode)) == -1:
OSError()
OSError(OSLastError())
else: # BSD sockets
var x: int = fcntl(s.fd, F_GETFL, 0)
if x == -1:
OSError()
OSError(OSLastError())
else:
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
if fcntl(s.fd, F_SETFL, mode) == -1:
OSError()
OSError(OSLastError())
s.nonblocking = not blocking
proc connect*(socket: TSocket, address: string, port = TPort(0), timeout: int,
@ -1665,6 +1649,6 @@ proc getFD*(socket: TSocket): cint = return socket.fd
when defined(Windows):
var wsa: TWSADATA
if WSAStartup(0x0101'i16, wsa) != 0: OSError()
if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())

View file

@ -27,11 +27,11 @@ when defined(windows):
var hTemp = GetStdHandle(STD_OUTPUT_HANDLE)
if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(),
addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
OSError()
OSError(OSLastError())
proc getCursorPos(): tuple [x,y: int] =
var c: TCONSOLE_SCREEN_BUFFER_INFO
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: OSError()
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: OSError(OSLastError())
return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y))
proc getAttributes(): int16 =
@ -39,8 +39,6 @@ when defined(windows):
# workaround Windows bugs: try several times
if GetConsoleScreenBufferInfo(conHandle, addr(c)) != 0:
return c.wAttributes
else:
OSError()
return 0x70'i16 # ERROR: return white background, black text
var
@ -53,7 +51,7 @@ proc setCursorPos*(x, y: int) =
var c: TCoord
c.x = int16(x)
c.y = int16(y)
if SetConsoleCursorPosition(conHandle, c) == 0: OSError()
if SetConsoleCursorPosition(conHandle, c) == 0: OSError(OSLastError())
else:
stdout.write("\e[" & $y & ';' & $x & 'f')
@ -63,10 +61,10 @@ proc setCursorXPos*(x: int) =
when defined(windows):
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError()
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
var origin = scrbuf.dwCursorPosition
origin.x = int16(x)
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError()
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError())
else:
stdout.write("\e[" & $x & 'G')
@ -77,10 +75,10 @@ when defined(windows):
when defined(windows):
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError()
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
var origin = scrbuf.dwCursorPosition
origin.y = int16(y)
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError()
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError())
else:
nil
@ -157,18 +155,18 @@ proc EraseLine* =
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
var numwrote: DWORD
var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError()
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
var origin = scrbuf.dwCursorPosition
origin.x = 0'i16
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError()
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError())
var ht = scrbuf.dwSize.Y - origin.Y
var wt = scrbuf.dwSize.X - origin.X
if FillConsoleOutputCharacter(hStdout,' ', ht*wt,
origin, addr(numwrote)) == 0:
OSError()
OSError(OSLastError())
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt,
scrbuf.dwCursorPosition, addr(numwrote)) == 0:
OSError()
OSError(OSLastError())
else:
stdout.write("\e[2K")
setCursorXPos(0)
@ -180,14 +178,14 @@ proc EraseScreen* =
var numwrote: DWORD
var origin: TCoord # is inititalized to 0, 0
var hStdout = conHandle
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError()
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y,
origin, addr(numwrote)) == 0:
OSError()
OSError(OSLastError())
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes,
scrbuf.dwSize.X * scrbuf.dwSize.Y,
origin, addr(numwrote)) == 0:
OSError()
OSError(OSLastError())
setCursorXPos(0)
else:
stdout.write("\e[2J")

View file

@ -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.
@ -338,24 +338,24 @@ when not defined(JS):
const
weekDays: array [0..6, TWeekDay] = [
dSun, dMon, dTue, dWed, dThu, dFri, dSat]
result.second = int(tm.second)
result.minute = int(tm.minute)
result.hour = int(tm.hour)
result.monthday = int(tm.monthday)
result.month = TMonth(tm.month)
result.year = tm.year + 1900'i32
result.weekday = weekDays[int(tm.weekDay)]
result.yearday = int(tm.yearday)
result.isDST = tm.isDST > 0
if local:
if result.isDST:
result.tzname = getTzname().DST
else:
result.tzname = getTzname().nonDST
else:
result.tzname = "UTC"
result.timezone = if local: getTimezone() else: 0
TTimeInfo(second: int(tm.second),
minute: int(tm.minute),
hour: int(tm.hour),
monthday: int(tm.monthday),
month: TMonth(tm.month),
year: tm.year + 1900'i32,
weekday: weekDays[int(tm.weekDay)],
yearday: int(tm.yearday),
isDST: tm.isDST > 0,
tzname: if local:
if tm.isDST > 0:
getTzname().DST
else:
getTzname().nonDST
else:
"UTC",
timezone: if local: getTimezone() else: 0
)
proc timeInfoToTM(t: TTimeInfo): structTM =
const

View file

@ -110,11 +110,9 @@ proc parseXml*(s: PStream, filename: string,
of xmlElementOpen, xmlElementStart:
result = parse(x, errors)
break
of xmlComment, xmlWhitespace: nil # just skip it
of xmlComment, xmlWhitespace, xmlSpecial: nil # just skip it
of xmlError:
errors.add(errorMsg(x))
of xmlSpecial:
errors.add(errorMsg(x, "<some_tag> expected"))
else:
errors.add(errorMsg(x, "<some_tag> expected"))
break

View file

@ -71,6 +71,16 @@ proc text*(n: PXmlNode): string {.inline.} =
assert n.k in {xnText, xnComment, xnCData, xnEntity}
result = n.fText
proc rawText*(n: PXmlNode): string {.inline.} =
## returns the underlying 'text' string by reference.
## This is only used for speed hacks.
shallowCopy(result, n.fText)
proc rawTag*(n: PXmlNode): string {.inline.} =
## returns the underlying 'tag' string by reference.
## This is only used for speed hacks.
shallowCopy(result, n.fTag)
proc innerText*(n: PXmlNode): string =
## gets the inner text of `n`. `n` has to be an ``xnElement`` node. Only
## ``xnText`` and ``xnEntity`` nodes are considered part of `n`'s inner text,

View file

@ -489,7 +489,7 @@ proc FD_SET*(Socket: TWinSocket, FDSet: var TFDSet) =
proc FD_ZERO*(FDSet: var TFDSet) =
FDSet.fd_count = 0
proc WSAStartup*(wVersionRequired: int16, WSData: var TWSAData): cint {.
proc WSAStartup*(wVersionRequired: int16, WSData: ptr TWSAData): cint {.
stdcall, importc: "WSAStartup", dynlib: ws2dll.}
proc getaddrinfo*(nodename, servname: cstring, hints: ptr TAddrInfo,

View file

@ -27,7 +27,7 @@ type
gshort* = cshort
glong* = clong
gint* = cint
gboolean* = bool
gboolean* = distinct gint
guchar* = char
gushort* = int16
gulong* = int
@ -173,6 +173,12 @@ type
TGBoxedFreeFunc* = proc (boxed: gpointer){.cdecl.}
PGsource = pointer # I don't know and don't care
converter gbool*(nimbool: bool): gboolean =
return ord(nimbool).gboolean
converter toBool*(gbool: gboolean): bool =
return int(gbool) == 1
const
G_TYPE_FUNDAMENTAL_SHIFT* = 2
G_TYPE_FUNDAMENTAL_MAX* = 255 shl G_TYPE_FUNDAMENTAL_SHIFT

View file

@ -2,6 +2,8 @@
import
glib2, atk, pango, gdk2pixbuf, gdk2
export gbool, toBool
when defined(win32):
const
lib = "libgtk-win32-2.0-0.dll"

View file

@ -26,9 +26,9 @@ import
proc pushstring*(L: PState, s: string)
# compatibilty macros
proc getn*(L: PState, n: int): int
proc getn*(L: PState, n: cint): cint
# calls lua_objlen
proc setn*(L: PState, t, n: int)
proc setn*(L: PState, t, n: cint)
# does nothing!
type
Treg*{.final.} = object
@ -37,58 +37,45 @@ type
Preg* = ptr Treg
proc openlib*(L: PState, libname: cstring, lr: Preg, nup: int){.cdecl,
dynlib: lua.LIB_NAME, importc: "luaL_openlib".}
proc register*(L: PState, libname: cstring, lr: Preg){.cdecl,
dynlib: lua.LIB_NAME, importc: "luaL_register".}
proc getmetafield*(L: PState, obj: int, e: cstring): int{.cdecl,
dynlib: lua.LIB_NAME, importc: "luaL_getmetafield".}
proc callmeta*(L: PState, obj: int, e: cstring): int{.cdecl,
dynlib: LIB_NAME, importc: "luaL_callmeta".}
proc typerror*(L: PState, narg: int, tname: cstring): int{.cdecl,
dynlib: LIB_NAME, importc: "luaL_typerror".}
proc argerror*(L: PState, numarg: int, extramsg: cstring): int{.cdecl,
dynlib: LIB_NAME, importc: "luaL_argerror".}
proc checklstring*(L: PState, numArg: int, len: ptr int): cstring{.cdecl,
dynlib: LIB_NAME, importc: "luaL_checklstring".}
proc optlstring*(L: PState, numArg: int, def: cstring, len: ptr int): cstring{.
cdecl, dynlib: LIB_NAME, importc: "luaL_optlstring".}
proc checknumber*(L: PState, numArg: int): Number{.cdecl,
dynlib: LIB_NAME, importc: "luaL_checknumber".}
proc optnumber*(L: PState, nArg: int, def: Number): Number{.cdecl,
dynlib: LIB_NAME, importc: "luaL_optnumber".}
proc checkinteger*(L: PState, numArg: int): Integer{.cdecl,
dynlib: LIB_NAME, importc: "luaL_checkinteger".}
proc optinteger*(L: PState, nArg: int, def: Integer): Integer{.
cdecl, dynlib: LIB_NAME, importc: "luaL_optinteger".}
proc checkstack*(L: PState, sz: int, msg: cstring){.cdecl,
dynlib: LIB_NAME, importc: "luaL_checkstack".}
proc checktype*(L: PState, narg, t: int){.cdecl, dynlib: LIB_NAME,
importc: "luaL_checktype".}
proc checkany*(L: PState, narg: int){.cdecl, dynlib: LIB_NAME,
importc: "luaL_checkany".}
proc newmetatable*(L: PState, tname: cstring): int{.cdecl,
dynlib: LIB_NAME, importc: "luaL_newmetatable".}
proc checkudata*(L: PState, ud: int, tname: cstring): Pointer{.cdecl,
dynlib: LIB_NAME, importc: "luaL_checkudata".}
proc where*(L: PState, lvl: int){.cdecl, dynlib: LIB_NAME,
importc: "luaL_where".}
proc error*(L: PState, fmt: cstring): int{.cdecl, varargs,
dynlib: LIB_NAME, importc: "luaL_error".}
proc checkoption*(L: PState, narg: int, def: cstring, lst: cstringArray): int{.
cdecl, dynlib: LIB_NAME, importc: "luaL_checkoption".}
proc reference*(L: PState, t: int): int{.cdecl, dynlib: LIB_NAME,
importc: "luaL_ref".}
proc unref*(L: PState, t, theref: int){.cdecl, dynlib: LIB_NAME,
importc: "luaL_unref".}
proc loadfile*(L: PState, filename: cstring): int{.cdecl,
dynlib: LIB_NAME, importc: "luaL_loadfile".}
proc loadbuffer*(L: PState, buff: cstring, size: int, name: cstring): int{.
cdecl, dynlib: LIB_NAME, importc: "luaL_loadbuffer".}
proc loadstring*(L: PState, s: cstring): int{.cdecl, dynlib: LIB_NAME,
importc: "luaL_loadstring".}
proc newstate*(): PState{.cdecl, dynlib: LIB_NAME,
importc: "luaL_newstate".}
{.push callConv: cdecl, dynlib: lua.LIB_NAME.}
{.push importc: "luaL_$1".}
proc openlib*(L: PState, libname: cstring, lr: Preg, nup: cint)
proc register*(L: PState, libname: cstring, lr: Preg)
proc getmetafield*(L: PState, obj: cint, e: cstring): cint
proc callmeta*(L: PState, obj: cint, e: cstring): cint
proc typerror*(L: PState, narg: cint, tname: cstring): cint
proc argerror*(L: PState, numarg: cint, extramsg: cstring): cint
proc checklstring*(L: PState, numArg: cint, len: ptr int): cstring
proc optlstring*(L: PState, numArg: cint, def: cstring, len: ptr cint): cstring
proc checknumber*(L: PState, numArg: cint): Number
proc optnumber*(L: PState, nArg: cint, def: Number): Number
proc checkinteger*(L: PState, numArg: cint): Integer
proc optinteger*(L: PState, nArg: cint, def: Integer): Integer
proc checkstack*(L: PState, sz: cint, msg: cstring)
proc checktype*(L: PState, narg, t: cint)
proc checkany*(L: PState, narg: cint)
proc newmetatable*(L: PState, tname: cstring): cint
proc checkudata*(L: PState, ud: cint, tname: cstring): Pointer
proc where*(L: PState, lvl: cint)
proc error*(L: PState, fmt: cstring): cint{.varargs.}
proc checkoption*(L: PState, narg: cint, def: cstring, lst: cstringArray): cint
proc unref*(L: PState, t, theref: cint)
proc loadfile*(L: PState, filename: cstring): cint
proc loadbuffer*(L: PState, buff: cstring, size: cint, name: cstring): cint
proc loadstring*(L: PState, s: cstring): cint
proc newstate*(): PState
{.pop.}
proc reference*(L: PState, t: cint): cint{.importc: "luaL_ref".}
{.pop.}
proc open*(): PState
# compatibility; moved from unit lua to lauxlib because it needs luaL_newstate
#
@ -96,18 +83,18 @@ proc open*(): PState
#** some useful macros
#** ===============================================================
#
proc argcheck*(L: PState, cond: bool, numarg: int, extramsg: cstring)
proc checkstring*(L: PState, n: int): cstring
proc optstring*(L: PState, n: int, d: cstring): cstring
proc checkint*(L: PState, n: int): int
proc checklong*(L: PState, n: int): int32
proc optint*(L: PState, n: int, d: float64): int
proc optlong*(L: PState, n: int, d: float64): int32
proc dofile*(L: PState, filename: cstring): int
proc dostring*(L: PState, str: cstring): int
proc argcheck*(L: PState, cond: bool, numarg: cint, extramsg: cstring)
proc checkstring*(L: PState, n: cint): cstring
proc optstring*(L: PState, n: cint, d: cstring): cstring
proc checkint*(L: PState, n: cint): cint
proc checklong*(L: PState, n: cint): clong
proc optint*(L: PState, n: cint, d: float64): cint
proc optlong*(L: PState, n: cint, d: float64): clong
proc dofile*(L: PState, filename: cstring): cint
proc dostring*(L: PState, str: cstring): cint
proc getmetatable*(L: PState, tname: cstring)
# not translated:
# #define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
# #define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
#
#** =======================================================
#** Generic Buffer manipulation
@ -119,7 +106,7 @@ const # note: this is just arbitrary, as it related to t
type
Buffer*{.final.} = object
p*: cstring # current position in buffer
lvl*: int # number of strings in the stack (level)
lvl*: cint # number of strings in the stack (level)
L*: PState
buffer*: array[0..BUFFERSIZE - 1, Char] # warning: see note above about LUAL_BUFFERSIZE
@ -130,80 +117,76 @@ proc addchar*(B: PBuffer, c: Char)
# compatibility only (alias for luaL_addchar)
proc putchar*(B: PBuffer, c: Char)
# warning: see note above about LUAL_BUFFERSIZE
proc addsize*(B: PBuffer, n: int)
proc buffinit*(L: PState, B: PBuffer){.cdecl, dynlib: LIB_NAME,
importc: "luaL_buffinit".}
proc prepbuffer*(B: PBuffer): cstring{.cdecl, dynlib: LIB_NAME,
importc: "luaL_prepbuffer".}
proc addlstring*(B: PBuffer, s: cstring, L: int){.cdecl,
dynlib: LIB_NAME, importc: "luaL_addlstring".}
proc addstring*(B: PBuffer, s: cstring){.cdecl, dynlib: LIB_NAME,
importc: "luaL_addstring".}
proc addvalue*(B: PBuffer){.cdecl, dynlib: LIB_NAME,
importc: "luaL_addvalue".}
proc pushresult*(B: PBuffer){.cdecl, dynlib: LIB_NAME,
importc: "luaL_pushresult".}
proc gsub*(L: PState, s, p, r: cstring): cstring{.cdecl,
dynlib: LIB_NAME, importc: "luaL_gsub".}
proc findtable*(L: PState, idx: int, fname: cstring, szhint: int): cstring{.
cdecl, dynlib: LIB_NAME, importc: "luaL_findtable".}
proc addsize*(B: PBuffer, n: cint)
{.push callConv: cdecl, dynlib: lua.LIB_NAME, importc: "luaL_$1".}
proc buffinit*(L: PState, B: PBuffer)
proc prepbuffer*(B: PBuffer): cstring
proc addlstring*(B: PBuffer, s: cstring, L: cint)
proc addstring*(B: PBuffer, s: cstring)
proc addvalue*(B: PBuffer)
proc pushresult*(B: PBuffer)
proc gsub*(L: PState, s, p, r: cstring): cstring
proc findtable*(L: PState, idx: cint, fname: cstring, szhint: cint): cstring
# compatibility with ref system
# pre-defined references
{.pop.}
const
NOREF* = - 2
REFNIL* = - 1
proc unref*(L: PState, theref: int)
proc getref*(L: PState, theref: int)
proc unref*(L: PState, theref: cint)
proc getref*(L: PState, theref: cint)
#
#** Compatibility macros and functions
#
# implementation
proc pushstring(L: PState, s: string) =
pushlstring(L, cstring(s), len(s))
pushlstring(L, cstring(s), s.len.cint)
proc getn(L: PState, n: int): int =
proc getn(L: PState, n: cint): cint =
Result = objlen(L, n)
proc setn(L: PState, t, n: int) =
proc setn(L: PState, t, n: cint) =
# does nothing as this operation is deprecated
nil
proc open(): PState =
Result = newstate()
proc dofile(L: PState, filename: cstring): int =
proc dofile(L: PState, filename: cstring): cint =
Result = loadfile(L, filename)
if Result == 0: Result = pcall(L, 0, MULTRET, 0)
proc dostring(L: PState, str: cstring): int =
proc dostring(L: PState, str: cstring): cint =
Result = loadstring(L, str)
if Result == 0: Result = pcall(L, 0, MULTRET, 0)
proc getmetatable(L: PState, tname: cstring) =
getfield(L, REGISTRYINDEX, tname)
proc argcheck(L: PState, cond: bool, numarg: int, extramsg: cstring) =
proc argcheck(L: PState, cond: bool, numarg: cint, extramsg: cstring) =
if not cond:
discard argerror(L, numarg, extramsg)
proc checkstring(L: PState, n: int): cstring =
proc checkstring(L: PState, n: cint): cstring =
Result = checklstring(L, n, nil)
proc optstring(L: PState, n: int, d: cstring): cstring =
proc optstring(L: PState, n: cint, d: cstring): cstring =
Result = optlstring(L, n, d, nil)
proc checkint(L: PState, n: int): int =
Result = toInt(checknumber(L, n))
proc checkint(L: PState, n: cint): cint =
Result = cint(checknumber(L, n))
proc checklong(L: PState, n: int): int32 =
proc checklong(L: PState, n: cint): clong =
Result = int32(ToInt(checknumber(L, n)))
proc optint(L: PState, n: int, d: float64): int =
Result = int(ToInt(optnumber(L, n, d)))
proc optint(L: PState, n: cint, d: float64): cint =
Result = optnumber(L, n, d).cint
proc optlong(L: PState, n: int, d: float64): int32 =
proc optlong(L: PState, n: cint, d: float64): clong =
Result = int32(ToInt(optnumber(L, n, d)))
proc addchar(B: PBuffer, c: Char) =
@ -215,11 +198,11 @@ proc addchar(B: PBuffer, c: Char) =
proc putchar(B: PBuffer, c: Char) =
addchar(B, c)
proc addsize(B: PBuffer, n: int) =
proc addsize(B: PBuffer, n: cint) =
B.p = cast[cstring](cast[int](B.p) + n)
proc unref(L: PState, theref: int) =
proc unref(L: PState, theref: cint) =
unref(L, REGISTRYINDEX, theref)
proc getref(L: PState, theref: int) =
proc getref(L: PState, theref: cint) =
rawgeti(L, REGISTRYINDEX, theref)

View file

@ -51,16 +51,16 @@ when defined(useLuajit):
else:
when defined(MACOSX):
const
NAME* = "liblua(|5.2|5.1|5.0).dylib"
LIB_NAME* = "liblua(|5.2|5.1|5.0).dylib"
NAME* = "liblua(|5.1|5.0).dylib"
LIB_NAME* = "liblua(|5.1|5.0).dylib"
elif defined(UNIX):
const
NAME* = "liblua(|5.2|5.1|5.0).so(|.0)"
LIB_NAME* = "liblua(|5.2|5.1|5.0).so(|.0)"
NAME* = "liblua(|5.1|5.0).so(|.0)"
LIB_NAME* = "liblua(|5.1|5.0).so(|.0)"
else:
const
NAME* = "lua(|5.2|5.1|5.0).dll"
LIB_NAME* = "lua(|5.2|5.1|5.0).dll"
NAME* = "lua(|5.1|5.0).dll"
LIB_NAME* = "lua(|5.1|5.0).dll"
const
VERSION* = "Lua 5.1"
@ -76,7 +76,7 @@ const
ENVIRONINDEX* = - 10001
GLOBALSINDEX* = - 10002
proc upvalueindex*(I: int): int
proc upvalueindex*(I: cint): cint
const # thread status; 0 is OK
constYIELD* = 1
ERRRUN* = 2
@ -86,16 +86,16 @@ const # thread status; 0 is OK
type
PState* = Pointer
CFunction* = proc (L: PState): int{.cdecl.}
CFunction* = proc (L: PState): cint{.cdecl.}
#
#** functions that read/write blocks when loading/dumping Lua chunks
#
type
Reader* = proc (L: PState, ud: Pointer, sz: ptr int): cstring{.cdecl.}
Writer* = proc (L: PState, p: Pointer, sz: int, ud: Pointer): int{.cdecl.}
Alloc* = proc (ud, theptr: Pointer, osize, nsize: int){.cdecl.}
Reader* = proc (L: PState, ud: Pointer, sz: ptr cint): cstring{.cdecl.}
Writer* = proc (L: PState, p: Pointer, sz: cint, ud: Pointer): cint{.cdecl.}
Alloc* = proc (ud, theptr: Pointer, osize, nsize: cint){.cdecl.}
const
TNONE* = - 1
@ -112,130 +112,86 @@ const
type # Type of Numbers in Lua
Number* = float
Integer* = int
Integer* = cint
{.pragma: ilua, importc: "lua_$1".}
{.push callConv: cdecl, dynlib: LibName.}
#{.push importc: "lua_$1".}
proc newstate*(f: Alloc, ud: Pointer): PState {.ilua.}
proc close*(L: PState){.ilua.}
proc newthread*(L: PState): PState{.ilua.}
proc atpanic*(L: PState, panicf: CFunction): CFunction{.ilua.}
proc gettop*(L: PState): cint{.ilua.}
proc settop*(L: PState, idx: cint){.ilua.}
proc pushvalue*(L: PState, Idx: cint){.ilua.}
proc remove*(L: PState, idx: cint){.ilua.}
proc insert*(L: PState, idx: cint){.ilua.}
proc replace*(L: PState, idx: cint){.ilua.}
proc checkstack*(L: PState, sz: cint): cint{.ilua.}
proc xmove*(`from`, `to`: PState, n: cint){.ilua.}
proc isnumber*(L: PState, idx: cint): cint{.ilua.}
proc isstring*(L: PState, idx: cint): cint{.ilua.}
proc iscfunction*(L: PState, idx: cint): cint{.ilua.}
proc isuserdata*(L: PState, idx: cint): cint{.ilua.}
proc luatype*(L: PState, idx: cint): cint{.importc: "lua_type".}
proc typename*(L: PState, tp: cint): cstring{.ilua.}
proc equal*(L: PState, idx1, idx2: cint): cint{.ilua.}
proc rawequal*(L: PState, idx1, idx2: cint): cint{.ilua.}
proc lessthan*(L: PState, idx1, idx2: cint): cint{.ilua.}
proc tonumber*(L: PState, idx: cint): Number{.ilua.}
proc tointeger*(L: PState, idx: cint): Integer{.ilua.}
proc toboolean*(L: PState, idx: cint): cint{.ilua.}
proc tolstring*(L: PState, idx: cint, length: ptr cint): cstring{.ilua.}
proc objlen*(L: PState, idx: cint): cint{.ilua.}
proc tocfunction*(L: PState, idx: cint): CFunction{.ilua.}
proc touserdata*(L: PState, idx: cint): Pointer{.ilua.}
proc tothread*(L: PState, idx: cint): PState{.ilua.}
proc topointer*(L: PState, idx: cint): Pointer{.ilua.}
proc pushnil*(L: PState){.ilua.}
proc pushnumber*(L: PState, n: Number){.ilua.}
proc pushinteger*(L: PState, n: Integer){.ilua.}
proc pushlstring*(L: PState, s: cstring, len: cint){.ilua.}
proc pushstring*(L: PState, s: cstring){.ilua.}
proc pushvfstring*(L: PState, fmt: cstring, argp: Pointer): cstring{.ilua.}
proc pushfstring*(L: PState, fmt: cstring): cstring{.varargs,ilua.}
proc pushcclosure*(L: PState, fn: CFunction, n: cint){.ilua.}
proc pushboolean*(L: PState, b: cint){.ilua.}
proc pushlightuserdata*(L: PState, p: Pointer){.ilua.}
proc pushthread*(L: PState){.ilua.}
proc gettable*(L: PState, idx: cint){.ilua.}
proc getfield*(L: Pstate, idx: cint, k: cstring){.ilua.}
proc rawget*(L: PState, idx: cint){.ilua.}
proc rawgeti*(L: PState, idx, n: cint){.ilua.}
proc createtable*(L: PState, narr, nrec: cint){.ilua.}
proc newuserdata*(L: PState, sz: cint): Pointer{.ilua.}
proc getmetatable*(L: PState, objindex: cint): cint{.ilua.}
proc getfenv*(L: PState, idx: cint){.ilua.}
proc settable*(L: PState, idx: cint){.ilua.}
proc setfield*(L: PState, idx: cint, k: cstring){.ilua.}
proc rawset*(L: PState, idx: cint){.ilua.}
proc rawseti*(L: PState, idx, n: cint){.ilua.}
proc setmetatable*(L: PState, objindex: cint): cint{.ilua.}
proc setfenv*(L: PState, idx: cint): cint{.ilua.}
proc call*(L: PState, nargs, nresults: cint){.ilua.}
proc pcall*(L: PState, nargs, nresults, errf: cint): cint{.ilua.}
proc cpcall*(L: PState, func: CFunction, ud: Pointer): cint{.ilua.}
proc load*(L: PState, reader: Reader, dt: Pointer, chunkname: cstring): cint{.ilua.}
proc dump*(L: PState, writer: Writer, data: Pointer): cint{.ilua.}
proc luayield*(L: PState, nresults: cint): cint{.importc: "lua_yield".}
proc resume*(L: PState, narg: cint): cint{.ilua.}
proc status*(L: PState): cint{.ilua.}
proc gc*(L: PState, what, data: cint): cint{.ilua.}
proc error*(L: PState): cint{.ilua.}
proc next*(L: PState, idx: cint): cint{.ilua.}
proc concat*(L: PState, n: cint){.ilua.}
proc getallocf*(L: PState, ud: ptr Pointer): Alloc{.ilua.}
proc setallocf*(L: PState, f: Alloc, ud: Pointer){.ilua.}
{.pop.}
proc newstate*(f: Alloc, ud: Pointer): PState{.cdecl, dynlib: NAME,
importc: "lua_newstate".}
proc close*(L: PState){.cdecl, dynlib: NAME, importc: "lua_close".}
proc newthread*(L: PState): PState{.cdecl, dynlib: NAME,
importc: "lua_newthread".}
proc atpanic*(L: PState, panicf: CFunction): CFunction{.cdecl, dynlib: NAME,
importc: "lua_atpanic".}
proc gettop*(L: PState): int{.cdecl, dynlib: NAME, importc: "lua_gettop".}
proc settop*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_settop".}
proc pushvalue*(L: PState, Idx: int){.cdecl, dynlib: NAME,
importc: "lua_pushvalue".}
proc remove*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_remove".}
proc insert*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_insert".}
proc replace*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_replace".}
proc checkstack*(L: PState, sz: int): cint{.cdecl, dynlib: NAME,
importc: "lua_checkstack".}
proc xmove*(`from`, `to`: PState, n: int){.cdecl, dynlib: NAME,
importc: "lua_xmove".}
proc isnumber*(L: PState, idx: int): cint{.cdecl, dynlib: NAME,
importc: "lua_isnumber".}
proc isstring*(L: PState, idx: int): cint{.cdecl, dynlib: NAME,
importc: "lua_isstring".}
proc iscfunction*(L: PState, idx: int): cint{.cdecl, dynlib: NAME,
importc: "lua_iscfunction".}
proc isuserdata*(L: PState, idx: int): cint{.cdecl, dynlib: NAME,
importc: "lua_isuserdata".}
proc luatype*(L: PState, idx: int): int{.cdecl, dynlib: NAME, importc: "lua_type".}
proc typename*(L: PState, tp: int): cstring{.cdecl, dynlib: NAME,
importc: "lua_typename".}
proc equal*(L: PState, idx1, idx2: int): cint{.cdecl, dynlib: NAME,
importc: "lua_equal".}
proc rawequal*(L: PState, idx1, idx2: int): cint{.cdecl, dynlib: NAME,
importc: "lua_rawequal".}
proc lessthan*(L: PState, idx1, idx2: int): cint{.cdecl, dynlib: NAME,
importc: "lua_lessthan".}
proc tonumber*(L: PState, idx: int): Number{.cdecl, dynlib: NAME,
importc: "lua_tonumber".}
proc tointeger*(L: PState, idx: int): Integer{.cdecl, dynlib: NAME,
importc: "lua_tointeger".}
proc toboolean*(L: PState, idx: int): cint{.cdecl, dynlib: NAME,
importc: "lua_toboolean".}
proc tolstring*(L: PState, idx: int, length: ptr int): cstring{.cdecl,
dynlib: NAME, importc: "lua_tolstring".}
proc objlen*(L: PState, idx: int): int{.cdecl, dynlib: NAME,
importc: "lua_objlen".}
proc tocfunction*(L: PState, idx: int): CFunction{.cdecl, dynlib: NAME,
importc: "lua_tocfunction".}
proc touserdata*(L: PState, idx: int): Pointer{.cdecl, dynlib: NAME,
importc: "lua_touserdata".}
proc tothread*(L: PState, idx: int): PState{.cdecl, dynlib: NAME,
importc: "lua_tothread".}
proc topointer*(L: PState, idx: int): Pointer{.cdecl, dynlib: NAME,
importc: "lua_topointer".}
proc pushnil*(L: PState){.cdecl, dynlib: NAME, importc: "lua_pushnil".}
proc pushnumber*(L: PState, n: Number){.cdecl, dynlib: NAME,
importc: "lua_pushnumber".}
proc pushinteger*(L: PState, n: Integer){.cdecl, dynlib: NAME,
importc: "lua_pushinteger".}
proc pushlstring*(L: PState, s: cstring, len: int){.cdecl, dynlib: NAME,
importc: "lua_pushlstring".}
proc pushstring*(L: PState, s: cstring){.cdecl, dynlib: NAME,
importc: "lua_pushstring".}
proc pushvfstring*(L: PState, fmt: cstring, argp: Pointer): cstring{.cdecl,
dynlib: NAME, importc: "lua_pushvfstring".}
proc pushfstring*(L: PState, fmt: cstring): cstring{.cdecl, varargs,
dynlib: NAME, importc: "lua_pushfstring".}
proc pushcclosure*(L: PState, fn: CFunction, n: int){.cdecl, dynlib: NAME,
importc: "lua_pushcclosure".}
proc pushboolean*(L: PState, b: cint){.cdecl, dynlib: NAME,
importc: "lua_pushboolean".}
proc pushlightuserdata*(L: PState, p: Pointer){.cdecl, dynlib: NAME,
importc: "lua_pushlightuserdata".}
proc pushthread*(L: PState){.cdecl, dynlib: NAME, importc: "lua_pushthread".}
proc gettable*(L: PState, idx: int){.cdecl, dynlib: NAME,
importc: "lua_gettable".}
proc getfield*(L: Pstate, idx: int, k: cstring){.cdecl, dynlib: NAME,
importc: "lua_getfield".}
proc rawget*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_rawget".}
proc rawgeti*(L: PState, idx, n: int){.cdecl, dynlib: NAME,
importc: "lua_rawgeti".}
proc createtable*(L: PState, narr, nrec: int){.cdecl, dynlib: NAME,
importc: "lua_createtable".}
proc newuserdata*(L: PState, sz: int): Pointer{.cdecl, dynlib: NAME,
importc: "lua_newuserdata".}
proc getmetatable*(L: PState, objindex: int): int{.cdecl, dynlib: NAME,
importc: "lua_getmetatable".}
proc getfenv*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_getfenv".}
proc settable*(L: PState, idx: int){.cdecl, dynlib: NAME,
importc: "lua_settable".}
proc setfield*(L: PState, idx: int, k: cstring){.cdecl, dynlib: NAME,
importc: "lua_setfield".}
proc rawset*(L: PState, idx: int){.cdecl, dynlib: NAME, importc: "lua_rawset".}
proc rawseti*(L: PState, idx, n: int){.cdecl, dynlib: NAME,
importc: "lua_rawseti".}
proc setmetatable*(L: PState, objindex: int): int{.cdecl, dynlib: NAME,
importc: "lua_setmetatable".}
proc setfenv*(L: PState, idx: int): int{.cdecl, dynlib: NAME,
importc: "lua_setfenv".}
proc call*(L: PState, nargs, nresults: int){.cdecl, dynlib: NAME,
importc: "lua_call".}
proc pcall*(L: PState, nargs, nresults, errf: int): int{.cdecl, dynlib: NAME,
importc: "lua_pcall".}
proc cpcall*(L: PState, func: CFunction, ud: Pointer): int{.cdecl, dynlib: NAME,
importc: "lua_cpcall".}
proc load*(L: PState, reader: Reader, dt: Pointer, chunkname: cstring): int{.
cdecl, dynlib: NAME, importc: "lua_load".}
proc dump*(L: PState, writer: Writer, data: Pointer): int{.cdecl, dynlib: NAME,
importc: "lua_dump".}
proc luayield*(L: PState, nresults: int): int{.cdecl, dynlib: NAME,
importc: "lua_yield".}
proc resume*(L: PState, narg: int): int{.cdecl, dynlib: NAME,
importc: "lua_resume".}
proc status*(L: PState): int{.cdecl, dynlib: NAME, importc: "lua_status".}
proc gc*(L: PState, what, data: int): int{.cdecl, dynlib: NAME,
importc: "lua_gc".}
proc error*(L: PState): int{.cdecl, dynlib: NAME, importc: "lua_error".}
proc next*(L: PState, idx: int): int{.cdecl, dynlib: NAME, importc: "lua_next".}
proc concat*(L: PState, n: int){.cdecl, dynlib: NAME, importc: "lua_concat".}
proc getallocf*(L: PState, ud: ptr Pointer): Alloc{.cdecl, dynlib: NAME,
importc: "lua_getallocf".}
proc setallocf*(L: PState, f: Alloc, ud: Pointer){.cdecl, dynlib: NAME,
importc: "lua_setallocf".}
#
#** Garbage-collection functions and options
#
@ -256,29 +212,29 @@ const
#** ===============================================================
#
proc pop*(L: PState, n: int)
proc pop*(L: PState, n: cint)
proc newtable*(L: Pstate)
proc register*(L: PState, n: cstring, f: CFunction)
proc pushcfunction*(L: PState, f: CFunction)
proc strlen*(L: Pstate, i: int): int
proc isfunction*(L: PState, n: int): bool
proc istable*(L: PState, n: int): bool
proc islightuserdata*(L: PState, n: int): bool
proc isnil*(L: PState, n: int): bool
proc isboolean*(L: PState, n: int): bool
proc isthread*(L: PState, n: int): bool
proc isnone*(L: PState, n: int): bool
proc isnoneornil*(L: PState, n: int): bool
proc strlen*(L: Pstate, i: cint): cint
proc isfunction*(L: PState, n: cint): bool
proc istable*(L: PState, n: cint): bool
proc islightuserdata*(L: PState, n: cint): bool
proc isnil*(L: PState, n: cint): bool
proc isboolean*(L: PState, n: cint): bool
proc isthread*(L: PState, n: cint): bool
proc isnone*(L: PState, n: cint): bool
proc isnoneornil*(L: PState, n: cint): bool
proc pushliteral*(L: PState, s: cstring)
proc setglobal*(L: PState, s: cstring)
proc getglobal*(L: PState, s: cstring)
proc tostring*(L: PState, i: int): cstring
proc tostring*(L: PState, i: cint): cstring
#
#** compatibility macros and functions
#
proc getregistry*(L: PState)
proc getgccount*(L: PState): int
proc getgccount*(L: PState): cint
type
Chunkreader* = Reader
Chunkwriter* = Writer
@ -307,18 +263,18 @@ const
type
TDebug*{.final.} = object # activation record
event*: int
event*: cint
name*: cstring # (n)
namewhat*: cstring # (n) `global', `local', `field', `method'
what*: cstring # (S) `Lua', `C', `main', `tail'
source*: cstring # (S)
currentline*: int # (l)
nups*: int # (u) number of upvalues
linedefined*: int # (S)
lastlinedefined*: int # (S)
short_src*: array[0..IDSIZE - 1, Char] # (S)
# private part
i_ci*: int # active function
currentline*: cint # (l)
nups*: cint # (u) number of upvalues
linedefined*: cint # (S)
lastlinedefined*: cint # (S)
short_src*: array[0.. <IDSIZE, Char] # (S) \
# private part
i_ci*: cint # active function
PDebug* = ptr TDebug
Hook* = proc (L: PState, ar: PDebug){.cdecl.}
@ -329,31 +285,27 @@ type
#** ======================================================================
#
proc getstack*(L: PState, level: int, ar: PDebug): int{.cdecl, dynlib: NAME,
importc: "lua_getstack".}
proc getinfo*(L: PState, what: cstring, ar: PDebug): int{.cdecl, dynlib: NAME,
importc: "lua_getinfo".}
proc getlocal*(L: PState, ar: PDebug, n: int): cstring{.cdecl, dynlib: NAME,
importc: "lua_getlocal".}
proc setlocal*(L: PState, ar: PDebug, n: int): cstring{.cdecl, dynlib: NAME,
importc: "lua_setlocal".}
proc getupvalue*(L: PState, funcindex: int, n: int): cstring{.cdecl,
dynlib: NAME, importc: "lua_getupvalue".}
proc setupvalue*(L: PState, funcindex: int, n: int): cstring{.cdecl,
dynlib: NAME, importc: "lua_setupvalue".}
proc sethook*(L: PState, func: Hook, mask: int, count: int): int{.cdecl,
dynlib: NAME, importc: "lua_sethook".}
proc gethook*(L: PState): Hook{.cdecl, dynlib: NAME, importc: "lua_gethook".}
proc gethookmask*(L: PState): int{.cdecl, dynlib: NAME,
importc: "lua_gethookmask".}
proc gethookcount*(L: PState): int{.cdecl, dynlib: NAME,
importc: "lua_gethookcount".}
{.push callConv: cdecl, dynlib: lua.LIB_NAME.}
proc getstack*(L: PState, level: cint, ar: PDebug): cint{.ilua.}
proc getinfo*(L: PState, what: cstring, ar: PDebug): cint{.ilua.}
proc getlocal*(L: PState, ar: PDebug, n: cint): cstring{.ilua.}
proc setlocal*(L: PState, ar: PDebug, n: cint): cstring{.ilua.}
proc getupvalue*(L: PState, funcindex: cint, n: cint): cstring{.ilua.}
proc setupvalue*(L: PState, funcindex: cint, n: cint): cstring{.ilua.}
proc sethook*(L: PState, func: Hook, mask: cint, count: cint): cint{.ilua.}
proc gethook*(L: PState): Hook{.ilua.}
proc gethookmask*(L: PState): cint{.ilua.}
proc gethookcount*(L: PState): cint{.ilua.}
{.pop.}
# implementation
proc upvalueindex(I: int): int =
proc upvalueindex(I: cint): cint =
Result = GLOBALSINDEX - i
proc pop(L: PState, n: int) =
proc pop(L: PState, n: cint) =
settop(L, - n - 1)
proc newtable(L: PState) =
@ -366,35 +318,35 @@ proc register(L: PState, n: cstring, f: CFunction) =
proc pushcfunction(L: PState, f: CFunction) =
pushcclosure(L, f, 0)
proc strlen(L: PState, i: int): int =
proc strlen(L: PState, i: cint): cint =
Result = objlen(L, i)
proc isfunction(L: PState, n: int): bool =
proc isfunction(L: PState, n: cint): bool =
Result = luatype(L, n) == TFUNCTION
proc istable(L: PState, n: int): bool =
proc istable(L: PState, n: cint): bool =
Result = luatype(L, n) == TTABLE
proc islightuserdata(L: PState, n: int): bool =
proc islightuserdata(L: PState, n: cint): bool =
Result = luatype(L, n) == TLIGHTUSERDATA
proc isnil(L: PState, n: int): bool =
proc isnil(L: PState, n: cint): bool =
Result = luatype(L, n) == TNIL
proc isboolean(L: PState, n: int): bool =
proc isboolean(L: PState, n: cint): bool =
Result = luatype(L, n) == TBOOLEAN
proc isthread(L: PState, n: int): bool =
proc isthread(L: PState, n: cint): bool =
Result = luatype(L, n) == TTHREAD
proc isnone(L: PState, n: int): bool =
proc isnone(L: PState, n: cint): bool =
Result = luatype(L, n) == TNONE
proc isnoneornil(L: PState, n: int): bool =
proc isnoneornil(L: PState, n: cint): bool =
Result = luatype(L, n) <= 0
proc pushliteral(L: PState, s: cstring) =
pushlstring(L, s, len(s))
pushlstring(L, s, s.len.cint)
proc setglobal(L: PState, s: cstring) =
setfield(L, GLOBALSINDEX, s)
@ -402,11 +354,11 @@ proc setglobal(L: PState, s: cstring) =
proc getglobal(L: PState, s: cstring) =
getfield(L, GLOBALSINDEX, s)
proc tostring(L: PState, i: int): cstring =
proc tostring(L: PState, i: cint): cstring =
Result = tolstring(L, i, nil)
proc getregistry(L: PState) =
pushvalue(L, REGISTRYINDEX)
proc getgccount(L: PState): int =
proc getgccount(L: PState): cint =
Result = gc(L, GCCOUNT, 0)

View file

@ -32,35 +32,33 @@ const
DBLIBNAME* = "debug"
LOADLIBNAME* = "package"
proc open_base*(L: PState): cint{.cdecl, dynlib: LIB_NAME,
importc: "luaopen_base".}
proc open_table*(L: PState): cint{.cdecl, dynlib: LIB_NAME,
importc: "luaopen_table".}
proc open_io*(L: PState): cint{.cdecl, dynlib: LIB_NAME, importc: "luaopen_io".}
proc open_string*(L: PState): cint{.cdecl, dynlib: LIB_NAME,
importc: "luaopen_string".}
proc open_math*(L: PState): cint{.cdecl, dynlib: LIB_NAME,
importc: "luaopen_math".}
proc open_debug*(L: PState): cint{.cdecl, dynlib: LIB_NAME,
importc: "luaopen_debug".}
proc open_package*(L: PState): cint{.cdecl, dynlib: LIB_NAME,
importc: "luaopen_package".}
proc openlibs*(L: PState){.cdecl, dynlib: LIB_NAME, importc: "luaL_openlibs".}
{.pragma: ilua, importc: "lua$1".}
{.push callConv: cdecl, dynlib: lua.LIB_NAME.}
proc open_base*(L: PState): cint{.ilua.}
proc open_table*(L: PState): cint{.ilua.}
proc open_io*(L: PState): cint{.ilua.}
proc open_string*(L: PState): cint{.ilua.}
proc open_math*(L: PState): cint{.ilua.}
proc open_debug*(L: PState): cint{.ilua.}
proc open_package*(L: PState): cint{.ilua.}
proc openlibs*(L: PState){.importc: "luaL_openlibs".}
{.pop.}
proc baselibopen*(L: PState): Bool =
Result = open_base(L) != 0'i32
open_base(L) != 0'i32
proc tablibopen*(L: PState): Bool =
Result = open_table(L) != 0'i32
open_table(L) != 0'i32
proc iolibopen*(L: PState): Bool =
Result = open_io(L) != 0'i32
open_io(L) != 0'i32
proc strlibopen*(L: PState): Bool =
Result = open_string(L) != 0'i32
open_string(L) != 0'i32
proc mathlibopen*(L: PState): Bool =
Result = open_math(L) != 0'i32
open_math(L) != 0'i32
proc dblibopen*(L: PState): Bool =
Result = open_debug(L) != 0'i32
open_debug(L) != 0'i32

View file

@ -196,7 +196,7 @@ const
proc SSL_library_init*(): cInt{.cdecl, dynlib: DLLSSLName, importc, discardable.}
proc SSL_load_error_strings*(){.cdecl, dynlib: DLLSSLName, importc.}
proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLSSLName, importc.}
proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.}
proc SSLv23_client_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.}
proc SSLv23_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.}
@ -262,14 +262,15 @@ proc ERR_error_string*(e: cInt, buf: cstring): cstring{.cdecl,
proc ERR_get_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.}
proc ERR_peek_last_error*(): cInt{.cdecl, dynlib: DLLUtilName, importc.}
proc OpenSSL_add_all_algorithms*(){.cdecl, dynlib: DLLSSLName, importc: "OPENSSL_add_all_algorithms_conf".}
proc OpenSSL_add_all_algorithms*(){.cdecl, dynlib: DLLUtilName, importc: "OPENSSL_add_all_algorithms_conf".}
proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.}
proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, dynlib: DLLSSLName, importc.}
proc CRYPTO_malloc_init*() =
CRYPTO_set_mem_functions(alloc, realloc, dealloc)
when not defined(windows):
CRYPTO_set_mem_functions(alloc, realloc, dealloc)
when True:
nil

View file

@ -156,33 +156,34 @@ const
type
Thook_func* = proc (a2: cstring): cstring{.cdecl.}
# If non-null, this contains the address of a function that the application
# wants called before trying the standard tilde expansions. The function
# is called with the text sans tilde, and returns a malloc()'ed string
# which is the expansion, or a NULL pointer if the expansion fails.
when not defined(macosx):
# If non-null, this contains the address of a function that the application
# wants called before trying the standard tilde expansions. The function
# is called with the text sans tilde, and returns a malloc()'ed string
# which is the expansion, or a NULL pointer if the expansion fails.
var expansion_preexpansion_hook*{.importc: "tilde_expansion_preexpansion_hook",
dynlib: tildeDll.}: Thook_func
var expansion_preexpansion_hook*{.importc: "tilde_expansion_preexpansion_hook",
dynlib: tildeDll.}: Thook_func
# If non-null, this contains the address of a function to call if the
# standard meaning for expanding a tilde fails. The function is called
# with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
# which is the expansion, or a NULL pointer if there is no expansion.
# If non-null, this contains the address of a function to call if the
# standard meaning for expanding a tilde fails. The function is called
# with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
# which is the expansion, or a NULL pointer if there is no expansion.
var expansion_failure_hook*{.importc: "tilde_expansion_failure_hook",
dynlib: tildeDll.}: Thook_func
var expansion_failure_hook*{.importc: "tilde_expansion_failure_hook",
dynlib: tildeDll.}: Thook_func
# When non-null, this is a NULL terminated array of strings which
# are duplicates for a tilde prefix. Bash uses this to expand
# `=~' and `:~'.
# When non-null, this is a NULL terminated array of strings which
# are duplicates for a tilde prefix. Bash uses this to expand
# `=~' and `:~'.
var additional_prefixes*{.importc: "tilde_additional_prefixes", dynlib: tildeDll.}: cstringArray
var additional_prefixes*{.importc: "tilde_additional_prefixes", dynlib: tildeDll.}: cstringArray
# When non-null, this is a NULL terminated array of strings which match
# the end of a username, instead of just "/". Bash sets this to
# `:' and `=~'.
# When non-null, this is a NULL terminated array of strings which match
# the end of a username, instead of just "/". Bash sets this to
# `:' and `=~'.
var additional_suffixes*{.importc: "tilde_additional_suffixes", dynlib: tildeDll.}: cstringArray
var additional_suffixes*{.importc: "tilde_additional_suffixes", dynlib: tildeDll.}: cstringArray
# Return a new string which is the result of tilde expanding STRING.
@ -229,7 +230,8 @@ type
# The current undo list for RL_LINE_BUFFER.
var undo_list*{.importc: "rl_undo_list", dynlib: readlineDll.}: ptr TUNDO_LIST
when not defined(macosx):
var undo_list*{.importc: "rl_undo_list", dynlib: readlineDll.}: ptr TUNDO_LIST
# The data structure for mapping textual names to code addresses.
@ -239,7 +241,8 @@ type
function*: TCommandFunc
var funmap*{.importc: "funmap", dynlib: readlineDll.}: ptr ptr TFUNMAP
when not defined(macosx):
var funmap*{.importc: "funmap", dynlib: readlineDll.}: ptr ptr TFUNMAP
# ****************************************************************
#

View file

@ -1,16 +1,16 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Interface to the `libzip <http://www.nih.at/libzip/index.html>`_ library by
## Dieter Baron and Thomas Klausner. However, this does not need any external
## library (DLL, lib*.so), as the source for this library is included and
## compiled with this interface.
## Dieter Baron and Thomas Klausner. This version links
## against ``libzip2.so.2`` unless you define the symbol ``useLibzipSrc``; then
## it is compiled against some old ``libizp_all.c`` file.
#
# zip.h -- exported declarations.
@ -47,9 +47,16 @@
import times
{.compile: "libzip_all.c".}
when defined(unix):
{.passl: "-lz".}
when defined(unix) and not defined(useLibzipSrc):
when defined(macosx):
{.pragma: mydll, dynlib: "libzip2.dylib".}
else:
{.pragma: mydll, dynlib: "libzip2.so(|.2|.1|.0)".}
else:
when defined(unix):
{.passl: "-lz".}
{.compile: "libzip_all.c".}
{.pragma: mydll.}
type
Tzip_source_cmd* = int32
@ -57,7 +64,7 @@ type
Tzip_source_callback* = proc (state: pointer, data: pointer, length: int,
cmd: Tzip_source_cmd): int {.cdecl.}
Pzip_stat* = ptr Tzip_stat
Tzip_stat* {.final, pure.} = object
Tzip_stat* = object ## the 'zip_stat' struct
name*: cstring ## name of the file
index*: int32 ## index within archive
crc*: int32 ## crc of file data
@ -67,9 +74,9 @@ type
comp_method*: int16 ## compression method used
encryption_method*: int16 ## encryption method used
Tzip {.final, pure.} = object
Tzip_source {.final, pure.} = object
Tzip_file {.final, pure.} = object
Tzip = object
Tzip_source = object
Tzip_file = object
Pzip* = ptr Tzip ## represents a zip archive
Pzip_file* = ptr Tzip_file ## represents a file within an archive
@ -160,82 +167,83 @@ const
constZIP_SOURCE_FREE* = 5'i32 ## cleanup and free resources
proc zip_add*(para1: Pzip, para2: cstring, para3: Pzip_source): int32 {.cdecl,
importc: "zip_add".}
importc: "zip_add", mydll.}
proc zip_add_dir*(para1: Pzip, para2: cstring): int32 {.cdecl,
importc: "zip_add_dir".}
proc zip_close*(para1: Pzip) {.cdecl, importc: "zip_close".}
proc zip_delete*(para1: Pzip, para2: int32): int32 {.cdecl,
importc: "zip_add_dir", mydll.}
proc zip_close*(para1: Pzip) {.cdecl, importc: "zip_close", mydll.}
proc zip_delete*(para1: Pzip, para2: int32): int32 {.cdecl, mydll,
importc: "zip_delete".}
proc zip_error_clear*(para1: Pzip) {.cdecl, importc: "zip_error_clear".}
proc zip_error_clear*(para1: Pzip) {.cdecl, importc: "zip_error_clear", mydll.}
proc zip_error_get*(para1: Pzip, para2: ptr int32, para3: ptr int32) {.cdecl,
importc: "zip_error_get".}
proc zip_error_get_sys_type*(para1: int32): int32 {.cdecl,
importc: "zip_error_get", mydll.}
proc zip_error_get_sys_type*(para1: int32): int32 {.cdecl, mydll,
importc: "zip_error_get_sys_type".}
proc zip_error_to_str*(para1: cstring, para2: int, para3: int32,
para4: int32): int32 {.cdecl,
para4: int32): int32 {.cdecl, mydll,
importc: "zip_error_to_str".}
proc zip_fclose*(para1: Pzip_file) {.cdecl,
proc zip_fclose*(para1: Pzip_file) {.cdecl, mydll,
importc: "zip_fclose".}
proc zip_file_error_clear*(para1: Pzip_file) {.cdecl,
proc zip_file_error_clear*(para1: Pzip_file) {.cdecl, mydll,
importc: "zip_file_error_clear".}
proc zip_file_error_get*(para1: Pzip_file, para2: ptr int32, para3: ptr int32) {.
cdecl, importc: "zip_file_error_get".}
proc zip_file_strerror*(para1: Pzip_file): cstring {.cdecl,
cdecl, mydll, importc: "zip_file_error_get".}
proc zip_file_strerror*(para1: Pzip_file): cstring {.cdecl, mydll,
importc: "zip_file_strerror".}
proc zip_fopen*(para1: Pzip, para2: cstring, para3: int32): Pzip_file {.cdecl,
importc: "zip_fopen".}
mydll, importc: "zip_fopen".}
proc zip_fopen_index*(para1: Pzip, para2: int32, para3: int32): Pzip_file {.
cdecl, importc: "zip_fopen_index".}
cdecl, mydll, importc: "zip_fopen_index".}
proc zip_fread*(para1: Pzip_file, para2: pointer, para3: int): int {.
cdecl, importc: "zip_fread".}
cdecl, mydll, importc: "zip_fread".}
proc zip_get_archive_comment*(para1: Pzip, para2: ptr int32, para3: int32): cstring {.
cdecl, importc: "zip_get_archive_comment".}
cdecl, mydll, importc: "zip_get_archive_comment".}
proc zip_get_archive_flag*(para1: Pzip, para2: int32, para3: int32): int32 {.
cdecl, importc: "zip_get_archive_flag".}
cdecl, mydll, importc: "zip_get_archive_flag".}
proc zip_get_file_comment*(para1: Pzip, para2: int32, para3: ptr int32,
para4: int32): cstring {.cdecl,
para4: int32): cstring {.cdecl, mydll,
importc: "zip_get_file_comment".}
proc zip_get_name*(para1: Pzip, para2: int32, para3: int32): cstring {.cdecl,
importc: "zip_get_name".}
mydll, importc: "zip_get_name".}
proc zip_get_num_files*(para1: Pzip): int32 {.cdecl,
importc: "zip_get_num_files".}
mydll, importc: "zip_get_num_files".}
proc zip_name_locate*(para1: Pzip, para2: cstring, para3: int32): int32 {.cdecl,
importc: "zip_name_locate".}
mydll, importc: "zip_name_locate".}
proc zip_open*(para1: cstring, para2: int32, para3: ptr int32): Pzip {.cdecl,
importc: "zip_open".}
mydll, importc: "zip_open".}
proc zip_rename*(para1: Pzip, para2: int32, para3: cstring): int32 {.cdecl,
importc: "zip_rename".}
mydll, importc: "zip_rename".}
proc zip_replace*(para1: Pzip, para2: int32, para3: Pzip_source): int32 {.cdecl,
importc: "zip_replace".}
mydll, importc: "zip_replace".}
proc zip_set_archive_comment*(para1: Pzip, para2: cstring, para3: int32): int32 {.
cdecl, importc: "zip_set_archive_comment".}
cdecl, mydll, importc: "zip_set_archive_comment".}
proc zip_set_archive_flag*(para1: Pzip, para2: int32, para3: int32): int32 {.
cdecl, importc: "zip_set_archive_flag".}
cdecl, mydll, importc: "zip_set_archive_flag".}
proc zip_set_file_comment*(para1: Pzip, para2: int32, para3: cstring,
para4: int32): int32 {.cdecl,
para4: int32): int32 {.cdecl, mydll,
importc: "zip_set_file_comment".}
proc zip_source_buffer*(para1: Pzip, para2: pointer, para3: int, para4: int32): Pzip_source {.
cdecl, importc: "zip_source_buffer".}
cdecl, mydll, importc: "zip_source_buffer".}
proc zip_source_file*(para1: Pzip, para2: cstring, para3: int, para4: int): Pzip_source {.
cdecl, importc: "zip_source_file".}
cdecl, mydll, importc: "zip_source_file".}
proc zip_source_filep*(para1: Pzip, para2: TFile, para3: int, para4: int): Pzip_source {.
cdecl, importc: "zip_source_filep".}
proc zip_source_free*(para1: Pzip_source) {.cdecl,
cdecl, mydll, importc: "zip_source_filep".}
proc zip_source_free*(para1: Pzip_source) {.cdecl, mydll,
importc: "zip_source_free".}
proc zip_source_function*(para1: Pzip, para2: Tzip_source_callback,
para3: pointer): Pzip_source {.cdecl,
para3: pointer): Pzip_source {.cdecl, mydll,
importc: "zip_source_function".}
proc zip_source_zip*(para1: Pzip, para2: Pzip, para3: int32, para4: int32,
para5: int, para6: int): Pzip_source {.cdecl,
para5: int, para6: int): Pzip_source {.cdecl, mydll,
importc: "zip_source_zip".}
proc zip_stat*(para1: Pzip, para2: cstring, para3: int32, para4: Pzip_stat): int32 {.
cdecl, importc: "zip_stat".}
cdecl, mydll, importc: "zip_stat".}
proc zip_stat_index*(para1: Pzip, para2: int32, para3: int32, para4: Pzip_stat): int32 {.
cdecl, importc: "zip_stat_index".}
proc zip_stat_init*(para1: Pzip_stat) {.cdecl, importc: "zip_stat_init".}
proc zip_strerror*(para1: Pzip): cstring {.cdecl, importc: "zip_strerror".}
proc zip_unchange*(para1: Pzip, para2: int32): int32 {.cdecl,
cdecl, mydll, importc: "zip_stat_index".}
proc zip_stat_init*(para1: Pzip_stat) {.cdecl, mydll, importc: "zip_stat_init".}
proc zip_strerror*(para1: Pzip): cstring {.cdecl, mydll, importc: "zip_strerror".}
proc zip_unchange*(para1: Pzip, para2: int32): int32 {.cdecl, mydll,
importc: "zip_unchange".}
proc zip_unchange_all*(para1: Pzip): int32 {.cdecl, importc: "zip_unchange_all".}
proc zip_unchange_archive*(para1: Pzip): int32 {.cdecl,
proc zip_unchange_all*(para1: Pzip): int32 {.cdecl, mydll,
importc: "zip_unchange_all".}
proc zip_unchange_archive*(para1: Pzip): int32 {.cdecl, mydll,
importc: "zip_unchange_archive".}

View file

@ -0,0 +1,29 @@
# Tries to test the full ownership path generated by idetools.
proc lev1(t1: string) =
var temp = t1
for i in 0..len(temp)-1:
temp[i] = chr(int(temp[i]) + 1)
proc lev2(t2: string) =
var temp = t2
for i in 0..len(temp)-1:
temp[i] = chr(int(temp[i]) + 1)
proc lev3(t3: string) =
var temp = t3
for i in 0..len(temp)-1:
temp[i] = chr(int(temp[i]) + 1)
proc lev4(t4: string) =
var temp = t4
for i in 0..len(temp)-1:
temp[i] = chr(int(temp[i]) + 1)
echo temp & "(lev4)"
lev4(temp & "(lev3)")
lev3(temp & "(lev2)")
lev2(temp & "(lev1)")
when isMainModule:
lev1("abcd")

View file

@ -0,0 +1,29 @@
absurd_nesting.nim
> c --verbosity:0 --hints:on
SuccessX
> idetools --track:$TESTNIM,6,6 --def $SILENT
skVar\tabsurd_nesting.lev1.temp\tstring
> idetools --track:$TESTNIM,21,13 --def $SILENT
skVar\tabsurd_nesting.lev1.lev2.lev3.lev4.temp\tstring
> idetools --track:$TESTNIM,6,27 --def $SILENT
skForVar\tabsurd_nesting.lev1.i\tint
> idetools --track:$TESTNIM,21,33 --def $SILENT
skForVar\tabsurd_nesting.lev1.lev1.lev3.lev4.i\tint
> idetools --track:$TESTNIM,24,8 --def $SILENT
skProc\tabsurd_nesting.lev1.lev1.lev3.lev4\tproc \(string\)
> idetools --track:$TESTNIM,4,13 --def $SILENT
skParam\tabsurd_nesting.lev1.t1\tstring
> idetools --track:$TESTNIM,4,13 --def $SILENT
skParam\tabsurd_nesting.lev1.t1\tstring
> idetools --track:$TESTNIM,19,19 --def $SILENT
skParam\tabsurd_nesting.lev1.lev2.lev3.lev4.t4\tstring

View file

@ -1,7 +1,10 @@
main.nim
> c
SuccessX
> c
! Processing
> c --verbosity:0 --hints:on
SuccessX
# The "Processing" string will be found always in proc mode since each
# compilation command will generate it. We need to test it only in Caas mode to
# verify the server is not recompiling again the file.
CaasRun > c --verbosity:0 --hints:on
CaasRun ! Processing
CaasRun SuccessX

View file

@ -1,7 +1,7 @@
main.nim
> c
> c --verbosity:0 --hints:on
SuccessX
> idetools --trackDirty:main_dirty.nim,main.nim,12,7 --suggest main.nim
> idetools --trackDirty:main_dirty.nim,$TESTNIM,12,7 --suggest $SILENT
skField\tx
skField\ty

View file

@ -1,9 +1,11 @@
main.nim
> c
> c --verbosity:0 --hints:on
SuccessX
> idetools --track:main.nim,5,18 --def main.nim
> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on
strutils.toUpper
SuccessX
> idetools --track:main.nim,5,18 --def main.nim
! SuccessX
> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on
strutils.toUpper
SuccessX
! SuccessX

View file

@ -1,10 +1,12 @@
main.nim
> idetools --track:main.nim,5,18 --def main.nim
> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on
strutils.toUpper
SuccessX
> idetools --track:main.nim,5,18 --def main.nim
! SuccessX
> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on
strutils.toUpper
SuccessX
> c
! SuccessX
> c --verbosity:0 --hints:on
SuccessX

View file

@ -1,7 +1,8 @@
main.nim
> idetools --track:main.nim,5,18 --def main.nim
> idetools --track:$TESTNIM,5,18 --def --verbosity:0 --hints:on
strutils.toUpper
SuccessX
> c
! SuccessX
> c --verbosity:0 --hints:on
SuccessX

View file

@ -0,0 +1,15 @@
# This example shows that idetools returns an empty signature for a forward
# declared proc in proc/symproc runs, but correctly returns the full signature
# in caas mode.
proc echoHello(text: string)
proc testForward() =
echo "T"
echoHello("T")
proc echoHello(text: string) =
echo "Hello Mr." & text
when isMainModule:
testForward()

View file

@ -0,0 +1,9 @@
forward_declarations.nim
> idetools --track:$TESTNIM,9,5 --def $SILENT
skProc
proc \(string\)
> idetools --track:$TESTNIM,5,9 --def $SILENT
skProc
proc \(string\)

View file

@ -0,0 +1,17 @@
forward_declarations.nim
> c --verbosity:0 --hints:on
SuccessX
# None of the following return three instances of the echoHello proc, the first
# being the forward declaration, the second being the usage inside testForward,
# and the third being the actual implementation.
> idetools --track:$TESTNIM,5,5 --usages $SILENT
skProc.*\n.*skProc.*\n.*skProc
> idetools --track:$TESTNIM,9,5 --usages $SILENT
skProc.*\n.*skProc.*\n.*skProc
> idetools --track:$TESTNIM,11,5 --usages $SILENT
skProc.*\n.*skProc.*\n.*skProc

View file

@ -0,0 +1,43 @@
import unicode, sequtils
proc test_enums() =
var o: Tfile
if o.open("files " & "test.txt", fmWrite):
o.write("test")
o.close()
proc test_iterators(filename = "tests.nim") =
let
input = readFile(filename)
letters = toSeq(runes(string(input)))
for letter in letters: echo int(letter)
const SOME_SEQUENCE = @[1, 2]
type
bad_string = distinct string
TPerson = object of TObject
name*: bad_string
age: int
proc adder(a, b: int): int =
result = a + b
type
PExpr = ref object of TObject ## abstract base class for an expression
PLiteral = ref object of PExpr
x: 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: PLiteral): int = e.x
method eval(e: PPlusExpr): int = eval(e.a) + eval(e.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)))

View file

@ -0,0 +1,44 @@
idetools_api.nim
> c --verbosity:0 --hints:on
SuccessX
> idetools --track:$TESTNIM,4,11 --def $SILENT
def\tskType\tsystem.TFile\tTFile
> idetools --track:$TESTNIM,5,7 --def $SILENT
def\tskProc\tsystem.Open\tproc \(var TFile, string, TFileMode, int\): bool
> idetools --track:$TESTNIM,5,21 --def $SILENT
def\tskProc\tsystem.\&\tproc \(string, string\): string\{.noSideEffect.\}
> idetools --track:$TESTNIM,5,38 --def $SILENT
def\tskEnumField\tsystem.TFileMode.fmWrite\tTFileMode
> idetools --track:$TESTNIM,7,6 --def $SILENT
def\tskProc\tsystem.Close\tproc \(TFile\)
> idetools --track:$TESTNIM,12,23 --def $SILENT
def\tskIterator\tunicode.runes\titerator \(string\): TRune
> idetools --track:$TESTNIM,12,15 --def $SILENT
def\tskTemplate\tsequtils.toSeq\tproc \(expr\): expr
> idetools --track:$TESTNIM,15,7 --def $SILENT
# ProcRun mode will fail the next line, because the type is returned empty.
def\tskConst\t$MODULE.SOME_SEQUENCE\tseq\[int\]\t
> idetools --track:$TESTNIM,15,23 --def $SILENT
def\tskProc\tsystem.@\tproc \(array\[IDX, T\]\): seq\[T\]\{.noSideEffect.\}
> idetools --track:$TESTNIM,17,3 --def $SILENT
# ProcRun mode will fail the next line, because the type is returned empty.
def\tskType\t$MODULE.bad_string\tbad_string\t
> idetools --track:$TESTNIM,11,24 --def $SILENT
def\tskParam\t$MODULE.test_iterators.filename\tstring
> idetools --track:$TESTNIM,6,5 --def $SILENT
def\tskVar\t$MODULE.test_enums.o\tTFile
> idetools --track:$TESTNIM,12,34 --def $SILENT
def\tskLet\t$MODULE.test_iterators.input\tTaintedString
> idetools --track:$TESTNIM,13,35 --def $SILENT
def\tskForVar\t$MODULE.test_iterators.letter\tTRune
> idetools --track:$TESTNIM,23,3 --def $SILENT
def\tskResult\t$MODULE.adder.result\tint
> idetools --track:$TESTNIM,19,6 --def $SILENT
# ProcRun mode will fail the next line, because the type is returned empty.
def\tskField\t$MODULE.TPerson.name\tbad_string\t
> idetools --track:$TESTNIM,43,7 --def $SILENT
def\tskMethod\t$MODULE.eval\tproc \(PPlusExpr\): int\t

View file

@ -0,0 +1,17 @@
import unicode, sequtils
proc test() =
let input = readFile("weird.nim")
for letter in runes(string(input)):
echo int(letter)
when 1 > 0:
proc failtest() =
let
input = readFile("weird.nim")
letters = toSeq(runes(string(input)))
for letter in letters:
echo int(letter)
when isMainModule:
test()

View file

@ -0,0 +1,14 @@
issue_416_template_shift.nim
> c --verbosity:0 --hints:on
SuccessX
> idetools --track:$TESTNIM,12,28 --def $SILENT
def\tskType\tsystem.string\tstring
> idetools --track:$TESTNIM,12,35 --def $SILENT
def\tskLet\t$MODULE.failtest.input\tTaintedString
# The following fail because they seem shifted one colum to the right.
> idetools --track:$TESTNIM,12,16 --def $SILENT
def\tskTemplate\tsequtils.toSeq\tproc \(expr\): expr
> idetools --track:$TESTNIM,12,22 --def $SILENT
def\tskIterator\tunicode.runes\titerator \(string\): TRune

View file

@ -0,0 +1,8 @@
const
VERSION_STR1* = "0.5.0" ## Idetools shifts this one column.
VERSION_STR2 = "0.5.0" ## This one is ok.
VERSION_STR3* = "0.5.0" ## Bad.
VERSION_STR4 = "0.5.0" ## Ok.
proc forward1*(): string = result = ""
proc forward2(): string = result = ""

View file

@ -0,0 +1,11 @@
issue_452_export_shift.nim
> c --verbosity:0 --hints:on
SuccessX
> idetools --track:$TESTNIM,2,2 --def $SILENT
def\tskConst\t$MODULE.VERSION_STR1\tstring
> idetools --track:$TESTNIM,3,2 --def $SILENT
def\tskConst\t$MODULE.VERSION_STR2\tstring
> idetools --track:$TESTNIM,7,5 --def $SILENT
def\tskProc\t$MODULE.forward1\tproc \(\): string\t
> idetools --track:$TESTNIM,8,5 --def $SILENT
def\tskProc\t$MODULE.forward2\tproc \(\): string\t

View file

@ -0,0 +1,19 @@
type
TThing = object of TObject
TUnit = object of TThing
x: int
method collide(a, b: TThing) {.inline.} =
quit "to override!"
method collide(a: TThing, b: TUnit) {.inline.} =
echo "collide1"
method collide(a: TUnit, b: TThing) {.inline.} =
echo "collide2"
var
a, b: TUnit
when isMainModule:
collide(a, b) # output: 2

View file

@ -0,0 +1,5 @@
issue_477_dynamic_dispatch.nim
> c --run
SuccessX
> idetools --track:issue_477_dynamic_dispatch.nim,19,5 --def $SILENT
def\tskMethod\tissue_477_dynamic_dispatch.collide\tproc \(TUnit, TThing\)\{.inline.\}

View file

@ -0,0 +1,29 @@
import unicode, sequtils
# This example shows that idetools returns proc as signature for everything
# which can be called. While a clever person would use the second column to
# differentiate betwen procs, methods and others, why does the output contain
# incorrect information?
type
TThing = object of TObject
TUnit = object of TThing
x: int
method collide(a, b: TThing) {.inline.} =
quit "to override!"
method collide(a: TThing, b: TUnit) {.inline.} =
echo "1"
method collide(a: TUnit, b: TThing) {.inline.} =
echo "2"
var
a, b: TUnit
let
input = readFile("its_full_of_procs.nim")
letters = toSeq(runes(string(input)))
collide(a, b) # output: 2

View file

@ -0,0 +1,20 @@
its_full_of_procs.nim
> idetools --track:$TESTNIM,26,15 --def $SILENT
skProc
proc \(
> idetools --track:$TESTNIM,27,21 --def $SILENT
skIterator
iterator \(
!proc \(
> idetools --track:$TESTNIM,29,0 --def $SILENT
skMethod
method \(
!proc \(
> idetools --track:$TESTNIM,27,15 --def $SILENT
skTemplate
template \(
!proc \(

View file

@ -1,7 +1,7 @@
main.nim
> idetools --trackDirty:main_dirty.nim,main.nim,12,7 --suggest main.nim
> idetools --trackDirty:main_dirty.nim,$TESTNIM,12,7 --suggest $SILENT
skField\tx
skField\ty
> c
> c --verbosity:0 --hints:on
SuccessX

View file

@ -1,22 +1,97 @@
import osproc, streams, os, strutils, re
type
TNimrodSession* = object
nim: PProcess
## Compiler as a service tester.
##
## This test cases uses the txt files in the caas/ subdirectory.
##
## Each of the text files inside encodes a session with the compiler:
##
## The first line indicates the main project file.
##
## Lines starting with '>' indicate a command to be sent to the compiler and
## the lines following a command include checks for expected or forbidden
## output (! for forbidden).
##
## If a line starts with '#' it will be ignored completely, so you can use that
## for comments.
##
## All the tests are run both in ProcRun (each command creates a separate
## process) and CaasRun (first command starts up a server and it is reused for
## the rest) modes. Since some cases are specific to either ProcRun or CaasRun
## modes, you can prefix a line with the mode and the line will be processed
## only in that mode.
##
## The rest of the line is treated as a regular expression, so be careful
## escaping metacharacters like parenthesis. Before the line is processed as a
## regular expression, some basic variables are searched for and replaced in
## the tests. The variables which will be replaced are:
##
## - $TESTNIM: filename specified in the first line of the script.
## - $MODULE: like $TESTNIM but without extension, useful for expected output.
##
## You can optionally pass parameters at the command line to modify the
## behaviour of the test suite. By default only tests which fail will be echoed
## to stdout. If you want to see all the output pass the word "verbose" as a
## parameter.
##
## If you don't want to run all the test case files, you can pass any substring
## as a parameter. Only files matching the passed substring will be run. The
## filtering doesn't use any globbing metacharacters, it's a plain match.
##
## Example to run only "*-compile*.txt" tests in verbose mode:
##
## ./caasdriver verbose -compile
proc dirname(path: string): string = path.splitPath()[0]
type
TRunMode = enum
ProcRun, CaasRun, SymbolProcRun
TNimrodSession* = object
nim: PProcess # Holds the open process for CaasRun sessions, nil otherwise.
mode: TRunMode # Stores the type of run mode the session was started with.
lastOutput: string # Preserves the last output, needed for ProcRun mode.
filename: string # Appended to each command starting with '>'. Also a var.
modname: string # Like filename but without extension.
nimcache: string # Input script based name for the nimcache dir.
const
modes = [CaasRun, ProcRun, SymbolProcRun]
filenameReplaceVar = "$TESTNIM"
moduleReplaceVar = "$MODULE"
silentReplaceVar = "$SILENT"
silentReplaceText = "--verbosity:0 --hints:off"
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 replaceVars(session: var TNimrodSession, text: string): string =
result = text.replace(filenameReplaceVar, session.filename)
result = result.replace(moduleReplaceVar, session.modname)
result = result.replace(silentReplaceVar, silentReplaceText)
proc doCommand*(session: var TNimrodSession, command: string): string =
session.nim.inputStream.write(command & "\n")
proc startNimrodSession(project, script: string, mode: TRunMode):
TNimrodSession =
let (dir, name, ext) = project.splitFile
result.mode = mode
result.lastOutput = ""
result.filename = name & ext
result.modname = name
let (nimcacheDir, nimcacheName, nimcacheExt) = script.splitFile
result.nimcache = "SymbolProcRun." & nimcacheName
if mode == SymbolProcRun:
removeDir(nimcacheDir / result.nimcache)
if mode == CaasRun:
result.nim = startProcess(NimrodBin, workingDir = dir,
args = ["serve", "--server.type:stdin", name])
proc doCaasCommand(session: var TNimrodSession, command: string): string =
assert session.mode == CaasRun
session.nim.inputStream.write(session.replaceVars(command) & "\n")
session.nim.inputStream.flush
result = ""
@ -30,10 +105,40 @@ proc doCommand*(session: var TNimrodSession, command: string): string =
result = "FAILED TO EXECUTE: " & command & "\n" & result
break
proc close(session: var TNimrodSession) {.destructor.} =
session.nim.close
proc doProcCommand(session: var TNimrodSession, command: string): string =
assert session.mode == ProcRun or session.mode == SymbolProcRun
except: result = "FAILED TO EXECUTE: " & command & "\n" & result
var
process = startProcess(NimrodBin, args = session.replaceVars(command).split)
stream = outputStream(process)
line = TaintedString("")
proc doScenario(script: string, output: PStream): bool =
result = ""
while stream.readLine(line):
if result.len > 0: result &= "\n"
result &= line.string
process.close()
proc doCommand(session: var TNimrodSession, command: string) =
if session.mode == CaasRun:
session.lastOutput = doCaasCommand(session,
command & " " & session.filename)
else:
var command = command
# For symbol runs we prepend the necessary parameters to avoid clobbering
# the normal nimcache.
if session.mode == SymbolProcRun:
command = "--symbolFiles:on --nimcache:" & session.nimcache &
" " & command
session.lastOutput = doProcCommand(session,
command & " " & session.filename)
proc close(session: var TNimrodSession) {.destructor.} =
if session.mode == CaasRun:
session.nim.close
proc doScenario(script: string, output: PStream, mode: TRunMode): bool =
result = true
var f = open(script)
@ -41,27 +146,40 @@ proc doScenario(script: string, output: PStream): bool =
if f.readLine(project):
var
s = startNimrodSession(script.dirname / project.string)
s = startNimrodSession(script.parentDir / project.string, script, mode)
tline = TaintedString("")
lastOutput = ""
ln = 1
while f.readLine(tline):
var line = tline.string
inc ln
# Filter lines by run mode, removing the prefix if the mode is current.
for testMode in modes:
if line.startsWith($testMode):
if testMode != mode:
line = ""
else:
line = line[len($testMode)..len(line) - 1].strip
break
if line.strip.len == 0: continue
if line.startsWith(">"):
lastOutput = s.doCommand(line.substr(1).strip)
output.writeln line, "\n", lastOutput
if line.startsWith("#"):
output.writeln line
continue
elif line.startsWith(">"):
s.doCommand(line.substr(1).strip)
output.writeln line, "\n", s.lastOutput
else:
var expectMatch = true
var pattern = line
var pattern = s.replaceVars(line)
if line.startsWith("!"):
pattern = line.substr(1).strip
pattern = pattern.substr(1).strip
expectMatch = false
var actualMatch = lastOutput.find(re(pattern)) != -1
let actualMatch =
s.lastOutput.find(re(pattern, flags = {reStudy})) != -1
if expectMatch == actualMatch:
output.writeln "SUCCESS ", line
@ -70,17 +188,33 @@ proc doScenario(script: string, output: PStream): bool =
result = false
iterator caasTestsRunner*(filter = ""): tuple[test, output: string,
status: bool] =
status: bool, mode: TRunMode] =
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)
for mode in modes:
var outStream = newStringStream()
let r = doScenario(scenario, outStream, mode)
yield (scenario, outStream.data, r, mode)
when isMainModule:
var filter = ""
if paramCount() > 0: filter = paramStr(1)
var
filter = ""
failures = 0
verbose = false
for t, o, r in caasTestsRunner(filter):
echo t, "\n", o
for i in 0..ParamCount() - 1:
let param = string(paramStr(i + 1))
case param
of "verbose": verbose = true
else: filter = param
if verbose and len(filter) > 0:
echo "Running only test cases matching filter '$1'" % [filter]
for test, output, result, mode in caasTestsRunner(filter):
if not result or verbose:
echo test, "\n", output, "-> ", $mode, ":", $result, "\n-----"
if not result:
failures += 1
quit(failures)

View file

@ -15,3 +15,12 @@ else:
return tkUnknown
case_token: inc i
#bug #488
macro foo: stmt =
var exp = newCall("whatwhat", newIntLitNode(1))
if compiles(getAst(exp)): return exp
else: echo "Does not compute!"
foo()

View file

@ -10,6 +10,8 @@ proc parseInt(x: TFile): int {.noSideEffect.} = nil
proc parseInt(x: char): int {.noSideEffect.} = nil
proc parseInt(x: int16): int {.noSideEffect.} = nil
proc parseInt[T](x: T): int = echo x; 34
type
TParseInt = proc (x: string): int {.noSideEffect.}
@ -33,3 +35,5 @@ type
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)
discard parseInt[string]("yay")

View file

@ -4,8 +4,8 @@ s = socket()
s.connect("www.google.com", TPort(80))
var recvData: string = ""
echo(s.recvLine(recvData))
echo(recvData)
var data: string = ""
s.readLine(data)
echo(data)

View file

@ -0,0 +1,494 @@
## Command line parsing module for Nimrod.
##
## `Nimrod <http://nimrod-code.org>`_ provides the `parseopt module
## <http://nimrod-code.org/parseopt.html>`_ to parse options from the
## commandline. This module tries to provide functionality to prevent you from
## writing commandline parsing and let you concentrate on providing the best
## possible experience for your users.
##
## Source code for this module can be found at
## https://github.com/gradha/argument_parser.
import os, strutils, tables, math, parseutils, sequtils, sets, algorithm,
unicode
const
VERSION_STR* = "0.1.2" ## Module version as a string.
VERSION_INT* = (major: 0, minor: 1, maintenance: 2) ## \
## Module version as an integer tuple.
##
## Major versions changes mean a break in API backwards compatibility, either
## through removal of symbols or modification of their purpose.
##
## Minor version changes can add procs (and maybe default parameters). Minor
## odd versions are development/git/unstable versions. Minor even versions
## are public stable releases.
##
## Maintenance version changes mean bugfixes or non API changes.
# - Types
type
Tparam_kind* = enum ## Different types of results for parameter parsing.
PK_EMPTY, PK_INT, PK_FLOAT, PK_STRING, PK_BOOL,
PK_BIGGEST_INT, PK_BIGGEST_FLOAT, PK_HELP
Tparameter_callback* =
proc (parameter: string; value: var Tparsed_parameter): string ## \
## Prototype of parameter callbacks
##
## A parameter callback is just a custom proc you provide which is invoked
## after a parameter is parsed passing the basic type validation. The
## `parameter` parameter is the string which triggered the option. The
## `value` parameter contains the string passed by the user already parsed
## into the basic type you specified for it.
##
## The callback proc has modification access to the Tparsed_parameter
## `value` parameter that will be put into Tcommandline_results: you can
## read it and also modify it, maybe changing its type. In fact, if you
## need special parsing, most likely you will end up specifying PK_STRING
## in the parameter input specification so that the parse() proc doesn't
## *mangle* the string before you can process it yourself.
##
## If the callback decides to abort the validation of the parameter, it has
## to put into result a non zero length string with a message for the user
## explaining why the validation failed, and maybe offer a hint as to what
## can be done to pass validation.
Tparameter_specification* = object ## \
## Holds the expectations of a parameter.
##
## You create these objects and feed them to the parse() proc, which then
## uses them to detect parameters and turn them into something uself.
names*: seq[string] ## List of possible parameters to catch for this.
consumes*: Tparam_kind ## Expected type of the parameter (empty for none)
custom_validator*: Tparameter_callback ## Optional custom callback
## to run after type conversion.
help_text*: string ## Help for this group of parameters.
Tparsed_parameter* = object ## \
## Contains the parsed value from the user.
##
## This implements an object variant through the kind field. You can 'case'
## this field to write a generic proc to deal with parsed parameters, but
## nothing prevents you from accessing directly the type of field you want
## if you expect only one kind.
case kind*: Tparam_kind
of PK_EMPTY: nil
of PK_INT: int_val*: int
of PK_BIGGEST_INT: big_int_val*: biggestInt
of PK_FLOAT: float_val*: float
of PK_BIGGEST_FLOAT: big_float_val*: biggestFloat
of PK_STRING: str_val*: string
of PK_BOOL: bool_val*: bool
of PK_HELP: nil
Tcommandline_results* = object of TObject ## \
## Contains the results of the parsing.
##
## Usually this is the result of the parse() call, but you can inherit from
## it to add your own fields for convenience.
##
## Note that you always have to access the ``options`` ordered table with
## the first variant of a parameter name. For instance, if you have an
## option specified like ``@["-s", "--silent"]`` and the user types
## ``--silent`` at the commandline, you have to use
## ``options.hasKey("-s")`` to test for it. This standarizes access through
## the first name variant for all options to avoid you repeating the test
## with different keys.
positional_parameters*: seq[Tparsed_parameter]
options*: TOrderedTable[string, Tparsed_parameter]
# - Tparam_kind procs
proc `$`*(value: Tparam_kind): string {.procvar.} =
## Stringifies the type, used to generate help texts.
case value:
of PK_EMPTY: result = ""
of PK_INT: result = "INT"
of PK_BIGGEST_INT: result = "BIG_INT"
of PK_FLOAT: result = "FLOAT"
of PK_BIGGEST_FLOAT: result = "BIG_FLOAG"
of PK_STRING: result = "STRING"
of PK_BOOL: result = "BOOL"
of PK_HELP: result = ""
# - Tparameter_specification procs
proc init*(param: var Tparameter_specification, consumes = PK_EMPTY,
custom_validator: Tparameter_callback = nil, help_text = "",
names: varargs[string]) =
## Initialization helper with default parameters.
##
## You can decide to miss some if you like the defaults, reducing code. You
## can also use new_parameter_specification() for single assignment
## variables.
param.names = @names
param.consumes = consumes
param.custom_validator = custom_validator
param.help_text = help_text
proc new_parameter_specification*(consumes = PK_EMPTY,
custom_validator: Tparameter_callback = nil, help_text = "",
names: varargs[string]): Tparameter_specification =
## Initialization helper for single assignment variables.
result.init(consumes, custom_validator, help_text, names)
# - Tparsed_parameter procs
proc `$`*(data: Tparsed_parameter): string {.procvar.} =
## Stringifies the value, mostly for debug purposes.
##
## The proc will display the value followed by non string type in brackets.
## The non string types would be PK_INT (i), PK_BIGGEST_INT (I), PK_FLOAT
## (f), PK_BIGGEST_FLOAT (F), PK_BOOL (b). The string type would be enclosed
## inside quotes. PK_EMPTY produces the word `nil`, and PK_HELP produces the
## world `help`.
case data.kind:
of PK_EMPTY: result = "nil"
of PK_INT: result = "$1(i)" % $data.int_val
of PK_BIGGEST_INT: result = "$1(I)" % $data.big_int_val
of PK_FLOAT: result = "$1(f)" % $data.float_val
of PK_BIGGEST_FLOAT: result = "$1(F)" % $data.big_float_val
of PK_STRING: result = "\"" & $data.str_val & "\""
of PK_BOOL: result = "$1(b)" % $data.bool_val
of PK_HELP: result = "help"
template new_parsed_parameter*(tkind: Tparam_kind, expr): Tparsed_parameter =
## Handy compile time template to build Tparsed_parameter object variants.
##
## The problem with object variants is that you first have to initialise them
## to a kind, then assign values to the correct variable, and it is a little
## bit annoying.
##
## Through this template you specify as the first parameter the kind of the
## Tparsed_parameter you want to build, and directly the value it will be
## initialised with. The template figures out at compile time what field to
## assign the variable to, and thus you reduce code clutter and may use this
## to initialise single assignments variables in `let` blocks. Example:
##
## .. code-block:: nimrod
## let
## parsed_param1 = new_parsed_parameter(PK_FLOAT, 3.41)
## parsed_param2 = new_parsed_parameter(PK_BIGGEST_INT, 2358123 * 23123)
## # The following line doesn't compile due to
## # type mismatch: got (string) but expected 'int'
## #parsed_param3 = new_parsed_parameter(PK_INT, "231")
var result {.gensym.}: Tparsed_parameter
result.kind = tkind
when tkind == PK_EMPTY: nil
elif tkind == PK_INT: result.int_val = expr
elif tkind == PK_BIGGEST_INT: result.big_int_val = expr
elif tkind == PK_FLOAT: result.float_val = expr
elif tkind == PK_BIGGEST_FLOAT: result.big_float_val = expr
elif tkind == PK_STRING: result.str_val = expr
elif tkind == PK_BOOL: result.bool_val = expr
elif tkind == PK_HELP: nil
else: {.error: "unknown kind".}
result
# - Tcommandline_results procs
proc init*(param: var Tcommandline_results;
positional_parameters: seq[Tparsed_parameter] = @[];
options: TOrderedTable[string, Tparsed_parameter] =
initOrderedTable[string, Tparsed_parameter](4)) =
## Initialization helper with default parameters.
param.positional_parameters = positional_parameters
param.options = options
proc `$`*(data: Tcommandline_results): string =
## Stringifies a Tcommandline_results structure for debug output
var dict: seq[string] = @[]
for key, value in data.options:
dict.add("$1: $2" % [escape(key), $value])
result = "Tcommandline_result{positional_parameters:[$1], options:{$2}}" % [
join(map(data.positional_parameters, `$`), ", "), join(dict, ", ")]
# - Parse code
template raise_or_quit(exception, message: expr): stmt {.immediate.} =
## Avoids repeating if check based on the default quit_on_failure variable.
##
## As a special case, if message has a zero length the call to quit won't
## generate any messages or errors (used by the mechanism to echo help to the
## user).
if quit_on_failure:
if len(message) > 0:
quit(message)
else:
quit()
else:
raise newException(exception, message)
template run_custom_proc(parsed_parameter: Tparsed_parameter,
custom_validator: Tparameter_callback,
parameter: TaintedString) =
## Runs the custom validator if it is not nil.
##
## Pass in the string of the parameter triggering the call. If the
if not custom_validator.isNil:
except:
raise_or_quit(EInvalidValue, ("Couldn't run custom proc for " &
"parameter $1:\n$2" % [escape(parameter),
getCurrentExceptionMsg()]))
let message = custom_validator(parameter, parsed_parameter)
if not message.isNil and message.len > 0:
raise_or_quit(EInvalidValue, ("Failed to validate value for " &
"parameter $1:\n$2" % [escape(parameter), message]))
proc parse_parameter(quit_on_failure: bool, param, value: string,
param_kind: Tparam_kind): Tparsed_parameter =
## Tries to parse a text according to the specified type.
##
## Pass the parameter string which requires a value and the text the user
## passed in for it. It will be parsed according to the param_kind. This proc
## will raise (EInvalidValue, EOverflow) if something can't be parsed.
result.kind = param_kind
case param_kind:
of PK_INT:
try: result.int_val = value.parseInt
except EOverflow:
raise_or_quit(EOverflow, ("parameter $1 requires an " &
"integer, but $2 is too large to fit into one") % [param,
escape(value)])
except EInvalidValue:
raise_or_quit(EInvalidValue, ("parameter $1 requires an " &
"integer, but $2 can't be parsed into one") % [param, escape(value)])
of PK_STRING:
result.str_val = value
of PK_FLOAT:
try: result.float_val = value.parseFloat
except EInvalidValue:
raise_or_quit(EInvalidValue, ("parameter $1 requires a " &
"float, but $2 can't be parsed into one") % [param, escape(value)])
of PK_BOOL:
try: result.bool_val = value.parseBool
except EInvalidValue:
raise_or_quit(EInvalidValue, ("parameter $1 requires a " &
"boolean, but $2 can't be parsed into one. Valid values are: " &
"y, yes, true, 1, on, n, no, false, 0, off") % [param, escape(value)])
of PK_BIGGEST_INT:
try:
let parsed_len = parseBiggestInt(value, result.big_int_val)
if value.len != parsed_len or parsed_len < 1:
raise_or_quit(EInvalidValue, ("parameter $1 requires an " &
"integer, but $2 can't be parsed completely into one") % [
param, escape(value)])
except EInvalidValue:
raise_or_quit(EInvalidValue, ("parameter $1 requires an " &
"integer, but $2 can't be parsed into one") % [param, escape(value)])
of PK_BIGGEST_FLOAT:
try:
let parsed_len = parseBiggestFloat(value, result.big_float_val)
if value.len != parsed_len or parsed_len < 1:
raise_or_quit(EInvalidValue, ("parameter $1 requires a " &
"float, but $2 can't be parsed completely into one") % [
param, escape(value)])
except EInvalidValue:
raise_or_quit(EInvalidValue, ("parameter $1 requires a " &
"float, but $2 can't be parsed into one") % [param, escape(value)])
of PK_EMPTY:
nil
of PK_HELP:
nil
template build_specification_lookup():
TOrderedTable[string, ptr Tparameter_specification] =
## Returns the table used to keep pointers to all of the specifications.
var result {.gensym.}: TOrderedTable[string, ptr Tparameter_specification]
result = initOrderedTable[string, ptr Tparameter_specification](
nextPowerOfTwo(expected.len))
for i in 0..expected.len-1:
for param_to_detect in expected[i].names:
if result.hasKey(param_to_detect):
raise_or_quit(EInvalidKey,
"Parameter $1 repeated in input specification" % param_to_detect)
else:
result[param_to_detect] = addr(expected[i])
result
proc echo_help*(expected: seq[Tparameter_specification] = @[],
type_of_positional_parameters = PK_STRING,
bad_prefixes = @["-", "--"], end_of_options = "--")
proc parse*(expected: seq[Tparameter_specification] = @[],
type_of_positional_parameters = PK_STRING, args: seq[TaintedString] = nil,
bad_prefixes = @["-", "--"], end_of_options = "--",
quit_on_failure = true): Tcommandline_results =
## Parses parameters and returns results.
##
## The expected array should contain a list of the parameters you want to
## detect, which can capture additional values. Uncaptured parameters are
## considered positional parameters for which you can specify a type with
## type_of_positional_parameters.
##
## Before accepting a positional parameter, the list of bad_prefixes is
## compared against it. If the positional parameter starts with any of them,
## an error is displayed to the user due to ambiguity. The user can overcome
## the ambiguity by typing the special string specified by end_of_options.
## Note that values captured by parameters are not checked against bad
## prefixes, otherwise it would be a problem to specify the dash as synonim
## for standard input for many programs.
##
## The args sequence should be the list of parameters passed to your program
## without the program binary (usually OSes provide the path to the binary as
## the zeroth parameter). If args is nil, the list will be retrieved from the
## OS.
##
## If there is any kind of error and quit_on_failure is true, the quit proc
## will be called with a user error message. If quit_on_failure is false
## errors will raise exceptions (usually EInvalidValue or EOverflow) instead
## for you to catch and handle.
assert type_of_positional_parameters != PK_EMPTY and
type_of_positional_parameters != PK_HELP
for bad_prefix in bad_prefixes:
assert bad_prefix.len > 0, "Can't pass in a bad prefix of zero length"
var
expected = expected
adding_options = true
result.init()
# Prepare the input parameter list, maybe get it from the OS if not available.
var args = args
if args == nil:
let total_params = ParamCount()
#echo "Got no explicit args, retrieving from OS. Count: ", total_params
newSeq(args, total_params)
for i in 0..total_params - 1:
#echo ($i)
args[i] = paramStr(i + 1)
# Generate lookup table for each type of parameter based on strings.
var lookup = build_specification_lookup()
# Loop through the input arguments detecting their type and doing stuff.
var i = 0
while i < args.len:
let arg = args[i]
block adding_positional_parameter:
if arg.len > 0 and adding_options:
if arg == end_of_options:
# Looks like we found the end_of_options marker, disable options.
adding_options = false
break adding_positional_parameter
elif lookup.hasKey(arg):
var parsed: Tparsed_parameter
let param = lookup[arg]
# Insert check here for help, which aborts parsing.
if param.consumes == PK_HELP:
echo_help(expected, type_of_positional_parameters,
bad_prefixes, end_of_options)
raise_or_quit(EInvalidKey, "")
if param.consumes != PK_EMPTY:
if i + 1 < args.len:
parsed = parse_parameter(quit_on_failure,
arg, args[i + 1], param.consumes)
run_custom_proc(parsed, param.custom_validator, arg)
i += 1
else:
raise_or_quit(EInvalidValue, ("parameter $1 requires a " &
"value, but none was provided") % [arg])
result.options[param.names[0]] = parsed
break adding_positional_parameter
else:
for bad_prefix in bad_prefixes:
if arg.startsWith(bad_prefix):
raise_or_quit(EInvalidValue, ("Found ambiguos parameter '$1' " &
"starting with '$2', put '$3' as the previous parameter " &
"if you want to force it as positional parameter.") % [arg,
bad_prefix, end_of_options])
# Unprocessed, add the parameter to the list of positional parameters.
result.positional_parameters.add(parse_parameter(quit_on_failure,
$(1 + i), arg, type_of_positional_parameters))
i += 1
proc toString(runes: seq[TRune]): string =
result = ""
for rune in runes: result.add(rune.toUTF8)
proc ascii_cmp(a, b: string): int =
## Comparison ignoring non ascii characters, for better switch sorting.
let a = filterIt(toSeq(runes(a)), it.isAlpha())
# Can't use filterIt twice, github bug #351.
let b = filter(toSeq(runes(b)), proc(x: TRune): bool = x.isAlpha())
return system.cmp(toString(a), toString(b))
proc build_help*(expected: seq[Tparameter_specification] = @[],
type_of_positional_parameters = PK_STRING,
bad_prefixes = @["-", "--"], end_of_options = "--"): seq[string] =
## Builds basic help text and returns it as a sequence of strings.
##
## Note that this proc doesn't do as much sanity checks as the normal parse()
## proc, though it's unlikely you will be using one without the other, so if
## you had a parameter specification problem you would find out soon.
result = @["Usage parameters: "]
# Generate lookup table for each type of parameter based on strings.
let quit_on_failure = false
var
expected = expected
lookup = build_specification_lookup()
keys = toSeq(lookup.keys())
# First generate the joined version of input parameters in a list.
var
seen = initSet[string]()
prefixes: seq[string] = @[]
helps: seq[string] = @[]
for key in keys:
if seen.contains(key):
continue
# Add the joined string to the list.
let param = lookup[key][]
var param_names = param.names
sort(param_names, ascii_cmp)
var prefix = join(param_names, ", ")
# Don't forget about the type, if the parameter consumes values
if param.consumes != PK_EMPTY and param.consumes != PK_HELP:
prefix &= " " & $param.consumes
prefixes.add(prefix)
helps.add(param.help_text)
# Ignore future elements.
for name in param.names: seen.incl(name)
# Calculate the biggest width and try to use that
let width = prefixes.map(proc (x: string): int = 3 + len(x)).max
for line in zip(prefixes, helps):
result.add(line.a & repeatChar(width - line.a.len) & line.b)
proc echo_help*(expected: seq[Tparameter_specification] = @[],
type_of_positional_parameters = PK_STRING,
bad_prefixes = @["-", "--"], end_of_options = "--") =
## Prints out help on the terminal.
##
## This is just a wrapper around build_help. Note that calling this proc
## won't exit your program, you should call quit() yourself.
for line in build_help(expected,
type_of_positional_parameters, bad_prefixes, end_of_options):
echo line
when isMainModule:
# Simply tests code embedded in docs.
let
parsed_param1 = new_parsed_parameter(PK_FLOAT, 3.41)
parsed_param2 = new_parsed_parameter(PK_BIGGEST_INT, 2358123 * 23123)
#parsed_param3 = new_parsed_parameter(PK_INT, "231")

View file

@ -33,18 +33,9 @@ proc emptyNode*(): PNimrodNode {.compileTime.} =
proc dot*(left, right: PNimrodNode): PNimrodNode {.compileTime.} =
result = newNimNode(nnkDotExpr).und(left, right)
proc postfix*(a: PNimrodNode, b: string): PNimrodNode {.compileTime.} =
result = newNimNode(nnkPostfix).und(newIdentNode(!b), a)
proc prefix*(a: string, b: PNimrodNode): PNimrodNode {.compileTime.} =
result = newNimNode(nnkPrefix).und(newIdentNode(!a), b)
proc infix*(a, b, c: PNimrodNode): PNimrodNode {.compileTime.} =
## 5.infix("+", 10) ## => 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

View file

@ -145,7 +145,7 @@ proc lobbyInit*() =
var pos = vec2f(10, 10)
u_alias = gui.newTextEntry(
if s.existsKey("alias"): s["alias"].str else: "alias",
if s.hasKey("alias"): s["alias"].str else: "alias",
pos)
pos.y += 20
u_passwd = gui.newTextEntry("buzz", pos)

View file

@ -227,12 +227,13 @@ template checkFile(path: expr): stmt {.dirty, immediate.} =
cacheImpl newSprite, SpriteSheets, PSpriteSheet:
result.file = filename
if not(filename =~ re"\S+_(\d+)x(\d+)\.\S\S\S"):
if filename =~ re"\S+_(\d+)x(\d+)\.\S\S\S":
result.framew = strutils.parseInt(matches[0])
result.frameh = strutils.parseInt(matches[1])
checkFile("data/gfx"/result.file)
else:
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
@ -288,23 +289,23 @@ proc validateSettings*(settings: PJsonNode, errors: var seq[string]): bool =
if settings.kind != JObject:
addError("Settings root must be an object")
return
if not settings.existsKey("vehicles"):
if not settings.hasKey("vehicles"):
addError("Vehicles section missing")
if not settings.existsKey("objects"):
if not settings.hasKey("objects"):
errors.add("Objects section is missing")
result = false
if not settings.existsKey("level"):
if not settings.hasKey("level"):
errors.add("Level settings section is missing")
result = false
else:
let lvl = settings["level"]
if lvl.kind != JObject or not lvl.existsKey("size"):
if lvl.kind != JObject or not lvl.hasKey("size"):
errors.add("Invalid level settings")
result = false
elif not lvl.existsKey("size") or lvl["size"].kind != JArray or lvl["size"].len != 2:
elif not lvl.hasKey("size") or lvl["size"].kind != JArray or lvl["size"].len != 2:
errors.add("Invalid/missing level size")
result = false
if not settings.existsKey("items"):
if not settings.hasKey("items"):
errors.add("Items section missing")
result = false
else:
@ -363,7 +364,7 @@ proc loadSettings*(rawJson: string, errors: var seq[string]): bool =
nameToVehID[veh.name] = veh.id
inc vID
vID = 0
if settings.existsKey("bullets"):
if settings.hasKey("bullets"):
for blt in settings["bullets"].items:
var bullet = importBullet(blt, errors)
bullet.id = bID
@ -408,21 +409,21 @@ proc fetchBullet(name: string): PBulletRecord =
return cfg.bullets[nameToBulletID[name]]
proc getField(node: PJsonNode, field: string, target: var float) =
if not node.existsKey(field):
if not node.hasKey(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):
if not node.hasKey(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):
if not node.hasKey(field):
return
case node[field].kind
of JBool:
@ -434,7 +435,7 @@ proc getField(node: PJsonNode; field: string; target: var bool) =
else: nil
template checkKey(node: expr; key: string): stmt =
if not existsKey(node, key):
if not hasKey(node, key):
return
proc importTrail(data: PJsonNode; errors: var seq[string]): TTrailRecord =
@ -450,17 +451,17 @@ proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings =
checkKey(data, "level")
var level = data["level"]
if level.existsKey("size") and level["size"].kind == JArray and level["size"].len == 2:
if level.hasKey("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"):
if level.hasKey("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:
if data.hasKey("physics") and data["physics"].kind == JObject:
let phys = data["physics"]
phys.getField("radius", result.radius)
phys.getField("mass", result.mass)
@ -489,10 +490,10 @@ proc importAnim(data: PJsonNode, errors: var seq[string]): PAnimationRecord =
result.delay = 1000.0
result.spriteSheet = nil
if data.existsKey("anim"):
if data.hasKey("anim"):
let anim = data["anim"]
if anim.kind == JObject:
if anim.existsKey("file"):
if anim.hasKey("file"):
result.spriteSheet = newSprite(anim["file"].str, errors)
anim.getField "angle", result.angle

View file

@ -55,7 +55,8 @@ idPacket(ZoneQuery, 'Q',
tuple[pad: char = '\0'])
type SpawnKind = enum
SpawnItem = 1'i8, SpawnVehicle, SpawnObject
SpawnDummy,
SpawnItem, SpawnVehicle, SpawnObject
forwardPacketT(SpawnKind, int8)
defPacket(ScSpawn, tuple[
kind: SpawnKind; id: uint16; record: uint16; amount: uint16])
@ -64,7 +65,8 @@ defPacket(ScSpawn, tuple[
type TAssetType* = enum
FZoneCfg = 1'i8, FGraphics, FSound
FDummy,
FZoneCfg, FGraphics, FSound
forwardPacketT(TAssetType, int8)
forwardPacket(MD5Digest, array[0..15, int8])

Some files were not shown because too many files have changed in this diff Show more