Merge remote-tracking branch 'upstream/master' into standalone
This commit is contained in:
commit
7dd9dbe4dd
119 changed files with 4593 additions and 1580 deletions
11
.gitignore
vendored
11
.gitignore
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -651,7 +651,9 @@ proc finishTypeDescriptions(m: BModule) =
|
|||
while i < len(m.typeStack):
|
||||
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)
|
||||
|
|
@ -692,7 +698,7 @@ proc isObjLackingTypeField(typ: PType): bool {.inline.} =
|
|||
result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
|
||||
(typ.sons[0] == nil) or isPureObject(typ))
|
||||
|
||||
proc genTypeInfoAuxBase(m: BModule, typ: PType, name, base: PRope) =
|
||||
proc genTypeInfoAuxBase(m: BModule, typ: PType, name, base: PRope) =
|
||||
var nimtypeKind: int
|
||||
#allocMemTI(m, typ, name)
|
||||
if isObjLackingTypeField(typ):
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -67,10 +67,9 @@ const
|
|||
# other idea: use a timeout! -> Wether code compiles depends on the machine
|
||||
# the compiler runs on then! Bad idea!
|
||||
|
||||
proc newStackFrame*(): PStackFrame =
|
||||
proc newStackFrame*(): PStackFrame =
|
||||
new(result)
|
||||
initIdNodeTable(result.mapping)
|
||||
result.params = @[]
|
||||
result.slots = @[]
|
||||
|
||||
proc newEvalContext*(module: PSym, mode: TEvalMode): PEvalContext =
|
||||
new(result)
|
||||
|
|
@ -287,62 +286,86 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
|
|||
result = newNodeIT(nkCurly, info, t)
|
||||
else: InternalError("getNullValue: " & $t.kind)
|
||||
|
||||
proc evalVar(c: PEvalContext, n: PNode): PNode =
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
var a = n.sons[i]
|
||||
if a.kind == nkCommentStmt: continue
|
||||
if a.kind != nkIdentDefs: return raiseCannotEval(c, n.info)
|
||||
# XXX var (x, y) = z support?
|
||||
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):
|
||||
let a = n.sons[i]
|
||||
if a.kind == nkCommentStmt: continue
|
||||
#assert(a.sons[0].kind == nkSym) can happen for transformed vars
|
||||
if a.sons[2].kind != nkEmpty:
|
||||
result = evalAux(c, a.sons[2], {})
|
||||
if result.kind in {nkType..nkNilLit}:
|
||||
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 =
|
||||
result = efLValue in flags or n.typ == nil or
|
||||
n.typ.kind in {tyExpr, tyStmt, tyTypeDesc}
|
||||
|
||||
proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode =
|
||||
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
|
||||
while x != nil:
|
||||
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)
|
||||
if result.kind == nkReturnToken:
|
||||
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
584
compiler/guards.nim
Normal 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
|
||||
|
|
@ -235,9 +235,9 @@ proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: TCharSet) =
|
|||
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):
|
||||
if s[i] in {'.', 'e', 'E'}:
|
||||
proc isFloatLiteral(s: string): bool =
|
||||
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'])
|
||||
inc(L.bufpos)
|
||||
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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -106,11 +106,13 @@ type
|
|||
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored,
|
||||
warnNilStatement, warnAnalysisLoophole,
|
||||
warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure,
|
||||
warnEachIdentIsTuple, warnShadowIdent, warnUser,
|
||||
hintSuccess, hintSuccessX,
|
||||
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
|
||||
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
|
||||
hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
|
||||
warnEachIdentIsTuple, warnShadowIdent,
|
||||
warnProveInit, warnProveField, warnProveIndex,
|
||||
warnUninit, warnUser,
|
||||
hintSuccess, hintSuccessX,
|
||||
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
|
||||
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
|
||||
hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
|
||||
hintConditionAlwaysTrue, hintPattern,
|
||||
hintUser
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ Files: "bin/empty.txt"
|
|||
|
||||
[Lib]
|
||||
Files: "lib/nimbase.h"
|
||||
Files: "lib/copying.txt"
|
||||
Files: "lib/*.nim"
|
||||
Files: "lib/*.cfg"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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!)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -147,7 +147,10 @@ proc expectIntLit(c: PContext, n: PNode): int =
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -493,13 +495,15 @@ proc putWithSpace(g: var TSrcGen, kind: TTokType, s: string) =
|
|||
|
||||
proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0,
|
||||
theEnd: int = - 1, separator = tkComma) =
|
||||
for i in countup(start, sonsLen(n) + theEnd):
|
||||
for i in countup(start, sonsLen(n) + theEnd):
|
||||
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 c:
|
||||
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:
|
||||
|
|
@ -724,7 +731,8 @@ proc gasm(g: var TSrcGen, n: PNode) =
|
|||
gcoms(g)
|
||||
gsub(g, n.sons[1])
|
||||
|
||||
proc gident(g: var TSrcGen, n: PNode) =
|
||||
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):
|
||||
|
|
@ -897,8 +905,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
|||
gsub(g, n.sons[pragmasPos])
|
||||
put(g, tkColon, ":")
|
||||
gsub(g, n.sons[bodyPos])
|
||||
of nkConstDef, nkIdentDefs:
|
||||
gcomma(g, n, 0, - 3)
|
||||
of nkConstDef, nkIdentDefs:
|
||||
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, "=")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -42,14 +43,6 @@ import
|
|||
# an array and filling it in parallel should be supported but is not easily
|
||||
# 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 -------------------------
|
||||
|
||||
|
|
@ -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
|
||||
track(tracked, n.sons[0])
|
||||
|
||||
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):
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
@ -385,9 +394,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
|||
addSon(b, a.sons[length-2]) # keep type desc for doc generator
|
||||
addSon(b, copyTree(def))
|
||||
addSon(result, b)
|
||||
else:
|
||||
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
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ proc newConstraint(c: PContext, k: TTypeKind): PType =
|
|||
|
||||
proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||
if n.sonsLen == 0: return newConstraint(c, tyEnum)
|
||||
var
|
||||
var
|
||||
counter, x: BiggestInt
|
||||
e: PSym
|
||||
base: PType
|
||||
|
|
@ -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)
|
||||
|
|
@ -386,34 +396,34 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
|
|||
swap(branch.sons[L-2], branch.sons[L-1])
|
||||
checkForOverlap(c, t, i, branchIndex)
|
||||
|
||||
proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
||||
father: PNode, rectype: PSym)
|
||||
proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
||||
father: PNode, rectype: PSym) =
|
||||
proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
||||
father: PNode, rectype: PType)
|
||||
proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
||||
father: PNode, rectype: PType) =
|
||||
var a = copyNode(n)
|
||||
checkMinSonsLen(n, 2)
|
||||
semRecordNodeAux(c, n.sons[0], check, pos, a, rectype)
|
||||
if a.sons[0].kind != nkSym:
|
||||
if a.sons[0].kind != nkSym:
|
||||
internalError("semRecordCase: discriminant is no symbol")
|
||||
return
|
||||
incl(a.sons[0].sym.flags, sfDiscriminant)
|
||||
var covered: biggestInt = 0
|
||||
var typ = skipTypes(a.sons[0].Typ, abstractVar-{tyTypeDesc})
|
||||
if not isOrdinalType(typ):
|
||||
if not isOrdinalType(typ):
|
||||
LocalError(n.info, errSelectorMustBeOrdinal)
|
||||
elif firstOrd(typ) < 0:
|
||||
elif firstOrd(typ) < 0:
|
||||
LocalError(n.info, errOrdXMustNotBeNegative, a.sons[0].sym.name.s)
|
||||
elif lengthOrd(typ) > 0x00007FFF:
|
||||
elif lengthOrd(typ) > 0x00007FFF:
|
||||
LocalError(n.info, errLenXinvalid, a.sons[0].sym.name.s)
|
||||
var chckCovered = true
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
var b = copyTree(n.sons[i])
|
||||
addSon(a, b)
|
||||
case n.sons[i].kind
|
||||
of nkOfBranch:
|
||||
of nkOfBranch:
|
||||
checkMinSonsLen(b, 2)
|
||||
semCaseBranch(c, a, b, i, covered)
|
||||
of nkElse:
|
||||
of nkElse:
|
||||
chckCovered = false
|
||||
checkSonsLen(b, 1)
|
||||
else: illFormedAst(n)
|
||||
|
|
@ -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:
|
||||
|
|
@ -463,7 +473,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
|||
semRecordCase(c, n, check, pos, father, rectype)
|
||||
of nkNilLit:
|
||||
if father.kind != nkRecList: addSon(father, newNodeI(nkRecList, n.info))
|
||||
of nkRecList:
|
||||
of nkRecList:
|
||||
# attempt to keep the nesting at a sane level:
|
||||
var a = if father.kind == nkRecList: father else: copyNode(n)
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
|
|
@ -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,17 +493,19 @@ 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):
|
||||
if ContainsOrIncl(check, f.name.id):
|
||||
localError(n.sons[i].info, errAttemptToRedefine, f.name.s)
|
||||
if a.kind == nkEmpty: addSon(father, newSymNode(f))
|
||||
else: addSon(a, newSymNode(f))
|
||||
|
|
@ -502,20 +514,20 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
|
|||
else: illFormedAst(n)
|
||||
|
||||
proc addInheritedFieldsAux(c: PContext, check: var TIntSet, pos: var int,
|
||||
n: PNode) =
|
||||
n: PNode) =
|
||||
case n.kind
|
||||
of nkRecCase:
|
||||
of nkRecCase:
|
||||
if (n.sons[0].kind != nkSym): InternalError(n.info, "addInheritedFieldsAux")
|
||||
addInheritedFieldsAux(c, check, pos, n.sons[0])
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
case n.sons[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
of nkOfBranch, nkElse:
|
||||
addInheritedFieldsAux(c, check, pos, lastSon(n.sons[i]))
|
||||
else: internalError(n.info, "addInheritedFieldsAux(record case branch)")
|
||||
of nkRecList:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
of nkRecList:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
addInheritedFieldsAux(c, check, pos, n.sons[i])
|
||||
of nkSym:
|
||||
of nkSym:
|
||||
Incl(check, n.sym.name.id)
|
||||
inc(pos)
|
||||
else: InternalError(n.info, "addInheritedFieldsAux()")
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ type
|
|||
isGeneric,
|
||||
isFromIntLit, # conversion *from* int literal; proven safe
|
||||
isEqual
|
||||
|
||||
const
|
||||
isNilConversion = isConvertible # maybe 'isIntConv' fits better?
|
||||
|
||||
proc markUsed*(n: PNode, s: PSym)
|
||||
|
||||
|
|
@ -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 = ""
|
||||
|
|
@ -196,7 +208,7 @@ proc NotFoundError*(c: PContext, n: PNode) =
|
|||
add(candidates, getProcHeader(sym))
|
||||
add(candidates, "\n")
|
||||
sym = nextOverloadIter(o, c, n.sons[0])
|
||||
if candidates != "":
|
||||
if candidates != "":
|
||||
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
|
||||
LocalError(n.Info, errGenerated, result)
|
||||
|
||||
|
|
@ -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)
|
||||
of tyPointer:
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -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,8 +514,8 @@ 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}):
|
||||
# fix a stupid code gen bug by normalizing:
|
||||
{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)
|
||||
add(result, elseBranch)
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -186,4 +186,4 @@ complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt
|
|||
| bindStmt | mixinStmt)
|
||||
/ simpleStmt
|
||||
stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED)
|
||||
/ simpleStmt
|
||||
/ simpleStmt ^+ ';'
|
||||
|
|
|
|||
467
doc/idetools.txt
Normal file
467
doc/idetools.txt
Normal 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: ""
|
||||
16
doc/lib.txt
16
doc/lib.txt
|
|
@ -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
|
||||
--------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
--------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -399,6 +399,21 @@ interfacing with libraries written in Objective C:
|
|||
The compiler needs to be told to generate Objective C (command ``objc``) for
|
||||
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
|
||||
|
|
@ -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
|
||||
=======================
|
||||
|
||||
|
|
|
|||
15
doc/tut1.txt
15
doc/tut1.txt
|
|
@ -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
|
||||
---------
|
||||
|
|
|
|||
17
koch.nim
17
koch.nim
|
|
@ -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,9 +88,9 @@ proc inno(args: string) =
|
|||
NimrodVersion)
|
||||
|
||||
proc install(args: string) =
|
||||
exec("nimrod cc -r tools/niminst/niminst --var:version=$# scripts compiler/nimrod.ini" %
|
||||
NimrodVersion)
|
||||
exec("sh ./install.sh $#" % args)
|
||||
exec("nimrod cc -r $# --var:version=$# scripts compiler/nimrod.ini" %
|
||||
[compileNimInst, NimrodVersion])
|
||||
exec("sh ./install.sh $#" % args)
|
||||
|
||||
proc web(args: string) =
|
||||
exec(("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" &
|
||||
|
|
|
|||
|
|
@ -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 ]
|
||||
|
|
@ -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`.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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 ------------------------------
|
||||
|
|
|
|||
|
|
@ -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) \
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -692,4 +692,4 @@ proc rstToHtml*(s: string, options: TRstParseOptions,
|
|||
var rst = rstParse(s, filen, 0, 1, dummyHasToc, options)
|
||||
result = ""
|
||||
renderRstToOut(d, rst, result)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 ``Ü`` 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,16 +532,20 @@ 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))
|
||||
if x.bufpos == oldPos:
|
||||
if x.bufpos == oldPos:
|
||||
# force progress!
|
||||
next(x)
|
||||
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
|
||||
|
|
@ -400,22 +564,9 @@ proc loadHtml*(path: string, errors: var seq[string]): PXmlNode =
|
|||
proc loadHtml*(path: string): PXmlNode =
|
||||
## Loads and parses HTML from file specified by ``path``, and returns
|
||||
## a ``PXmlNode``. All parsing errors are ignored.
|
||||
var errors: seq[string] = @[]
|
||||
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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
179
lib/pure/os.nim
179
lib/pure/os.nim
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 ------------------------------
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
||||
# Strip ':'
|
||||
if parseBiggestInt(line, result, 1) == 0:
|
||||
raise newException(EInvalidReply, "Unable to parse integer.")
|
||||
else: OSError()
|
||||
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.")
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,312 +1,310 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module contains a few procedures to control the *terminal*
|
||||
## (also called *console*). On UNIX, the implementation simply uses ANSI escape
|
||||
## sequences and does not depend on any other module, on Windows it uses the
|
||||
## Windows API.
|
||||
## Changing the style is permanent even after program termination! Use the
|
||||
## code ``system.addQuitProc(resetAttributes)`` to restore the defaults.
|
||||
|
||||
import macros
|
||||
|
||||
when defined(windows):
|
||||
import windows, os
|
||||
|
||||
var
|
||||
conHandle: THandle
|
||||
# = createFile("CONOUT$", GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0)
|
||||
|
||||
block:
|
||||
var hTemp = GetStdHandle(STD_OUTPUT_HANDLE)
|
||||
if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(),
|
||||
addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
|
||||
OSError()
|
||||
|
||||
proc getCursorPos(): tuple [x,y: int] =
|
||||
var c: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: OSError()
|
||||
return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y))
|
||||
|
||||
proc getAttributes(): int16 =
|
||||
var c: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
# 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
|
||||
oldAttr = getAttributes()
|
||||
|
||||
proc setCursorPos*(x, y: int) =
|
||||
## sets the terminal's cursor to the (x,y) position. (0,0) is the
|
||||
## upper left of the screen.
|
||||
when defined(windows):
|
||||
var c: TCoord
|
||||
c.x = int16(x)
|
||||
c.y = int16(y)
|
||||
if SetConsoleCursorPosition(conHandle, c) == 0: OSError()
|
||||
else:
|
||||
stdout.write("\e[" & $y & ';' & $x & 'f')
|
||||
|
||||
proc setCursorXPos*(x: int) =
|
||||
## sets the terminal's cursor to the x position. The y position is
|
||||
## not changed.
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError()
|
||||
var origin = scrbuf.dwCursorPosition
|
||||
origin.x = int16(x)
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError()
|
||||
else:
|
||||
stdout.write("\e[" & $x & 'G')
|
||||
|
||||
when defined(windows):
|
||||
proc setCursorYPos*(y: int) =
|
||||
## sets the terminal's cursor to the y position. The x position is
|
||||
## not changed. **Warning**: This is not supported on UNIX!
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError()
|
||||
var origin = scrbuf.dwCursorPosition
|
||||
origin.y = int16(y)
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError()
|
||||
else:
|
||||
nil
|
||||
|
||||
proc CursorUp*(count=1) =
|
||||
## Moves the cursor up by `count` rows.
|
||||
when defined(windows):
|
||||
var p = getCursorPos()
|
||||
dec(p.y, count)
|
||||
setCursorPos(p.x, p.y)
|
||||
else:
|
||||
stdout.write("\e[" & $count & 'A')
|
||||
|
||||
proc CursorDown*(count=1) =
|
||||
## Moves the cursor down by `count` rows.
|
||||
when defined(windows):
|
||||
var p = getCursorPos()
|
||||
inc(p.y, count)
|
||||
setCursorPos(p.x, p.y)
|
||||
else:
|
||||
stdout.write("\e[" & $count & 'B')
|
||||
|
||||
proc CursorForward*(count=1) =
|
||||
## Moves the cursor forward by `count` columns.
|
||||
when defined(windows):
|
||||
var p = getCursorPos()
|
||||
inc(p.x, count)
|
||||
setCursorPos(p.x, p.y)
|
||||
else:
|
||||
stdout.write("\e[" & $count & 'C')
|
||||
|
||||
proc CursorBackward*(count=1) =
|
||||
## Moves the cursor backward by `count` columns.
|
||||
when defined(windows):
|
||||
var p = getCursorPos()
|
||||
dec(p.x, count)
|
||||
setCursorPos(p.x, p.y)
|
||||
else:
|
||||
stdout.write("\e[" & $count & 'D')
|
||||
|
||||
when true:
|
||||
nil
|
||||
else:
|
||||
proc EraseLineEnd* =
|
||||
## Erases from the current cursor position to the end of the current line.
|
||||
when defined(windows):
|
||||
nil
|
||||
else:
|
||||
stdout.write("\e[K")
|
||||
|
||||
proc EraseLineStart* =
|
||||
## Erases from the current cursor position to the start of the current line.
|
||||
when defined(windows):
|
||||
nil
|
||||
else:
|
||||
stdout.write("\e[1K")
|
||||
|
||||
proc EraseDown* =
|
||||
## Erases the screen from the current line down to the bottom of the screen.
|
||||
when defined(windows):
|
||||
nil
|
||||
else:
|
||||
stdout.write("\e[J")
|
||||
|
||||
proc EraseUp* =
|
||||
## Erases the screen from the current line up to the top of the screen.
|
||||
when defined(windows):
|
||||
nil
|
||||
else:
|
||||
stdout.write("\e[1J")
|
||||
|
||||
proc EraseLine* =
|
||||
## Erases the entire current line.
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
var numwrote: DWORD
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError()
|
||||
var origin = scrbuf.dwCursorPosition
|
||||
origin.x = 0'i16
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError()
|
||||
var ht = scrbuf.dwSize.Y - origin.Y
|
||||
var wt = scrbuf.dwSize.X - origin.X
|
||||
if FillConsoleOutputCharacter(hStdout,' ', ht*wt,
|
||||
origin, addr(numwrote)) == 0:
|
||||
OSError()
|
||||
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt,
|
||||
scrbuf.dwCursorPosition, addr(numwrote)) == 0:
|
||||
OSError()
|
||||
else:
|
||||
stdout.write("\e[2K")
|
||||
setCursorXPos(0)
|
||||
|
||||
proc EraseScreen* =
|
||||
## Erases the screen with the background colour and moves the cursor to home.
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
var numwrote: DWORD
|
||||
var origin: TCoord # is inititalized to 0, 0
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError()
|
||||
if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y,
|
||||
origin, addr(numwrote)) == 0:
|
||||
OSError()
|
||||
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes,
|
||||
scrbuf.dwSize.X * scrbuf.dwSize.Y,
|
||||
origin, addr(numwrote)) == 0:
|
||||
OSError()
|
||||
setCursorXPos(0)
|
||||
else:
|
||||
stdout.write("\e[2J")
|
||||
|
||||
proc ResetAttributes* {.noconv.} =
|
||||
## resets all attributes; it is advisable to register this as a quit proc
|
||||
## with ``system.addQuitProc(resetAttributes)``.
|
||||
when defined(windows):
|
||||
discard SetConsoleTextAttribute(conHandle, oldAttr)
|
||||
else:
|
||||
stdout.write("\e[0m")
|
||||
|
||||
type
|
||||
TStyle* = enum ## different styles for text output
|
||||
styleBright = 1, ## bright text
|
||||
styleDim, ## dim text
|
||||
styleUnknown, ## unknown
|
||||
styleUnderscore = 4, ## underscored text
|
||||
styleBlink, ## blinking/bold text
|
||||
styleReverse = 7, ## unknown
|
||||
styleHidden ## hidden text
|
||||
|
||||
when not defined(windows):
|
||||
var
|
||||
# XXX: These better be thread-local
|
||||
gFG = 0
|
||||
gBG = 0
|
||||
|
||||
proc setStyle*(style: set[TStyle]) =
|
||||
## sets the terminal style
|
||||
when defined(windows):
|
||||
var a = 0'i16
|
||||
if styleBright in style: a = a or int16(FOREGROUND_INTENSITY)
|
||||
if styleBlink in style: a = a or int16(BACKGROUND_INTENSITY)
|
||||
if styleReverse in style: a = a or 0x4000'i16 # COMMON_LVB_REVERSE_VIDEO
|
||||
if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE
|
||||
discard SetConsoleTextAttribute(conHandle, a)
|
||||
else:
|
||||
for s in items(style):
|
||||
stdout.write("\e[" & $ord(s) & 'm')
|
||||
|
||||
proc WriteStyled*(txt: string, style: set[TStyle] = {styleBright}) =
|
||||
## writes the text `txt` in a given `style`.
|
||||
when defined(windows):
|
||||
var old = getAttributes()
|
||||
setStyle(style)
|
||||
stdout.write(txt)
|
||||
discard SetConsoleTextAttribute(conHandle, old)
|
||||
else:
|
||||
setStyle(style)
|
||||
stdout.write(txt)
|
||||
resetAttributes()
|
||||
if gFG != 0:
|
||||
stdout.write("\e[" & $ord(gFG) & 'm')
|
||||
if gBG != 0:
|
||||
stdout.write("\e[" & $ord(gBG) & 'm')
|
||||
|
||||
type
|
||||
TForegroundColor* = enum ## terminal's foreground colors
|
||||
fgBlack = 30, ## black
|
||||
fgRed, ## red
|
||||
fgGreen, ## green
|
||||
fgYellow, ## yellow
|
||||
fgBlue, ## blue
|
||||
fgMagenta, ## magenta
|
||||
fgCyan, ## cyan
|
||||
fgWhite ## white
|
||||
|
||||
TBackgroundColor* = enum ## terminal's background colors
|
||||
bgBlack = 40, ## black
|
||||
bgRed, ## red
|
||||
bgGreen, ## green
|
||||
bgYellow, ## yellow
|
||||
bgBlue, ## blue
|
||||
bgMagenta, ## magenta
|
||||
bgCyan, ## cyan
|
||||
bgWhite ## white
|
||||
|
||||
proc setForegroundColor*(fg: TForegroundColor, bright=false) =
|
||||
## sets the terminal's foreground color
|
||||
when defined(windows):
|
||||
var old = getAttributes() and not 0x0007
|
||||
if bright:
|
||||
old = old or FOREGROUND_INTENSITY
|
||||
const lookup: array [TForegroundColor, int] = [
|
||||
0,
|
||||
(FOREGROUND_RED),
|
||||
(FOREGROUND_GREEN),
|
||||
(FOREGROUND_RED or FOREGROUND_GREEN),
|
||||
(FOREGROUND_BLUE),
|
||||
(FOREGROUND_RED or FOREGROUND_BLUE),
|
||||
(FOREGROUND_BLUE or FOREGROUND_GREEN),
|
||||
(FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)]
|
||||
discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[fg]))
|
||||
else:
|
||||
gFG = ord(fg)
|
||||
if bright: inc(gFG, 60)
|
||||
stdout.write("\e[" & $gFG & 'm')
|
||||
|
||||
proc setBackgroundColor*(bg: TBackgroundColor, bright=false) =
|
||||
## sets the terminal's background color
|
||||
when defined(windows):
|
||||
var old = getAttributes() and not 0x0070
|
||||
if bright:
|
||||
old = old or BACKGROUND_INTENSITY
|
||||
const lookup: array [TBackgroundColor, int] = [
|
||||
0,
|
||||
(BACKGROUND_RED),
|
||||
(BACKGROUND_GREEN),
|
||||
(BACKGROUND_RED or BACKGROUND_GREEN),
|
||||
(BACKGROUND_BLUE),
|
||||
(BACKGROUND_RED or BACKGROUND_BLUE),
|
||||
(BACKGROUND_BLUE or BACKGROUND_GREEN),
|
||||
(BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)]
|
||||
discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[bg]))
|
||||
else:
|
||||
gBG = ord(bg)
|
||||
if bright: inc(gBG, 60)
|
||||
stdout.write("\e[" & $gBG & 'm')
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module contains a few procedures to control the *terminal*
|
||||
## (also called *console*). On UNIX, the implementation simply uses ANSI escape
|
||||
## sequences and does not depend on any other module, on Windows it uses the
|
||||
## Windows API.
|
||||
## Changing the style is permanent even after program termination! Use the
|
||||
## code ``system.addQuitProc(resetAttributes)`` to restore the defaults.
|
||||
|
||||
import macros
|
||||
|
||||
when defined(windows):
|
||||
import windows, os
|
||||
|
||||
var
|
||||
conHandle: THandle
|
||||
# = createFile("CONOUT$", GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0)
|
||||
|
||||
block:
|
||||
var hTemp = GetStdHandle(STD_OUTPUT_HANDLE)
|
||||
if DuplicateHandle(GetCurrentProcess(), hTemp, GetCurrentProcess(),
|
||||
addr(conHandle), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
|
||||
OSError(OSLastError())
|
||||
|
||||
proc getCursorPos(): tuple [x,y: int] =
|
||||
var c: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
if GetConsoleScreenBufferInfo(conHandle, addr(c)) == 0: OSError(OSLastError())
|
||||
return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y))
|
||||
|
||||
proc getAttributes(): int16 =
|
||||
var c: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
# workaround Windows bugs: try several times
|
||||
if GetConsoleScreenBufferInfo(conHandle, addr(c)) != 0:
|
||||
return c.wAttributes
|
||||
return 0x70'i16 # ERROR: return white background, black text
|
||||
|
||||
var
|
||||
oldAttr = getAttributes()
|
||||
|
||||
proc setCursorPos*(x, y: int) =
|
||||
## sets the terminal's cursor to the (x,y) position. (0,0) is the
|
||||
## upper left of the screen.
|
||||
when defined(windows):
|
||||
var c: TCoord
|
||||
c.x = int16(x)
|
||||
c.y = int16(y)
|
||||
if SetConsoleCursorPosition(conHandle, c) == 0: OSError(OSLastError())
|
||||
else:
|
||||
stdout.write("\e[" & $y & ';' & $x & 'f')
|
||||
|
||||
proc setCursorXPos*(x: int) =
|
||||
## sets the terminal's cursor to the x position. The y position is
|
||||
## not changed.
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
|
||||
var origin = scrbuf.dwCursorPosition
|
||||
origin.x = int16(x)
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError())
|
||||
else:
|
||||
stdout.write("\e[" & $x & 'G')
|
||||
|
||||
when defined(windows):
|
||||
proc setCursorYPos*(y: int) =
|
||||
## sets the terminal's cursor to the y position. The x position is
|
||||
## not changed. **Warning**: This is not supported on UNIX!
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
|
||||
var origin = scrbuf.dwCursorPosition
|
||||
origin.y = int16(y)
|
||||
if SetConsoleCursorPosition(conHandle, origin) == 0: OSError(OSLastError())
|
||||
else:
|
||||
nil
|
||||
|
||||
proc CursorUp*(count=1) =
|
||||
## Moves the cursor up by `count` rows.
|
||||
when defined(windows):
|
||||
var p = getCursorPos()
|
||||
dec(p.y, count)
|
||||
setCursorPos(p.x, p.y)
|
||||
else:
|
||||
stdout.write("\e[" & $count & 'A')
|
||||
|
||||
proc CursorDown*(count=1) =
|
||||
## Moves the cursor down by `count` rows.
|
||||
when defined(windows):
|
||||
var p = getCursorPos()
|
||||
inc(p.y, count)
|
||||
setCursorPos(p.x, p.y)
|
||||
else:
|
||||
stdout.write("\e[" & $count & 'B')
|
||||
|
||||
proc CursorForward*(count=1) =
|
||||
## Moves the cursor forward by `count` columns.
|
||||
when defined(windows):
|
||||
var p = getCursorPos()
|
||||
inc(p.x, count)
|
||||
setCursorPos(p.x, p.y)
|
||||
else:
|
||||
stdout.write("\e[" & $count & 'C')
|
||||
|
||||
proc CursorBackward*(count=1) =
|
||||
## Moves the cursor backward by `count` columns.
|
||||
when defined(windows):
|
||||
var p = getCursorPos()
|
||||
dec(p.x, count)
|
||||
setCursorPos(p.x, p.y)
|
||||
else:
|
||||
stdout.write("\e[" & $count & 'D')
|
||||
|
||||
when true:
|
||||
nil
|
||||
else:
|
||||
proc EraseLineEnd* =
|
||||
## Erases from the current cursor position to the end of the current line.
|
||||
when defined(windows):
|
||||
nil
|
||||
else:
|
||||
stdout.write("\e[K")
|
||||
|
||||
proc EraseLineStart* =
|
||||
## Erases from the current cursor position to the start of the current line.
|
||||
when defined(windows):
|
||||
nil
|
||||
else:
|
||||
stdout.write("\e[1K")
|
||||
|
||||
proc EraseDown* =
|
||||
## Erases the screen from the current line down to the bottom of the screen.
|
||||
when defined(windows):
|
||||
nil
|
||||
else:
|
||||
stdout.write("\e[J")
|
||||
|
||||
proc EraseUp* =
|
||||
## Erases the screen from the current line up to the top of the screen.
|
||||
when defined(windows):
|
||||
nil
|
||||
else:
|
||||
stdout.write("\e[1J")
|
||||
|
||||
proc EraseLine* =
|
||||
## Erases the entire current line.
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
var numwrote: DWORD
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
|
||||
var origin = scrbuf.dwCursorPosition
|
||||
origin.x = 0'i16
|
||||
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(OSLastError())
|
||||
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes, ht * wt,
|
||||
scrbuf.dwCursorPosition, addr(numwrote)) == 0:
|
||||
OSError(OSLastError())
|
||||
else:
|
||||
stdout.write("\e[2K")
|
||||
setCursorXPos(0)
|
||||
|
||||
proc EraseScreen* =
|
||||
## Erases the screen with the background colour and moves the cursor to home.
|
||||
when defined(windows):
|
||||
var scrbuf: TCONSOLE_SCREEN_BUFFER_INFO
|
||||
var numwrote: DWORD
|
||||
var origin: TCoord # is inititalized to 0, 0
|
||||
var hStdout = conHandle
|
||||
if GetConsoleScreenBufferInfo(hStdout, addr(scrbuf)) == 0: OSError(OSLastError())
|
||||
if FillConsoleOutputCharacter(hStdout, ' ', scrbuf.dwSize.X*scrbuf.dwSize.Y,
|
||||
origin, addr(numwrote)) == 0:
|
||||
OSError(OSLastError())
|
||||
if FillConsoleOutputAttribute(hStdout, scrbuf.wAttributes,
|
||||
scrbuf.dwSize.X * scrbuf.dwSize.Y,
|
||||
origin, addr(numwrote)) == 0:
|
||||
OSError(OSLastError())
|
||||
setCursorXPos(0)
|
||||
else:
|
||||
stdout.write("\e[2J")
|
||||
|
||||
proc ResetAttributes* {.noconv.} =
|
||||
## resets all attributes; it is advisable to register this as a quit proc
|
||||
## with ``system.addQuitProc(resetAttributes)``.
|
||||
when defined(windows):
|
||||
discard SetConsoleTextAttribute(conHandle, oldAttr)
|
||||
else:
|
||||
stdout.write("\e[0m")
|
||||
|
||||
type
|
||||
TStyle* = enum ## different styles for text output
|
||||
styleBright = 1, ## bright text
|
||||
styleDim, ## dim text
|
||||
styleUnknown, ## unknown
|
||||
styleUnderscore = 4, ## underscored text
|
||||
styleBlink, ## blinking/bold text
|
||||
styleReverse = 7, ## unknown
|
||||
styleHidden ## hidden text
|
||||
|
||||
when not defined(windows):
|
||||
var
|
||||
# XXX: These better be thread-local
|
||||
gFG = 0
|
||||
gBG = 0
|
||||
|
||||
proc setStyle*(style: set[TStyle]) =
|
||||
## sets the terminal style
|
||||
when defined(windows):
|
||||
var a = 0'i16
|
||||
if styleBright in style: a = a or int16(FOREGROUND_INTENSITY)
|
||||
if styleBlink in style: a = a or int16(BACKGROUND_INTENSITY)
|
||||
if styleReverse in style: a = a or 0x4000'i16 # COMMON_LVB_REVERSE_VIDEO
|
||||
if styleUnderscore in style: a = a or 0x8000'i16 # COMMON_LVB_UNDERSCORE
|
||||
discard SetConsoleTextAttribute(conHandle, a)
|
||||
else:
|
||||
for s in items(style):
|
||||
stdout.write("\e[" & $ord(s) & 'm')
|
||||
|
||||
proc WriteStyled*(txt: string, style: set[TStyle] = {styleBright}) =
|
||||
## writes the text `txt` in a given `style`.
|
||||
when defined(windows):
|
||||
var old = getAttributes()
|
||||
setStyle(style)
|
||||
stdout.write(txt)
|
||||
discard SetConsoleTextAttribute(conHandle, old)
|
||||
else:
|
||||
setStyle(style)
|
||||
stdout.write(txt)
|
||||
resetAttributes()
|
||||
if gFG != 0:
|
||||
stdout.write("\e[" & $ord(gFG) & 'm')
|
||||
if gBG != 0:
|
||||
stdout.write("\e[" & $ord(gBG) & 'm')
|
||||
|
||||
type
|
||||
TForegroundColor* = enum ## terminal's foreground colors
|
||||
fgBlack = 30, ## black
|
||||
fgRed, ## red
|
||||
fgGreen, ## green
|
||||
fgYellow, ## yellow
|
||||
fgBlue, ## blue
|
||||
fgMagenta, ## magenta
|
||||
fgCyan, ## cyan
|
||||
fgWhite ## white
|
||||
|
||||
TBackgroundColor* = enum ## terminal's background colors
|
||||
bgBlack = 40, ## black
|
||||
bgRed, ## red
|
||||
bgGreen, ## green
|
||||
bgYellow, ## yellow
|
||||
bgBlue, ## blue
|
||||
bgMagenta, ## magenta
|
||||
bgCyan, ## cyan
|
||||
bgWhite ## white
|
||||
|
||||
proc setForegroundColor*(fg: TForegroundColor, bright=false) =
|
||||
## sets the terminal's foreground color
|
||||
when defined(windows):
|
||||
var old = getAttributes() and not 0x0007
|
||||
if bright:
|
||||
old = old or FOREGROUND_INTENSITY
|
||||
const lookup: array [TForegroundColor, int] = [
|
||||
0,
|
||||
(FOREGROUND_RED),
|
||||
(FOREGROUND_GREEN),
|
||||
(FOREGROUND_RED or FOREGROUND_GREEN),
|
||||
(FOREGROUND_BLUE),
|
||||
(FOREGROUND_RED or FOREGROUND_BLUE),
|
||||
(FOREGROUND_BLUE or FOREGROUND_GREEN),
|
||||
(FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED)]
|
||||
discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[fg]))
|
||||
else:
|
||||
gFG = ord(fg)
|
||||
if bright: inc(gFG, 60)
|
||||
stdout.write("\e[" & $gFG & 'm')
|
||||
|
||||
proc setBackgroundColor*(bg: TBackgroundColor, bright=false) =
|
||||
## sets the terminal's background color
|
||||
when defined(windows):
|
||||
var old = getAttributes() and not 0x0070
|
||||
if bright:
|
||||
old = old or BACKGROUND_INTENSITY
|
||||
const lookup: array [TBackgroundColor, int] = [
|
||||
0,
|
||||
(BACKGROUND_RED),
|
||||
(BACKGROUND_GREEN),
|
||||
(BACKGROUND_RED or BACKGROUND_GREEN),
|
||||
(BACKGROUND_BLUE),
|
||||
(BACKGROUND_RED or BACKGROUND_BLUE),
|
||||
(BACKGROUND_BLUE or BACKGROUND_GREEN),
|
||||
(BACKGROUND_BLUE or BACKGROUND_GREEN or BACKGROUND_RED)]
|
||||
discard SetConsoleTextAttribute(conHandle, toU16(old or lookup[bg]))
|
||||
else:
|
||||
gBG = ord(bg)
|
||||
if bright: inc(gBG, 60)
|
||||
stdout.write("\e[" & $gBG & 'm')
|
||||
|
||||
proc isatty*(f: TFile): bool =
|
||||
## returns true if `f` is associated with a terminal device.
|
||||
|
|
@ -318,33 +316,33 @@ proc isatty*(f: TFile): bool =
|
|||
importc: "_isatty", header: "<io.h>".}
|
||||
|
||||
result = isatty(fileHandle(f)) != 0'i32
|
||||
|
||||
proc styledEchoProcessArg(s: string) = write stdout, s
|
||||
proc styledEchoProcessArg(style: TStyle) = setStyle({style})
|
||||
proc styledEchoProcessArg(style: set[TStyle]) = setStyle style
|
||||
proc styledEchoProcessArg(color: TForegroundColor) = setForeGroundColor color
|
||||
proc styledEchoProcessArg(color: TBackgroundColor) = setBackGroundColor color
|
||||
|
||||
|
||||
proc styledEchoProcessArg(s: string) = write stdout, s
|
||||
proc styledEchoProcessArg(style: TStyle) = setStyle({style})
|
||||
proc styledEchoProcessArg(style: set[TStyle]) = setStyle style
|
||||
proc styledEchoProcessArg(color: TForegroundColor) = setForeGroundColor color
|
||||
proc styledEchoProcessArg(color: TBackgroundColor) = setBackGroundColor color
|
||||
|
||||
macro styledEcho*(m: varargs[expr]): stmt =
|
||||
## to be documented.
|
||||
let m = callsite()
|
||||
result = newNimNode(nnkStmtList)
|
||||
|
||||
for i in countup(1, m.len - 1):
|
||||
result.add(newCall(bindSym"styledEchoProcessArg", m[i]))
|
||||
|
||||
result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n")))
|
||||
result.add(newCall(bindSym"resetAttributes"))
|
||||
|
||||
when isMainModule:
|
||||
system.addQuitProc(resetAttributes)
|
||||
write(stdout, "never mind")
|
||||
eraseLine()
|
||||
#setCursorPos(2, 2)
|
||||
writeStyled("styled text ", {styleBright, styleBlink, styleUnderscore})
|
||||
setBackGroundColor(bgCyan, true)
|
||||
setForeGroundColor(fgBlue)
|
||||
writeln(stdout, "ordinary text")
|
||||
|
||||
result = newNimNode(nnkStmtList)
|
||||
|
||||
for i in countup(1, m.len - 1):
|
||||
result.add(newCall(bindSym"styledEchoProcessArg", m[i]))
|
||||
|
||||
result.add(newCall(bindSym"write", bindSym"stdout", newStrLitNode("\n")))
|
||||
result.add(newCall(bindSym"resetAttributes"))
|
||||
|
||||
when isMainModule:
|
||||
system.addQuitProc(resetAttributes)
|
||||
write(stdout, "never mind")
|
||||
eraseLine()
|
||||
#setCursorPos(2, 2)
|
||||
writeStyled("styled text ", {styleBright, styleBlink, styleUnderscore})
|
||||
setBackGroundColor(bgCyan, true)
|
||||
setForeGroundColor(fgBlue)
|
||||
writeln(stdout, "ordinary text")
|
||||
|
||||
styledEcho("styled text ", {styleBright, styleBlink, styleUnderscore})
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ when defined(Windows):
|
|||
OwningThread: int
|
||||
LockSemaphore: int
|
||||
Reserved: int32
|
||||
|
||||
|
||||
TSysCond = THandle
|
||||
|
||||
proc InitSysLock(L: var TSysLock) {.stdcall, noSideEffect,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
import
|
||||
glib2, atk, pango, gdk2pixbuf, gdk2
|
||||
|
||||
export gbool, toBool
|
||||
|
||||
when defined(win32):
|
||||
const
|
||||
lib = "libgtk-win32-2.0-0.dll"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
# ****************************************************************
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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".}
|
||||
|
|
|
|||
29
tests/caas/absurd_nesting.nim
Normal file
29
tests/caas/absurd_nesting.nim
Normal 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")
|
||||
29
tests/caas/absurd_nesting.txt
Normal file
29
tests/caas/absurd_nesting.txt
Normal 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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
15
tests/caas/forward_declarations.nim
Normal file
15
tests/caas/forward_declarations.nim
Normal 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()
|
||||
9
tests/caas/forward_declarations.txt
Normal file
9
tests/caas/forward_declarations.txt
Normal 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\)
|
||||
17
tests/caas/forward_usages.txt
Normal file
17
tests/caas/forward_usages.txt
Normal 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
|
||||
43
tests/caas/idetools_api.nim
Normal file
43
tests/caas/idetools_api.nim
Normal 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)))
|
||||
44
tests/caas/idetools_api.txt
Normal file
44
tests/caas/idetools_api.txt
Normal 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
|
||||
17
tests/caas/issue_416_template_shift.nim
Normal file
17
tests/caas/issue_416_template_shift.nim
Normal 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()
|
||||
14
tests/caas/issue_416_template_shift.txt
Normal file
14
tests/caas/issue_416_template_shift.txt
Normal 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
|
||||
|
||||
8
tests/caas/issue_452_export_shift.nim
Normal file
8
tests/caas/issue_452_export_shift.nim
Normal 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 = ""
|
||||
11
tests/caas/issue_452_export_shift.txt
Normal file
11
tests/caas/issue_452_export_shift.txt
Normal 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
|
||||
19
tests/caas/issue_477_dynamic_dispatch.nim
Normal file
19
tests/caas/issue_477_dynamic_dispatch.nim
Normal 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
|
||||
5
tests/caas/issue_477_dynamic_dispatch.txt
Normal file
5
tests/caas/issue_477_dynamic_dispatch.txt
Normal 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.\}
|
||||
29
tests/caas/its_full_of_procs.nim
Normal file
29
tests/caas/its_full_of_procs.nim
Normal 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
|
||||
20
tests/caas/its_full_of_procs.txt
Normal file
20
tests/caas/its_full_of_procs.txt
Normal 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 \(
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +1,101 @@
|
|||
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 = ""
|
||||
|
||||
|
||||
while true:
|
||||
var line = TaintedString("")
|
||||
if session.nim.outputStream.readLine(line):
|
||||
|
|
@ -30,38 +105,81 @@ 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)
|
||||
var project = TaintedString("")
|
||||
|
||||
|
||||
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)
|
||||
|
||||
for t, o, r in caasTestsRunner(filter):
|
||||
echo t, "\n", o
|
||||
|
||||
var
|
||||
filter = ""
|
||||
failures = 0
|
||||
verbose = false
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ proc parseInt(x: int8): int {.noSideEffect.} = nil
|
|||
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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
494
tests/manyloc/argument_parser/argument_parser.nim
Normal file
494
tests/manyloc/argument_parser/argument_parser.nim
Normal 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")
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue