Resolve conflic.
This commit is contained in:
commit
9a67d0c1e4
52 changed files with 666 additions and 234 deletions
|
|
@ -82,7 +82,7 @@ template mdbg*: bool {.dirty.} =
|
||||||
elif compiles(L.fileIdx):
|
elif compiles(L.fileIdx):
|
||||||
L.fileIdx == gProjectMainIdx
|
L.fileIdx == gProjectMainIdx
|
||||||
else:
|
else:
|
||||||
false
|
error()
|
||||||
|
|
||||||
# --------------------------- ident tables ----------------------------------
|
# --------------------------- ident tables ----------------------------------
|
||||||
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
|
proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
|
||||||
|
|
|
||||||
|
|
@ -604,6 +604,7 @@ proc generateHeaders(m: BModule) =
|
||||||
else:
|
else:
|
||||||
addf(m.s[cfsHeaders], "#include $1$N", [rope(it)])
|
addf(m.s[cfsHeaders], "#include $1$N", [rope(it)])
|
||||||
add(m.s[cfsHeaders], "#undef linux" & tnl)
|
add(m.s[cfsHeaders], "#undef linux" & tnl)
|
||||||
|
add(m.s[cfsHeaders], "#undef near" & tnl)
|
||||||
|
|
||||||
proc initFrame(p: BProc, procname, filename: Rope): Rope =
|
proc initFrame(p: BProc, procname, filename: Rope): Rope =
|
||||||
discard cgsym(p.module, "nimFrame")
|
discard cgsym(p.module, "nimFrame")
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,7 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
||||||
var base = lastSon(methods[0].ast).sym
|
var base = lastSon(methods[0].ast).sym
|
||||||
result = base
|
result = base
|
||||||
var paramLen = sonsLen(base.typ)
|
var paramLen = sonsLen(base.typ)
|
||||||
|
var nilchecks = newNodeI(nkStmtList, base.info)
|
||||||
var disp = newNodeI(nkIfStmt, base.info)
|
var disp = newNodeI(nkIfStmt, base.info)
|
||||||
var ands = getSysSym("and")
|
var ands = getSysSym("and")
|
||||||
var iss = getSysSym("of")
|
var iss = getSysSym("of")
|
||||||
|
|
@ -239,7 +240,11 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
||||||
if contains(relevantCols, col):
|
if contains(relevantCols, col):
|
||||||
var isn = newNodeIT(nkCall, base.info, getSysType(tyBool))
|
var isn = newNodeIT(nkCall, base.info, getSysType(tyBool))
|
||||||
addSon(isn, newSymNode(iss))
|
addSon(isn, newSymNode(iss))
|
||||||
addSon(isn, newSymNode(base.typ.n.sons[col].sym))
|
let param = base.typ.n.sons[col].sym
|
||||||
|
addSon(isn, newSymNode(param))
|
||||||
|
if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
|
||||||
|
addSon(nilchecks, newTree(nkCall,
|
||||||
|
newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param)))
|
||||||
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
|
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
|
||||||
if cond != nil:
|
if cond != nil:
|
||||||
var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
|
var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
|
||||||
|
|
@ -270,7 +275,8 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
||||||
addSon(disp, a)
|
addSon(disp, a)
|
||||||
else:
|
else:
|
||||||
disp = ret
|
disp = ret
|
||||||
result.ast.sons[bodyPos] = disp
|
nilchecks.add disp
|
||||||
|
result.ast.sons[bodyPos] = nilchecks
|
||||||
|
|
||||||
proc generateMethodDispatchers*(g: ModuleGraph): PNode =
|
proc generateMethodDispatchers*(g: ModuleGraph): PNode =
|
||||||
result = newNode(nkStmtList)
|
result = newNode(nkStmtList)
|
||||||
|
|
|
||||||
|
|
@ -785,14 +785,24 @@ proc writeJsonBuildInstructions*(projectfile: string) =
|
||||||
else:
|
else:
|
||||||
lit "],\L"
|
lit "],\L"
|
||||||
|
|
||||||
proc linkfiles(f: File; buf, objfiles: var string; toLink: seq[string]) =
|
proc linkfiles(f: File; buf, objfiles: var string) =
|
||||||
for i, it in toLink:
|
for i, it in externalToLink:
|
||||||
let objfile = addFileExt(it, CC[cCompiler].objExt)
|
let
|
||||||
str(objfile)
|
objFile = if noAbsolutePaths(): it.extractFilename else: it
|
||||||
|
objStr = addFileExt(objFile, CC[cCompiler].objExt)
|
||||||
add(objfiles, ' ')
|
add(objfiles, ' ')
|
||||||
add(objfiles, quoteShell(objfile))
|
add(objfiles, objStr)
|
||||||
|
str objStr
|
||||||
if i == toLink.high:
|
if toCompile.len == 0 and i == externalToLink.high:
|
||||||
|
lit "\L"
|
||||||
|
else:
|
||||||
|
lit ",\L"
|
||||||
|
for i, x in toCompile:
|
||||||
|
let objStr = quoteShell(x.obj)
|
||||||
|
add(objfiles, ' ')
|
||||||
|
add(objfiles, objStr)
|
||||||
|
str objStr
|
||||||
|
if i == toCompile.high:
|
||||||
lit "\L"
|
lit "\L"
|
||||||
else:
|
else:
|
||||||
lit ",\L"
|
lit ",\L"
|
||||||
|
|
@ -809,7 +819,7 @@ proc writeJsonBuildInstructions*(projectfile: string) =
|
||||||
lit "],\L\"link\":[\L"
|
lit "],\L\"link\":[\L"
|
||||||
var objfiles = ""
|
var objfiles = ""
|
||||||
# XXX add every file here that is to link
|
# XXX add every file here that is to link
|
||||||
linkfiles(f, buf, objfiles, externalToLink)
|
linkfiles(f, buf, objfiles)
|
||||||
|
|
||||||
lit "],\L\"linkcmd\": "
|
lit "],\L\"linkcmd\": "
|
||||||
str getLinkCmd(projectfile, objfiles)
|
str getLinkCmd(projectfile, objfiles)
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,28 @@ import
|
||||||
|
|
||||||
proc ensureNoMissingOrUnusedSymbols(scope: PScope)
|
proc ensureNoMissingOrUnusedSymbols(scope: PScope)
|
||||||
|
|
||||||
proc considerQuotedIdent*(n: PNode): PIdent =
|
proc noidentError(n, origin: PNode) =
|
||||||
|
var m = ""
|
||||||
|
if origin != nil:
|
||||||
|
m.add "in expression '" & origin.renderTree & "': "
|
||||||
|
m.add "identifier expected, but found '" & n.renderTree & "'"
|
||||||
|
localError(n.info, m)
|
||||||
|
|
||||||
|
proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent =
|
||||||
## Retrieve a PIdent from a PNode, taking into account accent nodes.
|
## Retrieve a PIdent from a PNode, taking into account accent nodes.
|
||||||
|
## ``origin`` can be nil. If it is not nil, it is used for a better
|
||||||
|
## error message.
|
||||||
|
template handleError(n, origin: PNode) =
|
||||||
|
noidentError(n, origin)
|
||||||
|
result = getIdent"<Error>"
|
||||||
|
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkIdent: result = n.ident
|
of nkIdent: result = n.ident
|
||||||
of nkSym: result = n.sym.name
|
of nkSym: result = n.sym.name
|
||||||
of nkAccQuoted:
|
of nkAccQuoted:
|
||||||
case n.len
|
case n.len
|
||||||
of 0:
|
of 0: handleError(n, origin)
|
||||||
localError(n.info, errIdentifierExpected, renderTree(n))
|
of 1: result = considerQuotedIdent(n.sons[0], origin)
|
||||||
result = getIdent"<Error>"
|
|
||||||
of 1: result = considerQuotedIdent(n.sons[0])
|
|
||||||
else:
|
else:
|
||||||
var id = ""
|
var id = ""
|
||||||
for i in 0.. <n.len:
|
for i in 0.. <n.len:
|
||||||
|
|
@ -33,14 +44,11 @@ proc considerQuotedIdent*(n: PNode): PIdent =
|
||||||
case x.kind
|
case x.kind
|
||||||
of nkIdent: id.add(x.ident.s)
|
of nkIdent: id.add(x.ident.s)
|
||||||
of nkSym: id.add(x.sym.name.s)
|
of nkSym: id.add(x.sym.name.s)
|
||||||
else:
|
else: handleError(n, origin)
|
||||||
localError(n.info, errIdentifierExpected, renderTree(n))
|
|
||||||
return getIdent"<Error>"
|
|
||||||
result = getIdent(id)
|
result = getIdent(id)
|
||||||
of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name
|
of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name
|
||||||
else:
|
else:
|
||||||
localError(n.info, errIdentifierExpected, renderTree(n))
|
handleError(n, origin)
|
||||||
result = getIdent"<Error>"
|
|
||||||
|
|
||||||
template addSym*(scope: PScope, s: PSym) =
|
template addSym*(scope: PScope, s: PSym) =
|
||||||
strTableAdd(scope.symbols, s)
|
strTableAdd(scope.symbols, s)
|
||||||
|
|
@ -353,7 +361,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||||
if n.sons[1].kind == nkIdent:
|
if n.sons[1].kind == nkIdent:
|
||||||
ident = n.sons[1].ident
|
ident = n.sons[1].ident
|
||||||
elif n.sons[1].kind == nkAccQuoted:
|
elif n.sons[1].kind == nkAccQuoted:
|
||||||
ident = considerQuotedIdent(n.sons[1])
|
ident = considerQuotedIdent(n.sons[1], n)
|
||||||
if ident != nil:
|
if ident != nil:
|
||||||
if o.m == c.module:
|
if o.m == c.module:
|
||||||
# a module may access its private members:
|
# a module may access its private members:
|
||||||
|
|
@ -363,8 +371,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||||
else:
|
else:
|
||||||
result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n)
|
result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n)
|
||||||
else:
|
else:
|
||||||
localError(n.sons[1].info, errIdentifierExpected,
|
noidentError(n.sons[1], n)
|
||||||
renderTree(n.sons[1]))
|
|
||||||
result = errorSym(c, n.sons[1])
|
result = errorSym(c, n.sons[1])
|
||||||
of nkClosedSymChoice, nkOpenSymChoice:
|
of nkClosedSymChoice, nkOpenSymChoice:
|
||||||
o.mode = oimSymChoice
|
o.mode = oimSymChoice
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
|
||||||
if msgs.gErrorCounter == 0 and
|
if msgs.gErrorCounter == 0 and
|
||||||
gCmd notin {cmdInterpret, cmdRun, cmdDump}:
|
gCmd notin {cmdInterpret, cmdRun, cmdDump}:
|
||||||
when declared(system.getMaxMem):
|
when declared(system.getMaxMem):
|
||||||
let usedMem = formatSize(getMaxMem()) & " peekmem"
|
let usedMem = formatSize(getMaxMem()) & " peakmem"
|
||||||
else:
|
else:
|
||||||
let usedMem = formatSize(getTotalMem())
|
let usedMem = formatSize(getTotalMem())
|
||||||
rawMessage(hintSuccessX, [$gLinesCompiled,
|
rawMessage(hintSuccessX, [$gLinesCompiled,
|
||||||
|
|
|
||||||
|
|
@ -355,7 +355,7 @@ proc semOpAux(c: PContext, n: PNode) =
|
||||||
var a = n.sons[i]
|
var a = n.sons[i]
|
||||||
if a.kind == nkExprEqExpr and sonsLen(a) == 2:
|
if a.kind == nkExprEqExpr and sonsLen(a) == 2:
|
||||||
var info = a.sons[0].info
|
var info = a.sons[0].info
|
||||||
a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0]), info)
|
a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0], a), info)
|
||||||
a.sons[1] = semExprWithType(c, a.sons[1], flags)
|
a.sons[1] = semExprWithType(c, a.sons[1], flags)
|
||||||
a.typ = a.sons[1].typ
|
a.typ = a.sons[1].typ
|
||||||
else:
|
else:
|
||||||
|
|
@ -1076,7 +1076,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
|
|
||||||
n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType})
|
n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType})
|
||||||
#restoreOldStyleType(n.sons[0])
|
#restoreOldStyleType(n.sons[0])
|
||||||
var i = considerQuotedIdent(n.sons[1])
|
var i = considerQuotedIdent(n.sons[1], n)
|
||||||
var ty = n.sons[0].typ
|
var ty = n.sons[0].typ
|
||||||
var f: PSym = nil
|
var f: PSym = nil
|
||||||
result = nil
|
result = nil
|
||||||
|
|
@ -1160,7 +1160,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
|
||||||
addSon(result, n.sons[1])
|
addSon(result, n.sons[1])
|
||||||
addSon(result, copyTree(n[0]))
|
addSon(result, copyTree(n[0]))
|
||||||
else:
|
else:
|
||||||
var i = considerQuotedIdent(n.sons[1])
|
var i = considerQuotedIdent(n.sons[1], n)
|
||||||
result = newNodeI(nkDotCall, n.info)
|
result = newNodeI(nkDotCall, n.info)
|
||||||
result.flags.incl nfDotField
|
result.flags.incl nfDotField
|
||||||
addSon(result, newIdentNode(i, n[1].info))
|
addSon(result, newIdentNode(i, n[1].info))
|
||||||
|
|
@ -1280,7 +1280,7 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
c.p.bracketExpr = oldBracketExpr
|
c.p.bracketExpr = oldBracketExpr
|
||||||
|
|
||||||
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
|
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
|
||||||
var id = considerQuotedIdent(a[1])
|
var id = considerQuotedIdent(a[1], a)
|
||||||
var setterId = newIdentNode(getIdent(id.s & '='), n.info)
|
var setterId = newIdentNode(getIdent(id.s & '='), n.info)
|
||||||
# a[0] is already checked for semantics, that does ``builtinFieldAccess``
|
# a[0] is already checked for semantics, that does ``builtinFieldAccess``
|
||||||
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
|
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
|
||||||
|
|
@ -1433,7 +1433,6 @@ proc semReturn(c: PContext, n: PNode): PNode =
|
||||||
|
|
||||||
proc semProcBody(c: PContext, n: PNode): PNode =
|
proc semProcBody(c: PContext, n: PNode): PNode =
|
||||||
openScope(c)
|
openScope(c)
|
||||||
|
|
||||||
result = semExpr(c, n)
|
result = semExpr(c, n)
|
||||||
if c.p.resultSym != nil and not isEmptyType(result.typ):
|
if c.p.resultSym != nil and not isEmptyType(result.typ):
|
||||||
# transform ``expr`` to ``result = expr``, but not if the expr is already
|
# transform ``expr`` to ``result = expr``, but not if the expr is already
|
||||||
|
|
@ -1530,7 +1529,7 @@ proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
|
||||||
checkSonsLen(n, 2)
|
checkSonsLen(n, 2)
|
||||||
var m = lookUpForDefined(c, n.sons[0], onlyCurrentScope)
|
var m = lookUpForDefined(c, n.sons[0], onlyCurrentScope)
|
||||||
if m != nil and m.kind == skModule:
|
if m != nil and m.kind == skModule:
|
||||||
let ident = considerQuotedIdent(n[1])
|
let ident = considerQuotedIdent(n[1], n)
|
||||||
if m == c.module:
|
if m == c.module:
|
||||||
result = strTableGet(c.topLevelScope.symbols, ident)
|
result = strTableGet(c.topLevelScope.symbols, ident)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1549,7 +1548,7 @@ proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode =
|
||||||
checkSonsLen(n, 2)
|
checkSonsLen(n, 2)
|
||||||
# we replace this node by a 'true' or 'false' node:
|
# we replace this node by a 'true' or 'false' node:
|
||||||
result = newIntNode(nkIntLit, 0)
|
result = newIntNode(nkIntLit, 0)
|
||||||
if not onlyCurrentScope and considerQuotedIdent(n[0]).s == "defined":
|
if not onlyCurrentScope and considerQuotedIdent(n[0], n).s == "defined":
|
||||||
if n.sons[1].kind != nkIdent:
|
if n.sons[1].kind != nkIdent:
|
||||||
localError(n.info, "obsolete usage of 'defined', use 'declared' instead")
|
localError(n.info, "obsolete usage of 'defined', use 'declared' instead")
|
||||||
elif condsyms.isDefined(n.sons[1].ident):
|
elif condsyms.isDefined(n.sons[1].ident):
|
||||||
|
|
@ -2084,9 +2083,8 @@ proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) =
|
||||||
|
|
||||||
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
var t = semTypeNode(c, n.sons[0], nil)
|
var t = semTypeNode(c, n.sons[0], nil)
|
||||||
result = n
|
result = newNodeIT(nkObjConstr, n.info, t)
|
||||||
result.typ = t
|
result.add n.sons[0]
|
||||||
result.kind = nkObjConstr
|
|
||||||
t = skipTypes(t, {tyGenericInst, tyAlias})
|
t = skipTypes(t, {tyGenericInst, tyAlias})
|
||||||
if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias})
|
if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias})
|
||||||
if t.kind != tyObject:
|
if t.kind != tyObject:
|
||||||
|
|
@ -2099,7 +2097,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
if it.kind != nkExprColonExpr:
|
if it.kind != nkExprColonExpr:
|
||||||
localError(n.info, errNamedExprExpected)
|
localError(n.info, errNamedExprExpected)
|
||||||
break
|
break
|
||||||
let id = considerQuotedIdent(it.sons[0])
|
let id = considerQuotedIdent(it.sons[0], it)
|
||||||
|
|
||||||
if containsOrIncl(ids, id.id):
|
if containsOrIncl(ids, id.id):
|
||||||
localError(it.info, errFieldInitTwice, id.s)
|
localError(it.info, errFieldInitTwice, id.s)
|
||||||
|
|
@ -2125,6 +2123,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
else:
|
else:
|
||||||
localError(it.info, errUndeclaredFieldX, id.s)
|
localError(it.info, errUndeclaredFieldX, id.s)
|
||||||
it.sons[1] = e
|
it.sons[1] = e
|
||||||
|
result.add it
|
||||||
# XXX object field name check for 'case objects' if the kind is static?
|
# XXX object field name check for 'case objects' if the kind is static?
|
||||||
if tfNeedsInit in objType.flags:
|
if tfNeedsInit in objType.flags:
|
||||||
while true:
|
while true:
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,10 @@ template macroToExpand(s): untyped =
|
||||||
s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfAllUntyped in s.flags)
|
s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfAllUntyped in s.flags)
|
||||||
|
|
||||||
template macroToExpandSym(s): untyped =
|
template macroToExpandSym(s): untyped =
|
||||||
s.kind in {skMacro, skTemplate} and (s.typ.len == 1)
|
s.kind in {skMacro, skTemplate} and (s.typ.len == 1) and not fromDotExpr
|
||||||
|
|
||||||
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||||
ctx: var GenericCtx): PNode =
|
ctx: var GenericCtx; fromDotExpr=false): PNode =
|
||||||
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
|
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
|
||||||
incl(s.flags, sfUsed)
|
incl(s.flags, sfUsed)
|
||||||
case s.kind
|
case s.kind
|
||||||
|
|
@ -145,7 +145,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||||
elif s.name.id in ctx.toMixin:
|
elif s.name.id in ctx.toMixin:
|
||||||
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
||||||
else:
|
else:
|
||||||
let syms = semGenericStmtSymbol(c, n, s, ctx)
|
let syms = semGenericStmtSymbol(c, n, s, ctx, fromDotExpr=true)
|
||||||
if syms.kind == nkSym:
|
if syms.kind == nkSym:
|
||||||
let choice = symChoice(c, n, s, scForceOpen)
|
let choice = symChoice(c, n, s, scForceOpen)
|
||||||
choice.kind = nkClosedSymChoice
|
choice.kind = nkClosedSymChoice
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,11 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
|
||||||
|
|
||||||
proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
|
proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
|
||||||
if n.sons[bodyPos].kind != nkEmpty:
|
if n.sons[bodyPos].kind != nkEmpty:
|
||||||
|
let procParams = result.typ.n
|
||||||
|
for i in 1 .. <procParams.len:
|
||||||
|
addDecl(c, procParams[i].sym)
|
||||||
|
maybeAddResult(c, result, result.ast)
|
||||||
|
|
||||||
inc c.inGenericInst
|
inc c.inGenericInst
|
||||||
# add it here, so that recursive generic procs are possible:
|
# add it here, so that recursive generic procs are possible:
|
||||||
var b = n.sons[bodyPos]
|
var b = n.sons[bodyPos]
|
||||||
|
|
@ -147,13 +152,17 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
|
||||||
for i in countup(0, c.generics.len - 1):
|
for i in countup(0, c.generics.len - 1):
|
||||||
if c.generics[i].genericSym.id == s.id:
|
if c.generics[i].genericSym.id == s.id:
|
||||||
var oldPrc = c.generics[i].inst.sym
|
var oldPrc = c.generics[i].inst.sym
|
||||||
|
pushProcCon(c, oldPrc)
|
||||||
|
pushOwner(c, oldPrc)
|
||||||
pushInfoContext(oldPrc.info)
|
pushInfoContext(oldPrc.info)
|
||||||
openScope(c)
|
openScope(c)
|
||||||
var n = oldPrc.ast
|
var n = oldPrc.ast
|
||||||
n.sons[bodyPos] = copyTree(s.getBody)
|
n.sons[bodyPos] = copyTree(s.getBody)
|
||||||
instantiateBody(c, n, nil, oldPrc, s)
|
instantiateBody(c, n, oldPrc.typ.n, oldPrc, s)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
popInfoContext()
|
popInfoContext()
|
||||||
|
popOwner(c)
|
||||||
|
popProcCon(c)
|
||||||
|
|
||||||
proc sideEffectsCheck(c: PContext, s: PSym) =
|
proc sideEffectsCheck(c: PContext, s: PSym) =
|
||||||
when false:
|
when false:
|
||||||
|
|
@ -229,7 +238,6 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||||
skipIntLiteralParams(result)
|
skipIntLiteralParams(result)
|
||||||
|
|
||||||
prc.typ = result
|
prc.typ = result
|
||||||
maybeAddResult(c, prc, prc.ast)
|
|
||||||
popInfoContext()
|
popInfoContext()
|
||||||
|
|
||||||
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
|
|
|
||||||
|
|
@ -160,10 +160,11 @@ proc discardCheck(c: PContext, result: PNode) =
|
||||||
else:
|
else:
|
||||||
var n = result
|
var n = result
|
||||||
while n.kind in skipForDiscardable: n = n.lastSon
|
while n.kind in skipForDiscardable: n = n.lastSon
|
||||||
|
var s = "expression '" & $n & "' is of type '" &
|
||||||
|
result.typ.typeToString & "' and has to be discarded"
|
||||||
if result.typ.kind == tyProc:
|
if result.typ.kind == tyProc:
|
||||||
localError(n.info, "value of type '" & result.typ.typeToString & "' has to be discarded; for a function call use ()")
|
s.add "; for a function call use ()"
|
||||||
else:
|
localError(n.info, s)
|
||||||
localError(n.info, errDiscardValueX, result.typ.typeToString)
|
|
||||||
|
|
||||||
proc semIf(c: PContext, n: PNode): PNode =
|
proc semIf(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
|
|
|
||||||
|
|
@ -350,6 +350,14 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
|
||||||
else: result = isIntConv
|
else: result = isIntConv
|
||||||
else: result = isNone
|
else: result = isNone
|
||||||
|
|
||||||
|
proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
|
||||||
|
if fGenericOrigin != nil and last.kind == tyGenericInst and
|
||||||
|
last.len-1 == fGenericOrigin.len:
|
||||||
|
for i in countup(1, sonsLen(fGenericOrigin) - 1):
|
||||||
|
let x = PType(idTableGet(c.bindings, fGenericOrigin.sons[i]))
|
||||||
|
if x == nil:
|
||||||
|
put(c, fGenericOrigin.sons[i], last.sons[i])
|
||||||
|
|
||||||
proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
|
proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
|
||||||
var t = a
|
var t = a
|
||||||
assert t.kind == tyObject
|
assert t.kind == tyObject
|
||||||
|
|
@ -363,12 +371,7 @@ proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
|
||||||
t = skipTypes(t, skipPtrs)
|
t = skipTypes(t, skipPtrs)
|
||||||
inc depth
|
inc depth
|
||||||
if t != nil:
|
if t != nil:
|
||||||
if fGenericOrigin != nil and last.kind == tyGenericInst and
|
genericParamPut(c, last, fGenericOrigin)
|
||||||
last.len-1 == fGenericOrigin.len:
|
|
||||||
for i in countup(1, sonsLen(fGenericOrigin) - 1):
|
|
||||||
let x = PType(idTableGet(c.bindings, fGenericOrigin.sons[i]))
|
|
||||||
if x == nil:
|
|
||||||
put(c, fGenericOrigin.sons[i], last.sons[i])
|
|
||||||
result = depth
|
result = depth
|
||||||
else:
|
else:
|
||||||
result = -1
|
result = -1
|
||||||
|
|
@ -398,7 +401,7 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType =
|
||||||
break
|
break
|
||||||
if r.kind == tyObject and ptrs <= 1: result = r
|
if r.kind == tyObject and ptrs <= 1: result = r
|
||||||
|
|
||||||
proc isGenericSubtype(a, f: PType, d: var int): bool =
|
proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin: PType = nil): bool =
|
||||||
assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody}
|
assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody}
|
||||||
var askip = skippedNone
|
var askip = skippedNone
|
||||||
var fskip = skippedNone
|
var fskip = skippedNone
|
||||||
|
|
@ -406,14 +409,17 @@ proc isGenericSubtype(a, f: PType, d: var int): bool =
|
||||||
let r = f.skipToObject(fskip)
|
let r = f.skipToObject(fskip)
|
||||||
if r == nil: return false
|
if r == nil: return false
|
||||||
var depth = 0
|
var depth = 0
|
||||||
|
var last = a
|
||||||
# XXX sameObjectType can return false here. Need to investigate
|
# XXX sameObjectType can return false here. Need to investigate
|
||||||
# why that is but sameObjectType does way too much work here anyway.
|
# why that is but sameObjectType does way too much work here anyway.
|
||||||
while t != nil and r.sym != t.sym and askip == fskip:
|
while t != nil and r.sym != t.sym and askip == fskip:
|
||||||
t = t.sons[0]
|
t = t.sons[0]
|
||||||
if t != nil: t = t.skipToObject(askip)
|
if t == nil: break
|
||||||
else: break
|
last = t
|
||||||
|
t = t.skipToObject(askip)
|
||||||
inc depth
|
inc depth
|
||||||
if t != nil and askip == fskip:
|
if t != nil and askip == fskip:
|
||||||
|
genericParamPut(c, last, fGenericOrigin)
|
||||||
d = depth
|
d = depth
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
|
|
@ -999,7 +1005,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
# simply no match for now:
|
# simply no match for now:
|
||||||
discard
|
discard
|
||||||
elif x.kind == tyGenericInst and
|
elif x.kind == tyGenericInst and
|
||||||
((f.sons[0] == x.sons[0]) or isGenericSubtype(x, f, depth)) and
|
((f.sons[0] == x.sons[0]) or isGenericSubType(c, x, f, depth)) and
|
||||||
(sonsLen(x) - 1 == sonsLen(f)):
|
(sonsLen(x) - 1 == sonsLen(f)):
|
||||||
for i in countup(1, sonsLen(f) - 1):
|
for i in countup(1, sonsLen(f) - 1):
|
||||||
if x.sons[i].kind == tyGenericParam:
|
if x.sons[i].kind == tyGenericParam:
|
||||||
|
|
@ -1039,6 +1045,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
else:
|
else:
|
||||||
put(c, f.sons[i], x)
|
put(c, f.sons[i], x)
|
||||||
|
|
||||||
|
if result == isNone:
|
||||||
|
# Here object inheriting from generic/specialized generic object
|
||||||
|
# crossing path with metatypes/aliases, so we need to separate them
|
||||||
|
# by checking sym.id
|
||||||
|
let genericSubtype = isGenericSubType(c, x, f, depth, f)
|
||||||
|
if not (genericSubtype and aobj.sym.id != fobj.sym.id):
|
||||||
|
depth = -1
|
||||||
|
|
||||||
if depth >= 0:
|
if depth >= 0:
|
||||||
c.inheritancePenalty += depth
|
c.inheritancePenalty += depth
|
||||||
# bug #4863: We still need to bind generic alias crap, so
|
# bug #4863: We still need to bind generic alias crap, so
|
||||||
|
|
|
||||||
21
doc/tut1.rst
21
doc/tut1.rst
|
|
@ -1394,6 +1394,27 @@ slice's bounds can hold any value supported by
|
||||||
their type, but it is the proc using the slice object which defines what values
|
their type, but it is the proc using the slice object which defines what values
|
||||||
are accepted.
|
are accepted.
|
||||||
|
|
||||||
|
To understand some of the different ways of specifying the indices of strings, arrays, sequences, etc.,
|
||||||
|
it must be remembered that Nim uses zero-based indices.
|
||||||
|
|
||||||
|
So the string ``b`` is of length 19, and two different ways of specifying the indices are
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
|
||||||
|
"Slices are useless."
|
||||||
|
| | |
|
||||||
|
0 11 17 using indices
|
||||||
|
^19 ^8 ^2 using ^ syntax
|
||||||
|
|
||||||
|
where ``b[0..^1]`` is equivalent to ``b[0..b.len-1]`` and ``b[0..<b.len]``, and it can be seen that the ``^1`` provides a short-hand way of specifying the ``b.len-1``
|
||||||
|
|
||||||
|
In the above example, because the string ends in a period, to get the portion of the string that is "useless" and replace it with "useful"
|
||||||
|
|
||||||
|
``b[11..^2]`` is the portion "useless", and
|
||||||
|
``b[11..^2] = "useful"`` replaces the "useless" portion with "useful",
|
||||||
|
giving the result "Slices are useful."
|
||||||
|
|
||||||
|
Note: alternate ways of writing this are ``b[^8..^2] = "useful"`` or as ``b[11..b.len-2] = "useful"`` or as ``b[11..<b.len-1] = "useful"`` or as ....
|
||||||
|
|
||||||
Tuples
|
Tuples
|
||||||
------
|
------
|
||||||
|
|
|
||||||
38
koch.nim
38
koch.nim
|
|
@ -41,30 +41,26 @@ Options:
|
||||||
Possible Commands:
|
Possible Commands:
|
||||||
boot [options] bootstraps with given command line options
|
boot [options] bootstraps with given command line options
|
||||||
distrohelper [bindir] helper for distro packagers
|
distrohelper [bindir] helper for distro packagers
|
||||||
geninstall generate ./install.sh; Unix only!
|
|
||||||
testinstall test tar.xz package; Unix only! Only for devs!
|
|
||||||
clean cleans Nim project; removes generated files
|
|
||||||
web [options] generates the website and the full documentation
|
|
||||||
website [options] generates only the website
|
|
||||||
csource [options] builds the C sources for installation
|
|
||||||
pdf builds the PDF documentation
|
|
||||||
zip builds the installation ZIP package
|
|
||||||
xz builds the installation XZ package
|
|
||||||
nsis [options] builds the NSIS Setup installer (for Windows)
|
|
||||||
tests [options] run the testsuite
|
|
||||||
temp options creates a temporary compiler for testing
|
|
||||||
winrelease creates a release (for coredevs only)
|
|
||||||
nimble builds the Nimble tool
|
|
||||||
tools builds Nim related tools
|
tools builds Nim related tools
|
||||||
pushcsource push generated C sources to its repo! Only for devs!
|
nimble builds the Nimble tool
|
||||||
Boot options:
|
Boot options:
|
||||||
-d:release produce a release version of the compiler
|
-d:release produce a release version of the compiler
|
||||||
-d:tinyc include the Tiny C backend (not supported on Windows)
|
|
||||||
-d:useLinenoise use the linenoise library for interactive mode
|
-d:useLinenoise use the linenoise library for interactive mode
|
||||||
(not needed on Windows)
|
(not needed on Windows)
|
||||||
-d:nativeStacktrace use native stack traces (only for Mac OS X or Linux)
|
|
||||||
-d:noCaas build Nim without CAAS support
|
|
||||||
-d:avoidTimeMachine only for Mac OS X, excludes nimcache dir from backups
|
-d:avoidTimeMachine only for Mac OS X, excludes nimcache dir from backups
|
||||||
|
|
||||||
|
Commands for core developers:
|
||||||
|
web [options] generates the website and the full documentation
|
||||||
|
website [options] generates only the website
|
||||||
|
csource -d:release builds the C sources for installation
|
||||||
|
pdf builds the PDF documentation
|
||||||
|
zip builds the installation zip package
|
||||||
|
xz builds the installation tar.xz package
|
||||||
|
testinstall test tar.xz package; Unix only!
|
||||||
|
tests [options] run the testsuite
|
||||||
|
temp options creates a temporary compiler for testing
|
||||||
|
winrelease creates a Windows release
|
||||||
|
pushcsource push generated C sources to its repo
|
||||||
Web options:
|
Web options:
|
||||||
--googleAnalytics:UA-... add the given google analytics code to the docs. To
|
--googleAnalytics:UA-... add the given google analytics code to the docs. To
|
||||||
build the official docs, use UA-48159761-1
|
build the official docs, use UA-48159761-1
|
||||||
|
|
@ -218,11 +214,14 @@ proc bundleNimsuggest(buildExe: bool) =
|
||||||
copyExe("nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe)
|
copyExe("nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe)
|
||||||
removeFile("nimsuggest/nimsuggest".exe)
|
removeFile("nimsuggest/nimsuggest".exe)
|
||||||
|
|
||||||
|
proc buildVccTool() =
|
||||||
|
nimexec("c -o:bin/vccexe.exe tools/vccenv/vccexe")
|
||||||
|
|
||||||
proc bundleWinTools() =
|
proc bundleWinTools() =
|
||||||
nimexec("c tools/finish.nim")
|
nimexec("c tools/finish.nim")
|
||||||
copyExe("tools/finish".exe, "finish".exe)
|
copyExe("tools/finish".exe, "finish".exe)
|
||||||
removeFile("tools/finish".exe)
|
removeFile("tools/finish".exe)
|
||||||
nimexec("c -o:bin/vccexe.exe tools/vccenv/vccexe")
|
buildVccTool()
|
||||||
nimexec("c -o:bin/nimgrab.exe -d:ssl tools/nimgrab.nim")
|
nimexec("c -o:bin/nimgrab.exe -d:ssl tools/nimgrab.nim")
|
||||||
when false:
|
when false:
|
||||||
# not yet a tool worth including
|
# not yet a tool worth including
|
||||||
|
|
@ -257,6 +256,7 @@ proc buildTools(latest: bool) =
|
||||||
|
|
||||||
let nimgrepExe = "bin/nimgrep".exe
|
let nimgrepExe = "bin/nimgrep".exe
|
||||||
nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim"
|
nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim"
|
||||||
|
when defined(windows): buildVccTool()
|
||||||
buildNimble(latest)
|
buildNimble(latest)
|
||||||
|
|
||||||
proc nsis(args: string) =
|
proc nsis(args: string) =
|
||||||
|
|
|
||||||
|
|
@ -173,17 +173,33 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
|
||||||
rawExec(db, query, args)
|
rawExec(db, query, args)
|
||||||
var sqlres = mysql.useResult(db)
|
var sqlres = mysql.useResult(db)
|
||||||
if sqlres != nil:
|
if sqlres != nil:
|
||||||
var L = int(mysql.numFields(sqlres))
|
var
|
||||||
var result = newRow(L)
|
L = int(mysql.numFields(sqlres))
|
||||||
var row: cstringArray
|
row: cstringArray
|
||||||
|
result: Row
|
||||||
|
backup: Row
|
||||||
|
newSeq(result, L)
|
||||||
while true:
|
while true:
|
||||||
row = mysql.fetchRow(sqlres)
|
row = mysql.fetchRow(sqlres)
|
||||||
if row == nil: break
|
if row == nil: break
|
||||||
for i in 0..L-1:
|
for i in 0..L-1:
|
||||||
setLen(result[i], 0)
|
|
||||||
if row[i] == nil:
|
if row[i] == nil:
|
||||||
|
if backup == nil:
|
||||||
|
newSeq(backup, L)
|
||||||
|
if backup[i] == nil and result[i] != nil:
|
||||||
|
shallowCopy(backup[i], result[i])
|
||||||
result[i] = nil
|
result[i] = nil
|
||||||
else:
|
else:
|
||||||
|
if result[i] == nil:
|
||||||
|
if backup != nil:
|
||||||
|
if backup[i] == nil:
|
||||||
|
backup[i] = ""
|
||||||
|
shallowCopy(result[i], backup[i])
|
||||||
|
setLen(result[i], 0)
|
||||||
|
else:
|
||||||
|
result[i] = ""
|
||||||
|
else:
|
||||||
|
setLen(result[i], 0)
|
||||||
add(result[i], row[i])
|
add(result[i], row[i])
|
||||||
yield result
|
yield result
|
||||||
properFreeResult(sqlres, row)
|
properFreeResult(sqlres, row)
|
||||||
|
|
|
||||||
|
|
@ -406,6 +406,8 @@ proc setInterval*(w: Window, function: proc (), pause: int): ref TInterval
|
||||||
proc setTimeout*(w: Window, code: cstring, pause: int): ref TTimeOut
|
proc setTimeout*(w: Window, code: cstring, pause: int): ref TTimeOut
|
||||||
proc setTimeout*(w: Window, function: proc (), pause: int): ref TInterval
|
proc setTimeout*(w: Window, function: proc (), pause: int): ref TInterval
|
||||||
proc stop*(w: Window)
|
proc stop*(w: Window)
|
||||||
|
proc requestAnimationFrame*(w: Window, function: proc (time: float)): int
|
||||||
|
proc cancelAnimationFrame*(w: Window, id: int)
|
||||||
|
|
||||||
# Node "methods"
|
# Node "methods"
|
||||||
proc appendChild*(n, child: Node)
|
proc appendChild*(n, child: Node)
|
||||||
|
|
|
||||||
|
|
@ -489,3 +489,13 @@ proc writeFromStream*(f: AsyncFile, fs: FutureStream[string]) {.async.} =
|
||||||
await f.write(value)
|
await f.write(value)
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
proc readToStream*(f: AsyncFile, fs: FutureStream[string]) {.async.} =
|
||||||
|
## Writes data to the specified future stream as the file is read.
|
||||||
|
while true:
|
||||||
|
let data = await read(f, 4000)
|
||||||
|
if data.len == 0:
|
||||||
|
break
|
||||||
|
await fs.write(data)
|
||||||
|
|
||||||
|
fs.complete()
|
||||||
|
|
@ -80,7 +80,7 @@ proc `[]`*[T](deq: Deque[T], i: Natural) : T {.inline.} =
|
||||||
## Access the i-th element of `deq` by order from first to last.
|
## Access the i-th element of `deq` by order from first to last.
|
||||||
## deq[0] is the first, deq[^1] is the last.
|
## deq[0] is the first, deq[^1] is the last.
|
||||||
xBoundsCheck(deq, i)
|
xBoundsCheck(deq, i)
|
||||||
return deq.data[(deq.first + i) and deq.mask]
|
return deq.data[(deq.head + i) and deq.mask]
|
||||||
|
|
||||||
proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} =
|
proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} =
|
||||||
## Access the i-th element of `deq` and returns a mutable
|
## Access the i-th element of `deq` and returns a mutable
|
||||||
|
|
|
||||||
|
|
@ -617,20 +617,6 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
|
||||||
flushFile(d)
|
flushFile(d)
|
||||||
close(d)
|
close(d)
|
||||||
|
|
||||||
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
|
|
||||||
tags: [ReadIOEffect, WriteIOEffect].} =
|
|
||||||
## Moves a file from `source` to `dest`. If this fails, `OSError` is raised.
|
|
||||||
when defined(Windows):
|
|
||||||
when useWinUnicode:
|
|
||||||
let s = newWideCString(source)
|
|
||||||
let d = newWideCString(dest)
|
|
||||||
if moveFileW(s, d) == 0'i32: raiseOSError(osLastError())
|
|
||||||
else:
|
|
||||||
if moveFileA(source, dest) == 0'i32: raiseOSError(osLastError())
|
|
||||||
else:
|
|
||||||
if c_rename(source, dest) != 0'i32:
|
|
||||||
raiseOSError(osLastError(), $strerror(errno))
|
|
||||||
|
|
||||||
when not declared(ENOENT) and not defined(Windows):
|
when not declared(ENOENT) and not defined(Windows):
|
||||||
when NoFakeVars:
|
when NoFakeVars:
|
||||||
const ENOENT = cint(2) # 2 on most systems including Solaris
|
const ENOENT = cint(2) # 2 on most systems including Solaris
|
||||||
|
|
@ -647,25 +633,63 @@ when defined(Windows):
|
||||||
template setFileAttributes(file, attrs: untyped): untyped =
|
template setFileAttributes(file, attrs: untyped): untyped =
|
||||||
setFileAttributesA(file, attrs)
|
setFileAttributesA(file, attrs)
|
||||||
|
|
||||||
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
||||||
## Removes the `file`. If this fails, `OSError` is raised. This does not fail
|
## Removes the `file`. If this fails, returns `false`. This does not fail
|
||||||
## if the file never existed in the first place.
|
## if the file never existed in the first place.
|
||||||
## On Windows, ignores the read-only attribute.
|
## On Windows, ignores the read-only attribute.
|
||||||
|
result = true
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
let f = newWideCString(file)
|
let f = newWideCString(file)
|
||||||
else:
|
else:
|
||||||
let f = file
|
let f = file
|
||||||
if deleteFile(f) == 0:
|
if deleteFile(f) == 0:
|
||||||
if getLastError() == ERROR_ACCESS_DENIED:
|
result = false
|
||||||
if setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) == 0:
|
let err = getLastError()
|
||||||
raiseOSError(osLastError())
|
if err == ERROR_FILE_NOT_FOUND or err == ERROR_PATH_NOT_FOUND:
|
||||||
if deleteFile(f) == 0:
|
result = true
|
||||||
raiseOSError(osLastError())
|
elif err == ERROR_ACCESS_DENIED and
|
||||||
|
setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) != 0 and
|
||||||
|
deleteFile(f) != 0:
|
||||||
|
result = true
|
||||||
else:
|
else:
|
||||||
if c_remove(file) != 0'i32 and errno != ENOENT:
|
if c_remove(file) != 0'i32 and errno != ENOENT:
|
||||||
|
result = false
|
||||||
|
|
||||||
|
proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} =
|
||||||
|
## Removes the `file`. If this fails, `OSError` is raised. This does not fail
|
||||||
|
## if the file never existed in the first place.
|
||||||
|
## On Windows, ignores the read-only attribute.
|
||||||
|
if not tryRemoveFile(file):
|
||||||
|
when defined(Windows):
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
else:
|
||||||
raiseOSError(osLastError(), $strerror(errno))
|
raiseOSError(osLastError(), $strerror(errno))
|
||||||
|
|
||||||
|
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
|
||||||
|
tags: [ReadIOEffect, WriteIOEffect].} =
|
||||||
|
## Moves a file from `source` to `dest`. If this fails, `OSError` is raised.
|
||||||
|
when defined(Windows):
|
||||||
|
when useWinUnicode:
|
||||||
|
let s = newWideCString(source)
|
||||||
|
let d = newWideCString(dest)
|
||||||
|
if moveFileW(s, d) == 0'i32: raiseOSError(osLastError())
|
||||||
|
else:
|
||||||
|
if moveFileA(source, dest) == 0'i32: raiseOSError(osLastError())
|
||||||
|
else:
|
||||||
|
if c_rename(source, dest) != 0'i32:
|
||||||
|
let err = osLastError()
|
||||||
|
if err == EXDEV.OSErrorCode:
|
||||||
|
# Fallback to copy & del
|
||||||
|
copyFile(source, dest)
|
||||||
|
try:
|
||||||
|
removeFile(source)
|
||||||
|
except:
|
||||||
|
discard tryRemoveFile(dest)
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
raiseOSError(err, $strerror(errno))
|
||||||
|
|
||||||
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
|
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
|
||||||
tags: [ExecIOEffect].} =
|
tags: [ExecIOEffect].} =
|
||||||
## Executes a `shell command`:idx:.
|
## Executes a `shell command`:idx:.
|
||||||
|
|
@ -1616,7 +1640,8 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} =
|
||||||
|
|
||||||
proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1",
|
proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1",
|
||||||
tags: [ReadIOEffect].} =
|
tags: [ReadIOEffect].} =
|
||||||
## returns the file size of `file`. Can raise ``OSError``.
|
## returns the file size of `file` (in bytes). An ``OSError`` exception is
|
||||||
|
## raised in case of an error.
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
var a: WIN32_FIND_DATA
|
var a: WIN32_FIND_DATA
|
||||||
var resA = findFirstFile(file, a)
|
var resA = findFirstFile(file, a)
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,6 @@
|
||||||
##
|
##
|
||||||
## Tested on these OSes: Linux, Windows, OSX
|
## Tested on these OSes: Linux, Windows, OSX
|
||||||
|
|
||||||
type
|
|
||||||
NilAccessError* = object of SystemError ## \
|
|
||||||
## Raised on dereferences of ``nil`` pointers.
|
|
||||||
|
|
||||||
# do allocate memory upfront:
|
# do allocate memory upfront:
|
||||||
var se: ref NilAccessError
|
var se: ref NilAccessError
|
||||||
new(se)
|
new(se)
|
||||||
|
|
|
||||||
|
|
@ -597,9 +597,12 @@ elif defined(JS):
|
||||||
result.month = Month(t.getMonth())
|
result.month = Month(t.getMonth())
|
||||||
result.year = t.getFullYear()
|
result.year = t.getFullYear()
|
||||||
result.weekday = weekDays[t.getDay()]
|
result.weekday = weekDays[t.getDay()]
|
||||||
result.yearday = 0
|
|
||||||
result.timezone = getTimezone()
|
result.timezone = getTimezone()
|
||||||
|
|
||||||
|
result.yearday = result.monthday - 1
|
||||||
|
for month in mJan..<result.month:
|
||||||
|
result.yearday += getDaysInMonth(month, result.year)
|
||||||
|
|
||||||
proc getGMTime(t: Time): TimeInfo =
|
proc getGMTime(t: Time): TimeInfo =
|
||||||
result.second = t.getUTCSeconds()
|
result.second = t.getUTCSeconds()
|
||||||
result.minute = t.getUTCMinutes()
|
result.minute = t.getUTCMinutes()
|
||||||
|
|
@ -608,7 +611,10 @@ elif defined(JS):
|
||||||
result.month = Month(t.getUTCMonth())
|
result.month = Month(t.getUTCMonth())
|
||||||
result.year = t.getUTCFullYear()
|
result.year = t.getUTCFullYear()
|
||||||
result.weekday = weekDays[t.getUTCDay()]
|
result.weekday = weekDays[t.getUTCDay()]
|
||||||
result.yearday = 0
|
|
||||||
|
result.yearday = result.monthday - 1
|
||||||
|
for month in mJan..<result.month:
|
||||||
|
result.yearday += getDaysInMonth(month, result.year)
|
||||||
|
|
||||||
proc timeInfoToTime(timeInfo: TimeInfo): Time = toTime(timeInfo)
|
proc timeInfoToTime(timeInfo: TimeInfo): Time = toTime(timeInfo)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -375,7 +375,6 @@ template test*(name, body) {.dirty.} =
|
||||||
ensureFormattersInitialized()
|
ensureFormattersInitialized()
|
||||||
|
|
||||||
if shouldRun(name):
|
if shouldRun(name):
|
||||||
var stackTrace {.inject.}: string
|
|
||||||
checkpoints = @[]
|
checkpoints = @[]
|
||||||
var testStatusIMPL {.inject.} = OK
|
var testStatusIMPL {.inject.} = OK
|
||||||
|
|
||||||
|
|
@ -391,7 +390,7 @@ template test*(name, body) {.dirty.} =
|
||||||
except:
|
except:
|
||||||
when not defined(js):
|
when not defined(js):
|
||||||
checkpoint("Unhandled exception: " & getCurrentExceptionMsg())
|
checkpoint("Unhandled exception: " & getCurrentExceptionMsg())
|
||||||
stackTrace = getCurrentException().getStackTrace()
|
var stackTrace {.inject.} = getCurrentException().getStackTrace()
|
||||||
fail()
|
fail()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
|
|
||||||
|
|
@ -154,9 +154,13 @@ proc `addr`*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
|
proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
|
||||||
## Builtin 'addr' operator for taking the address of a memory location.
|
## Builtin 'addr' operator for taking the address of a memory
|
||||||
## This works even for ``let`` variables or parameters for better interop
|
## location. This works even for ``let`` variables or parameters
|
||||||
## with C and so it is considered even more unsafe than the ordinary ``addr``.
|
## for better interop with C and so it is considered even more
|
||||||
|
## unsafe than the ordinary ``addr``. When you use it to write a
|
||||||
|
## wrapper for a C library, you should always check that the
|
||||||
|
## original library does never write to data behind the pointer that
|
||||||
|
## is returned from this procedure.
|
||||||
## Cannot be overloaded.
|
## Cannot be overloaded.
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
@ -554,6 +558,10 @@ type
|
||||||
## Raised if it is attempted to send a message to a dead thread.
|
## Raised if it is attempted to send a message to a dead thread.
|
||||||
##
|
##
|
||||||
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
|
||||||
|
NilAccessError* = object of SystemError ## \
|
||||||
|
## Raised on dereferences of ``nil`` pointers.
|
||||||
|
##
|
||||||
|
## This is only raised if the ``segfaults.nim`` module was imported!
|
||||||
|
|
||||||
{.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect,
|
{.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect,
|
||||||
FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect,
|
FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect,
|
||||||
|
|
|
||||||
|
|
@ -763,6 +763,8 @@ proc getOccupiedMem(a: MemRegion): int {.inline.} =
|
||||||
# ---------------------- thread memory region -------------------------------
|
# ---------------------- thread memory region -------------------------------
|
||||||
|
|
||||||
template instantiateForRegion(allocator: untyped) =
|
template instantiateForRegion(allocator: untyped) =
|
||||||
|
{.push stackTrace: off.}
|
||||||
|
|
||||||
when defined(fulldebug):
|
when defined(fulldebug):
|
||||||
proc interiorAllocatedPtr*(p: pointer): pointer =
|
proc interiorAllocatedPtr*(p: pointer): pointer =
|
||||||
result = interiorAllocatedPtr(allocator, p)
|
result = interiorAllocatedPtr(allocator, p)
|
||||||
|
|
@ -850,5 +852,6 @@ template instantiateForRegion(allocator: untyped) =
|
||||||
|
|
||||||
proc getOccupiedSharedMem(): int =
|
proc getOccupiedSharedMem(): int =
|
||||||
sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem)
|
sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem)
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,11 @@ proc chckRangeF(x, a, b: float): float =
|
||||||
|
|
||||||
proc chckNil(p: pointer) =
|
proc chckNil(p: pointer) =
|
||||||
if p == nil:
|
if p == nil:
|
||||||
sysFatal(ValueError, "attempt to write to a nil address")
|
sysFatal(NilAccessError, "attempt to write to a nil address")
|
||||||
|
|
||||||
|
proc chckNilDisp(p: pointer) {.compilerproc.} =
|
||||||
|
if p == nil:
|
||||||
|
sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")
|
||||||
|
|
||||||
proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
|
proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
|
||||||
# checks if obj is of type subclass:
|
# checks if obj is of type subclass:
|
||||||
|
|
|
||||||
|
|
@ -642,6 +642,10 @@ proc toU32*(a: int64): int32 {.asmNoStackFrame, compilerproc.} =
|
||||||
proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
|
proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
|
||||||
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
|
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
|
||||||
|
|
||||||
|
proc chckNilDisp(p: pointer) {.compilerproc.} =
|
||||||
|
if p == nil:
|
||||||
|
sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil")
|
||||||
|
|
||||||
type NimString = string # hack for hti.nim
|
type NimString = string # hack for hti.nim
|
||||||
include "system/hti"
|
include "system/hti"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,6 @@ type
|
||||||
|
|
||||||
PGcThread = ptr GcThread
|
PGcThread = ptr GcThread
|
||||||
GcThread {.pure, inheritable.} = object
|
GcThread {.pure, inheritable.} = object
|
||||||
sys: SysThread
|
|
||||||
when emulatedThreadVars and not useStackMaskHack:
|
when emulatedThreadVars and not useStackMaskHack:
|
||||||
tls: ThreadLocalStorage
|
tls: ThreadLocalStorage
|
||||||
else:
|
else:
|
||||||
|
|
@ -345,18 +344,16 @@ when not defined(useNimRtl):
|
||||||
# use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway.
|
# use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway.
|
||||||
|
|
||||||
type
|
type
|
||||||
Thread* {.pure, final.}[TArg] =
|
Thread* {.pure, final.}[TArg] = object
|
||||||
object of GcThread ## Nim thread. A thread is a heavy object (~14K)
|
core: PGcThread
|
||||||
## that **must not** be part of a message! Use
|
sys: SysThread
|
||||||
## a ``ThreadId`` for that.
|
|
||||||
when TArg is void:
|
when TArg is void:
|
||||||
dataFn: proc () {.nimcall, gcsafe.}
|
dataFn: proc () {.nimcall, gcsafe.}
|
||||||
else:
|
else:
|
||||||
dataFn: proc (m: TArg) {.nimcall, gcsafe.}
|
dataFn: proc (m: TArg) {.nimcall, gcsafe.}
|
||||||
data: TArg
|
data: TArg
|
||||||
ThreadId*[TArg] = ptr Thread[TArg] ## the current implementation uses
|
|
||||||
## a pointer as a thread ID.
|
{.deprecated: [TThread: Thread].}
|
||||||
{.deprecated: [TThread: Thread, TThreadId: ThreadId].}
|
|
||||||
|
|
||||||
var
|
var
|
||||||
threadDestructionHandlers {.rtlThreadVar.}: seq[proc () {.closure, gcsafe.}]
|
threadDestructionHandlers {.rtlThreadVar.}: seq[proc () {.closure, gcsafe.}]
|
||||||
|
|
@ -423,19 +420,20 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) =
|
||||||
when declared(threadType):
|
when declared(threadType):
|
||||||
threadType = ThreadType.NimThread
|
threadType = ThreadType.NimThread
|
||||||
when declared(registerThread):
|
when declared(registerThread):
|
||||||
thrd.stackBottom = addr(thrd)
|
thrd.core.stackBottom = addr(thrd)
|
||||||
registerThread(thrd)
|
registerThread(thrd.core)
|
||||||
p(thrd)
|
p(thrd)
|
||||||
when declared(registerThread): unregisterThread(thrd)
|
when declared(registerThread): unregisterThread(thrd.core)
|
||||||
when declared(deallocOsPages): deallocOsPages()
|
when declared(deallocOsPages): deallocOsPages()
|
||||||
else:
|
else:
|
||||||
threadProcWrapDispatch(thrd)
|
threadProcWrapDispatch(thrd)
|
||||||
|
|
||||||
template threadProcWrapperBody(closure: expr) {.immediate.} =
|
template threadProcWrapperBody(closure: expr) {.immediate.} =
|
||||||
when declared(globalsSlot): threadVarSetValue(globalsSlot, closure)
|
var thrd = cast[ptr Thread[TArg]](closure)
|
||||||
|
var core = thrd.core
|
||||||
|
when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core)
|
||||||
when declared(initAllocator):
|
when declared(initAllocator):
|
||||||
initAllocator()
|
initAllocator()
|
||||||
var thrd = cast[ptr Thread[TArg]](closure)
|
|
||||||
threadProcWrapStackFrame(thrd)
|
threadProcWrapStackFrame(thrd)
|
||||||
# Since an unhandled exception terminates the whole process (!), there is
|
# Since an unhandled exception terminates the whole process (!), there is
|
||||||
# no need for a ``try finally`` here, nor would it be correct: The current
|
# no need for a ``try finally`` here, nor would it be correct: The current
|
||||||
|
|
@ -444,7 +442,9 @@ template threadProcWrapperBody(closure: expr) {.immediate.} =
|
||||||
# page!
|
# page!
|
||||||
|
|
||||||
# mark as not running anymore:
|
# mark as not running anymore:
|
||||||
|
thrd.core = nil
|
||||||
thrd.dataFn = nil
|
thrd.dataFn = nil
|
||||||
|
deallocShared(cast[pointer](core))
|
||||||
|
|
||||||
{.push stack_trace:off.}
|
{.push stack_trace:off.}
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
|
|
@ -502,6 +502,10 @@ when false:
|
||||||
discard pthread_cancel(t.sys)
|
discard pthread_cancel(t.sys)
|
||||||
when declared(registerThread): unregisterThread(addr(t))
|
when declared(registerThread): unregisterThread(addr(t))
|
||||||
t.dataFn = nil
|
t.dataFn = nil
|
||||||
|
## if thread `t` already exited, `t.core` will be `null`.
|
||||||
|
if not isNil(t.core):
|
||||||
|
deallocShared(t.core)
|
||||||
|
t.core = nil
|
||||||
|
|
||||||
when hostOS == "windows":
|
when hostOS == "windows":
|
||||||
proc createThread*[TArg](t: var Thread[TArg],
|
proc createThread*[TArg](t: var Thread[TArg],
|
||||||
|
|
@ -510,9 +514,11 @@ when hostOS == "windows":
|
||||||
## creates a new thread `t` and starts its execution. Entry point is the
|
## creates a new thread `t` and starts its execution. Entry point is the
|
||||||
## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you
|
## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you
|
||||||
## don't need to pass any data to the thread.
|
## don't need to pass any data to the thread.
|
||||||
|
t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
|
||||||
|
|
||||||
when TArg isnot void: t.data = param
|
when TArg isnot void: t.data = param
|
||||||
t.dataFn = tp
|
t.dataFn = tp
|
||||||
when hasSharedHeap: t.stackSize = ThreadStackSize
|
when hasSharedHeap: t.core.stackSize = ThreadStackSize
|
||||||
var dummyThreadId: int32
|
var dummyThreadId: int32
|
||||||
t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg],
|
t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg],
|
||||||
addr(t), 0'i32, dummyThreadId)
|
addr(t), 0'i32, dummyThreadId)
|
||||||
|
|
@ -532,9 +538,11 @@ else:
|
||||||
## creates a new thread `t` and starts its execution. Entry point is the
|
## creates a new thread `t` and starts its execution. Entry point is the
|
||||||
## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you
|
## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you
|
||||||
## don't need to pass any data to the thread.
|
## don't need to pass any data to the thread.
|
||||||
|
t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
|
||||||
|
|
||||||
when TArg isnot void: t.data = param
|
when TArg isnot void: t.data = param
|
||||||
t.dataFn = tp
|
t.dataFn = tp
|
||||||
when hasSharedHeap: t.stackSize = ThreadStackSize
|
when hasSharedHeap: t.core.stackSize = ThreadStackSize
|
||||||
var a {.noinit.}: PthreadAttr
|
var a {.noinit.}: PthreadAttr
|
||||||
pthread_attr_init(a)
|
pthread_attr_init(a)
|
||||||
pthread_attr_setstacksize(a, ThreadStackSize)
|
pthread_attr_setstacksize(a, ThreadStackSize)
|
||||||
|
|
@ -554,10 +562,6 @@ else:
|
||||||
proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) =
|
proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) =
|
||||||
createThread[void](t, tp)
|
createThread[void](t, tp)
|
||||||
|
|
||||||
proc threadId*[TArg](t: var Thread[TArg]): ThreadId[TArg] {.inline.} =
|
|
||||||
## returns the thread ID of `t`.
|
|
||||||
result = addr(t)
|
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
proc mainThreadId*[TArg](): ThreadId[TArg] =
|
proc mainThreadId*[TArg](): ThreadId[TArg] =
|
||||||
## returns the thread ID of the main thread.
|
## returns the thread ID of the main thread.
|
||||||
|
|
|
||||||
|
|
@ -667,6 +667,8 @@ const
|
||||||
|
|
||||||
# Error Constants
|
# Error Constants
|
||||||
const
|
const
|
||||||
|
ERROR_FILE_NOT_FOUND* = 2
|
||||||
|
ERROR_PATH_NOT_FOUND* = 3
|
||||||
ERROR_ACCESS_DENIED* = 5
|
ERROR_ACCESS_DENIED* = 5
|
||||||
ERROR_HANDLE_EOF* = 38
|
ERROR_HANDLE_EOF* = 38
|
||||||
ERROR_BAD_ARGUMENTS* = 165
|
ERROR_BAD_ARGUMENTS* = 165
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
/* linenoise.c -- VERSION 1.0
|
/* linenoise.c -- guerrilla line editing library against the idea that a
|
||||||
*
|
* line editing lib needs to be 20,000 lines of C code.
|
||||||
* Guerrilla line editing library against the idea that a line editing lib
|
|
||||||
* needs to be 20,000 lines of C code.
|
|
||||||
*
|
*
|
||||||
* You can find the latest source code at:
|
* You can find the latest source code at:
|
||||||
*
|
*
|
||||||
|
|
@ -12,7 +10,7 @@
|
||||||
*
|
*
|
||||||
* ------------------------------------------------------------------------
|
* ------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
|
* Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||||
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||||
*
|
*
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
|
|
@ -113,17 +111,18 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#ifndef __LINENOISE_H
|
#include "linenoise.h"
|
||||||
# include "clinenoise.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
|
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
|
||||||
#define LINENOISE_MAX_LINE 4096
|
#define LINENOISE_MAX_LINE 4096
|
||||||
static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
|
static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
|
||||||
static linenoiseCompletionCallback *completionCallback = NULL;
|
static linenoiseCompletionCallback *completionCallback = NULL;
|
||||||
|
static linenoiseHintsCallback *hintsCallback = NULL;
|
||||||
|
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
|
||||||
|
|
||||||
static struct termios orig_termios; /* In order to restore at exit.*/
|
static struct termios orig_termios; /* In order to restore at exit.*/
|
||||||
static int rawmode = 0; /* For atexit() function to check if restore is needed*/
|
static int rawmode = 0; /* For atexit() function to check if restore is needed*/
|
||||||
|
|
@ -141,7 +140,7 @@ struct linenoiseState {
|
||||||
int ofd; /* Terminal stdout file descriptor. */
|
int ofd; /* Terminal stdout file descriptor. */
|
||||||
char *buf; /* Edited line buffer. */
|
char *buf; /* Edited line buffer. */
|
||||||
size_t buflen; /* Edited line buffer size. */
|
size_t buflen; /* Edited line buffer size. */
|
||||||
char *prompt; /* Prompt to display. */
|
const char *prompt; /* Prompt to display. */
|
||||||
size_t plen; /* Prompt length. */
|
size_t plen; /* Prompt length. */
|
||||||
size_t pos; /* Current cursor position. */
|
size_t pos; /* Current cursor position. */
|
||||||
size_t oldpos; /* Previous refresh cursor position. */
|
size_t oldpos; /* Previous refresh cursor position. */
|
||||||
|
|
@ -174,7 +173,7 @@ enum KEY_ACTION{
|
||||||
};
|
};
|
||||||
|
|
||||||
static void linenoiseAtExit(void);
|
static void linenoiseAtExit(void);
|
||||||
int linenoiseHistoryAdd(char *line);
|
int linenoiseHistoryAdd(const char *line);
|
||||||
static void refreshLine(struct linenoiseState *l);
|
static void refreshLine(struct linenoiseState *l);
|
||||||
|
|
||||||
/* Debugging macro. */
|
/* Debugging macro. */
|
||||||
|
|
@ -411,18 +410,30 @@ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
|
||||||
completionCallback = fn;
|
completionCallback = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Register a hits function to be called to show hits to the user at the
|
||||||
|
* right of the prompt. */
|
||||||
|
void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {
|
||||||
|
hintsCallback = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register a function to free the hints returned by the hints callback
|
||||||
|
* registered with linenoiseSetHintsCallback(). */
|
||||||
|
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {
|
||||||
|
freeHintsCallback = fn;
|
||||||
|
}
|
||||||
|
|
||||||
/* This function is used by the callback function registered by the user
|
/* This function is used by the callback function registered by the user
|
||||||
* in order to add completion options given the input string when the
|
* in order to add completion options given the input string when the
|
||||||
* user typed <tab>. See the example.c source code for a very easy to
|
* user typed <tab>. See the example.c source code for a very easy to
|
||||||
* understand example. */
|
* understand example. */
|
||||||
void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) {
|
void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
|
||||||
size_t len = strlen(str);
|
size_t len = strlen(str);
|
||||||
char *copy, **cvec;
|
char *copy, **cvec;
|
||||||
|
|
||||||
copy = (char*)malloc(len+1);
|
copy = malloc(len+1);
|
||||||
if (copy == NULL) return;
|
if (copy == NULL) return;
|
||||||
memcpy(copy,str,len+1);
|
memcpy(copy,str,len+1);
|
||||||
cvec = (char**)realloc(lc->cvec,sizeof(char*)*(lc->len+1));
|
cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
|
||||||
if (cvec == NULL) {
|
if (cvec == NULL) {
|
||||||
free(copy);
|
free(copy);
|
||||||
return;
|
return;
|
||||||
|
|
@ -447,12 +458,12 @@ static void abInit(struct abuf *ab) {
|
||||||
ab->len = 0;
|
ab->len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void abAppend(struct abuf *ab, char *s, int len) {
|
static void abAppend(struct abuf *ab, const char *s, int len) {
|
||||||
char *neww = (char*)realloc(ab->b,ab->len+len);
|
char *new = realloc(ab->b,ab->len+len);
|
||||||
|
|
||||||
if (neww == NULL) return;
|
if (new == NULL) return;
|
||||||
memcpy(neww+ab->len,s,len);
|
memcpy(new+ab->len,s,len);
|
||||||
ab->b = neww;
|
ab->b = new;
|
||||||
ab->len += len;
|
ab->len += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -460,6 +471,30 @@ static void abFree(struct abuf *ab) {
|
||||||
free(ab->b);
|
free(ab->b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Helper of refreshSingleLine() and refreshMultiLine() to show hints
|
||||||
|
* to the right of the prompt. */
|
||||||
|
void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
|
||||||
|
char seq[64];
|
||||||
|
if (hintsCallback && plen+l->len < l->cols) {
|
||||||
|
int color = -1, bold = 0;
|
||||||
|
char *hint = hintsCallback(l->buf,&color,&bold);
|
||||||
|
if (hint) {
|
||||||
|
int hintlen = strlen(hint);
|
||||||
|
int hintmaxlen = l->cols-(plen+l->len);
|
||||||
|
if (hintlen > hintmaxlen) hintlen = hintmaxlen;
|
||||||
|
if (bold == 1 && color == -1) color = 37;
|
||||||
|
if (color != -1 || bold != 0)
|
||||||
|
snprintf(seq,64,"\033[%d;%d;49m",bold,color);
|
||||||
|
abAppend(ab,seq,strlen(seq));
|
||||||
|
abAppend(ab,hint,hintlen);
|
||||||
|
if (color != -1 || bold != 0)
|
||||||
|
abAppend(ab,"\033[0m",4);
|
||||||
|
/* Call the function to free the hint returned. */
|
||||||
|
if (freeHintsCallback) freeHintsCallback(hint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Single line low level line refresh.
|
/* Single line low level line refresh.
|
||||||
*
|
*
|
||||||
* Rewrite the currently edited line accordingly to the buffer content,
|
* Rewrite the currently edited line accordingly to the buffer content,
|
||||||
|
|
@ -489,6 +524,8 @@ static void refreshSingleLine(struct linenoiseState *l) {
|
||||||
/* Write the prompt and the current buffer content */
|
/* Write the prompt and the current buffer content */
|
||||||
abAppend(&ab,l->prompt,strlen(l->prompt));
|
abAppend(&ab,l->prompt,strlen(l->prompt));
|
||||||
abAppend(&ab,buf,len);
|
abAppend(&ab,buf,len);
|
||||||
|
/* Show hits if any. */
|
||||||
|
refreshShowHints(&ab,l,plen);
|
||||||
/* Erase to right */
|
/* Erase to right */
|
||||||
snprintf(seq,64,"\x1b[0K");
|
snprintf(seq,64,"\x1b[0K");
|
||||||
abAppend(&ab,seq,strlen(seq));
|
abAppend(&ab,seq,strlen(seq));
|
||||||
|
|
@ -542,6 +579,9 @@ static void refreshMultiLine(struct linenoiseState *l) {
|
||||||
abAppend(&ab,l->prompt,strlen(l->prompt));
|
abAppend(&ab,l->prompt,strlen(l->prompt));
|
||||||
abAppend(&ab,l->buf,l->len);
|
abAppend(&ab,l->buf,l->len);
|
||||||
|
|
||||||
|
/* Show hits if any. */
|
||||||
|
refreshShowHints(&ab,l,plen);
|
||||||
|
|
||||||
/* If we are at the very end of the screen with our prompt, we need to
|
/* If we are at the very end of the screen with our prompt, we need to
|
||||||
* emit a newline and move the prompt to the first column. */
|
* emit a newline and move the prompt to the first column. */
|
||||||
if (l->pos &&
|
if (l->pos &&
|
||||||
|
|
@ -602,7 +642,7 @@ int linenoiseEditInsert(struct linenoiseState *l, char c) {
|
||||||
l->pos++;
|
l->pos++;
|
||||||
l->len++;
|
l->len++;
|
||||||
l->buf[l->len] = '\0';
|
l->buf[l->len] = '\0';
|
||||||
if ((!mlmode && l->plen+l->len < l->cols) /* || mlmode */) {
|
if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) {
|
||||||
/* Avoid a full update of the line in the
|
/* Avoid a full update of the line in the
|
||||||
* trivial case. */
|
* trivial case. */
|
||||||
if (write(l->ofd,&c,1) == -1) return -1;
|
if (write(l->ofd,&c,1) == -1) return -1;
|
||||||
|
|
@ -725,7 +765,7 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
|
||||||
* when ctrl+d is typed.
|
* when ctrl+d is typed.
|
||||||
*
|
*
|
||||||
* The function returns the length of the current buffer. */
|
* The function returns the length of the current buffer. */
|
||||||
static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, char *prompt)
|
static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
|
||||||
{
|
{
|
||||||
struct linenoiseState l;
|
struct linenoiseState l;
|
||||||
|
|
||||||
|
|
@ -776,6 +816,14 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||||
history_len--;
|
history_len--;
|
||||||
free(history[history_len]);
|
free(history[history_len]);
|
||||||
if (mlmode) linenoiseEditMoveEnd(&l);
|
if (mlmode) linenoiseEditMoveEnd(&l);
|
||||||
|
if (hintsCallback) {
|
||||||
|
/* Force a refresh without hints to leave the previous
|
||||||
|
* line as the user typed it after a newline. */
|
||||||
|
linenoiseHintsCallback *hc = hintsCallback;
|
||||||
|
hintsCallback = NULL;
|
||||||
|
refreshLine(&l);
|
||||||
|
hintsCallback = hc;
|
||||||
|
}
|
||||||
return (int)l.len;
|
return (int)l.len;
|
||||||
case CTRL_C: /* ctrl-c */
|
case CTRL_C: /* ctrl-c */
|
||||||
errno = EAGAIN;
|
errno = EAGAIN;
|
||||||
|
|
@ -931,41 +979,71 @@ void linenoisePrintKeyCodes(void) {
|
||||||
|
|
||||||
/* This function calls the line editing function linenoiseEdit() using
|
/* This function calls the line editing function linenoiseEdit() using
|
||||||
* the STDIN file descriptor set in raw mode. */
|
* the STDIN file descriptor set in raw mode. */
|
||||||
static int linenoiseRaw(char *buf, size_t buflen, char *prompt) {
|
static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
if (buflen == 0) {
|
if (buflen == 0) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (!isatty(STDIN_FILENO)) {
|
|
||||||
/* Not a tty: read from file / pipe. */
|
|
||||||
if (fgets(buf, buflen, stdin) == NULL) return -1;
|
|
||||||
count = strlen(buf);
|
|
||||||
if (count && buf[count-1] == '\n') {
|
|
||||||
count--;
|
|
||||||
buf[count] = '\0';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Interactive editing. */
|
|
||||||
if (enableRawMode(STDIN_FILENO) == -1) return -1;
|
if (enableRawMode(STDIN_FILENO) == -1) return -1;
|
||||||
count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
|
count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
|
||||||
disableRawMode(STDIN_FILENO);
|
disableRawMode(STDIN_FILENO);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function is called when linenoise() is called with the standard
|
||||||
|
* input file descriptor not attached to a TTY. So for example when the
|
||||||
|
* program using linenoise is called in pipe or with a file redirected
|
||||||
|
* to its standard input. In this case, we want to be able to return the
|
||||||
|
* line regardless of its length (by default we are limited to 4k). */
|
||||||
|
static char *linenoiseNoTTY(void) {
|
||||||
|
char *line = NULL;
|
||||||
|
size_t len = 0, maxlen = 0;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
if (len == maxlen) {
|
||||||
|
if (maxlen == 0) maxlen = 16;
|
||||||
|
maxlen *= 2;
|
||||||
|
char *oldval = line;
|
||||||
|
line = realloc(line,maxlen);
|
||||||
|
if (line == NULL) {
|
||||||
|
if (oldval) free(oldval);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int c = fgetc(stdin);
|
||||||
|
if (c == EOF || c == '\n') {
|
||||||
|
if (c == EOF && len == 0) {
|
||||||
|
free(line);
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
line[len] = '\0';
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
line[len] = c;
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* The high level function that is the main API of the linenoise library.
|
/* The high level function that is the main API of the linenoise library.
|
||||||
* This function checks if the terminal has basic capabilities, just checking
|
* This function checks if the terminal has basic capabilities, just checking
|
||||||
* for a blacklist of stupid terminals, and later either calls the line
|
* for a blacklist of stupid terminals, and later either calls the line
|
||||||
* editing function or uses dummy fgets() so that you will be able to type
|
* editing function or uses dummy fgets() so that you will be able to type
|
||||||
* something even in the most desperate of the conditions. */
|
* something even in the most desperate of the conditions. */
|
||||||
char *linenoise(char *prompt) {
|
char *linenoise(const char *prompt) {
|
||||||
char buf[LINENOISE_MAX_LINE];
|
char buf[LINENOISE_MAX_LINE];
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
if (isUnsupportedTerm()) {
|
if (!isatty(STDIN_FILENO)) {
|
||||||
|
/* Not a tty: read from file / pipe. In this mode we don't want any
|
||||||
|
* limit to the line size, so we call a function to handle that. */
|
||||||
|
return linenoiseNoTTY();
|
||||||
|
} else if (isUnsupportedTerm()) {
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
printf("%s",prompt);
|
printf("%s",prompt);
|
||||||
|
|
@ -984,6 +1062,14 @@ char *linenoise(char *prompt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This is just a wrapper the user may want to call in order to make sure
|
||||||
|
* the linenoise returned buffer is freed with the same allocator it was
|
||||||
|
* created with. Useful when the main program is using an alternative
|
||||||
|
* allocator. */
|
||||||
|
void linenoiseFree(void *ptr) {
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
/* ================================ History ================================= */
|
/* ================================ History ================================= */
|
||||||
|
|
||||||
/* Free the history, but does not reset it. Only used when we have to
|
/* Free the history, but does not reset it. Only used when we have to
|
||||||
|
|
@ -1011,14 +1097,14 @@ static void linenoiseAtExit(void) {
|
||||||
* histories, but will work well for a few hundred of entries.
|
* histories, but will work well for a few hundred of entries.
|
||||||
*
|
*
|
||||||
* Using a circular buffer is smarter, but a bit more complex to handle. */
|
* Using a circular buffer is smarter, but a bit more complex to handle. */
|
||||||
int linenoiseHistoryAdd(char *line) {
|
int linenoiseHistoryAdd(const char *line) {
|
||||||
char *linecopy;
|
char *linecopy;
|
||||||
|
|
||||||
if (history_max_len == 0) return 0;
|
if (history_max_len == 0) return 0;
|
||||||
|
|
||||||
/* Initialization on first call. */
|
/* Initialization on first call. */
|
||||||
if (history == NULL) {
|
if (history == NULL) {
|
||||||
history = (char**)malloc(sizeof(char*)*history_max_len);
|
history = malloc(sizeof(char*)*history_max_len);
|
||||||
if (history == NULL) return 0;
|
if (history == NULL) return 0;
|
||||||
memset(history,0,(sizeof(char*)*history_max_len));
|
memset(history,0,(sizeof(char*)*history_max_len));
|
||||||
}
|
}
|
||||||
|
|
@ -1045,14 +1131,14 @@ int linenoiseHistoryAdd(char *line) {
|
||||||
* just the latest 'len' elements if the new history length value is smaller
|
* just the latest 'len' elements if the new history length value is smaller
|
||||||
* than the amount of items already inside the history. */
|
* than the amount of items already inside the history. */
|
||||||
int linenoiseHistorySetMaxLen(int len) {
|
int linenoiseHistorySetMaxLen(int len) {
|
||||||
char **neww;
|
char **new;
|
||||||
|
|
||||||
if (len < 1) return 0;
|
if (len < 1) return 0;
|
||||||
if (history) {
|
if (history) {
|
||||||
int tocopy = history_len;
|
int tocopy = history_len;
|
||||||
|
|
||||||
neww = (char**)malloc(sizeof(char*)*len);
|
new = malloc(sizeof(char*)*len);
|
||||||
if (neww == NULL) return 0;
|
if (new == NULL) return 0;
|
||||||
|
|
||||||
/* If we can't copy everything, free the elements we'll not use. */
|
/* If we can't copy everything, free the elements we'll not use. */
|
||||||
if (len < tocopy) {
|
if (len < tocopy) {
|
||||||
|
|
@ -1061,10 +1147,10 @@ int linenoiseHistorySetMaxLen(int len) {
|
||||||
for (j = 0; j < tocopy-len; j++) free(history[j]);
|
for (j = 0; j < tocopy-len; j++) free(history[j]);
|
||||||
tocopy = len;
|
tocopy = len;
|
||||||
}
|
}
|
||||||
memset(neww,0,sizeof(char*)*len);
|
memset(new,0,sizeof(char*)*len);
|
||||||
memcpy(neww,history+(history_len-tocopy), sizeof(char*)*tocopy);
|
memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy);
|
||||||
free(history);
|
free(history);
|
||||||
history = neww;
|
history = new;
|
||||||
}
|
}
|
||||||
history_max_len = len;
|
history_max_len = len;
|
||||||
if (history_len > history_max_len)
|
if (history_len > history_max_len)
|
||||||
|
|
@ -1074,11 +1160,15 @@ int linenoiseHistorySetMaxLen(int len) {
|
||||||
|
|
||||||
/* Save the history in the specified file. On success 0 is returned
|
/* Save the history in the specified file. On success 0 is returned
|
||||||
* otherwise -1 is returned. */
|
* otherwise -1 is returned. */
|
||||||
int linenoiseHistorySave(char *filename) {
|
int linenoiseHistorySave(const char *filename) {
|
||||||
FILE *fp = fopen(filename,"w");
|
mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
|
||||||
|
FILE *fp;
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
|
fp = fopen(filename,"w");
|
||||||
|
umask(old_umask);
|
||||||
if (fp == NULL) return -1;
|
if (fp == NULL) return -1;
|
||||||
|
chmod(filename,S_IRUSR|S_IWUSR);
|
||||||
for (j = 0; j < history_len; j++)
|
for (j = 0; j < history_len; j++)
|
||||||
fprintf(fp,"%s\n",history[j]);
|
fprintf(fp,"%s\n",history[j]);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
@ -1090,7 +1180,7 @@ int linenoiseHistorySave(char *filename) {
|
||||||
*
|
*
|
||||||
* If the file exists and the operation succeeded 0 is returned, otherwise
|
* If the file exists and the operation succeeded 0 is returned, otherwise
|
||||||
* on error -1 is returned. */
|
* on error -1 is returned. */
|
||||||
int linenoiseHistoryLoad(char *filename) {
|
int linenoiseHistoryLoad(const char *filename) {
|
||||||
FILE *fp = fopen(filename,"r");
|
FILE *fp = fopen(filename,"r");
|
||||||
char buf[LINENOISE_MAX_LINE];
|
char buf[LINENOISE_MAX_LINE];
|
||||||
|
|
||||||
|
|
@ -39,22 +39,35 @@
|
||||||
#ifndef __LINENOISE_H
|
#ifndef __LINENOISE_H
|
||||||
#define __LINENOISE_H
|
#define __LINENOISE_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct linenoiseCompletions {
|
typedef struct linenoiseCompletions {
|
||||||
size_t len;
|
size_t len;
|
||||||
char **cvec;
|
char **cvec;
|
||||||
} linenoiseCompletions;
|
} linenoiseCompletions;
|
||||||
|
|
||||||
typedef void(linenoiseCompletionCallback)(char *, linenoiseCompletions *);
|
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
|
||||||
|
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
|
||||||
|
typedef void(linenoiseFreeHintsCallback)(void *);
|
||||||
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
|
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
|
||||||
void linenoiseAddCompletion(linenoiseCompletions *, char *);
|
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
|
||||||
|
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
|
||||||
|
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
||||||
|
|
||||||
char *linenoise(char *prompt);
|
char *linenoise(const char *prompt);
|
||||||
int linenoiseHistoryAdd(char *line);
|
void linenoiseFree(void *ptr);
|
||||||
|
int linenoiseHistoryAdd(const char *line);
|
||||||
int linenoiseHistorySetMaxLen(int len);
|
int linenoiseHistorySetMaxLen(int len);
|
||||||
int linenoiseHistorySave(char *filename);
|
int linenoiseHistorySave(const char *filename);
|
||||||
int linenoiseHistoryLoad(char *filename);
|
int linenoiseHistoryLoad(const char *filename);
|
||||||
void linenoiseClearScreen(void);
|
void linenoiseClearScreen(void);
|
||||||
void linenoiseSetMultiLine(int ml);
|
void linenoiseSetMultiLine(int ml);
|
||||||
void linenoisePrintKeyCodes(void);
|
void linenoisePrintKeyCodes(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* __LINENOISE_H */
|
#endif /* __LINENOISE_H */
|
||||||
|
|
@ -14,8 +14,7 @@ type
|
||||||
|
|
||||||
CompletionCallback* = proc (a2: cstring; a3: ptr Completions) {.cdecl.}
|
CompletionCallback* = proc (a2: cstring; a3: ptr Completions) {.cdecl.}
|
||||||
|
|
||||||
{.emit: staticRead"clinenoise.h".}
|
{.compile: "linenoise.c".}
|
||||||
{.emit: staticRead"clinenoise.c".}
|
|
||||||
|
|
||||||
proc setCompletionCallback*(a2: ptr CompletionCallback) {.
|
proc setCompletionCallback*(a2: ptr CompletionCallback) {.
|
||||||
importc: "linenoiseSetCompletionCallback".}
|
importc: "linenoiseSetCompletionCallback".}
|
||||||
|
|
|
||||||
|
|
@ -485,6 +485,10 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) =
|
||||||
incl gGlobalOptions, optCaasEnabled
|
incl gGlobalOptions, optCaasEnabled
|
||||||
isServing = true
|
isServing = true
|
||||||
wantMainModule()
|
wantMainModule()
|
||||||
|
|
||||||
|
if not fileExists(gProjectFull):
|
||||||
|
quit "cannot find file: " & gProjectFull
|
||||||
|
|
||||||
add(searchPaths, options.libpath)
|
add(searchPaths, options.libpath)
|
||||||
|
|
||||||
# do not stop after the first error:
|
# do not stop after the first error:
|
||||||
|
|
|
||||||
17
tests/collections/tdeques.nim
Normal file
17
tests/collections/tdeques.nim
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
discard """
|
||||||
|
output: '''true'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
import deques
|
||||||
|
|
||||||
|
|
||||||
|
proc index(self: Deque[int], idx: Natural): int =
|
||||||
|
self[idx]
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
var testDeque = initDeque[int]()
|
||||||
|
testDeque.addFirst(1)
|
||||||
|
assert testDeque.index(0) == 1
|
||||||
|
|
||||||
|
main()
|
||||||
|
echo "true"
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
discard """
|
discard """
|
||||||
line: 10
|
line: 10
|
||||||
errormsg: "value of type 'bool' has to be discarded"
|
errormsg: '''expression 'open(f, "arg.txt", fmRead, -1)' is of type 'bool' and has to be discarded'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc p =
|
proc p =
|
||||||
var f: TFile
|
var f: File
|
||||||
echo "hi"
|
echo "hi"
|
||||||
|
|
||||||
open(f, "arg.txt")
|
open(f, "arg.txt")
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
errormsg: "value of type 'string' has to be discarded"
|
errormsg: '''expression '"invalid"' is of type 'string' and has to be discarded'''
|
||||||
line: 12
|
line: 12
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
errormsg: "value of type 'bool' has to be discarded"
|
errormsg: "expression 'true' is of type 'bool' and has to be discarded"
|
||||||
line: 13
|
line: 13
|
||||||
file: "tdont_show_system.nim"
|
file: "tdont_show_system.nim"
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
line: 10
|
line: 10
|
||||||
errormsg: "value of type 'string' has to be discarded"
|
errormsg: "expression 'result[1 .. -(len(result), 1)]' is of type 'string' and has to be discarded"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# bug #578
|
# bug #578
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tstmtexp.nim"
|
file: "tstmtexp.nim"
|
||||||
line: 8
|
line: 8
|
||||||
errormsg: "value of type 'int literal(5)' has to be discarded"
|
errormsg: "expression '5' is of type 'int literal(5)' and has to be discarded"
|
||||||
"""
|
"""
|
||||||
# Test 3
|
# Test 3
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,16 @@ proc arrayItem(a: ArrayType): auto =
|
||||||
var arr: ArrayType[int]
|
var arr: ArrayType[int]
|
||||||
echo arrayItem(arr)
|
echo arrayItem(arr)
|
||||||
|
|
||||||
|
# bug #5597
|
||||||
|
|
||||||
|
template fail() = "what"
|
||||||
|
|
||||||
|
proc g[T](x: var T) =
|
||||||
|
x.fail = 3
|
||||||
|
|
||||||
|
type
|
||||||
|
Obj = object
|
||||||
|
fail: int
|
||||||
|
|
||||||
|
var y: Obj
|
||||||
|
g y
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
output: '''b()
|
output: '''b()
|
||||||
|
720 120.0
|
||||||
720 120.0'''
|
720 120.0'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -16,8 +17,6 @@ proc fac[T](x: T): T =
|
||||||
|
|
||||||
echo fac(6), " ", fac(5.0)
|
echo fac(6), " ", fac(5.0)
|
||||||
|
|
||||||
when false:
|
|
||||||
# This still doesn't work...
|
|
||||||
# test recursive generic with forwarding:
|
# test recursive generic with forwarding:
|
||||||
proc fac2[T](x: T): T
|
proc fac2[T](x: T): T
|
||||||
|
|
||||||
|
|
@ -26,3 +25,4 @@ when false:
|
||||||
proc fac2[T](x: T): T =
|
proc fac2[T](x: T): T =
|
||||||
if x == 0: return 1
|
if x == 0: return 1
|
||||||
else: return fac2(x-1)*x
|
else: return fac2(x-1)*x
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
output: "1.1000000000000001e+00 11"
|
output: "1.1 11\n42\n0"
|
||||||
ccodecheck: "!@'ClEnv'"
|
ccodecheck: "!@'ClEnv'"
|
||||||
disabled: "true"
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc p[T](a, b: T): T
|
proc p[T](a, b: T): T
|
||||||
|
|
@ -12,3 +11,18 @@ proc p[T](a, b: T): T =
|
||||||
let c = b
|
let c = b
|
||||||
result = a + b + c
|
result = a + b + c
|
||||||
|
|
||||||
|
# https://github.com/nim-lang/Nim/issues/4908
|
||||||
|
proc foo(t: typedesc[int]): int
|
||||||
|
proc bar(): int = foo(int)
|
||||||
|
proc foo(t: typedesc[int]): int =
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# https://github.com/nim-lang/Nim/issues/4104
|
||||||
|
proc print[T](t: T) # Error: implementation of 'print.print(t: int)' expected
|
||||||
|
print 42 # moving this line after the implementation fixes the error,
|
||||||
|
# but such behaviour makes forward declaration pointless
|
||||||
|
proc print[T](t: T) =
|
||||||
|
echo t
|
||||||
|
|
||||||
|
echo bar()
|
||||||
|
|
||||||
|
|
|
||||||
65
tests/generics/tobjecttyperel.nim
Normal file
65
tests/generics/tobjecttyperel.nim
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
discard """
|
||||||
|
output: '''(peel: 0, color: 15)
|
||||||
|
(color: 15)
|
||||||
|
17
|
||||||
|
(width: 0.0, taste: nil, color: 13)
|
||||||
|
(width: 0.0, taste: nil, color: 15)
|
||||||
|
cool'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
# bug #5241
|
||||||
|
type
|
||||||
|
BaseFruit[T] = object of RootObj
|
||||||
|
color: T
|
||||||
|
|
||||||
|
MidLevel[T] = object of BaseFruit[T]
|
||||||
|
|
||||||
|
Mango = object of MidLevel[int]
|
||||||
|
peel: int
|
||||||
|
|
||||||
|
Peach[X, T, Y] = object of T
|
||||||
|
width: X
|
||||||
|
taste: Y
|
||||||
|
|
||||||
|
proc setColor[T](self: var BaseFruit[T]) =
|
||||||
|
self.color = 15
|
||||||
|
|
||||||
|
proc setColor[T](self: var BaseFruit[T], c: int) =
|
||||||
|
self.color = c
|
||||||
|
|
||||||
|
var c: Mango
|
||||||
|
setColor(c)
|
||||||
|
echo c
|
||||||
|
|
||||||
|
var d: MidLevel[int]
|
||||||
|
setColor(d)
|
||||||
|
echo d
|
||||||
|
|
||||||
|
type
|
||||||
|
FooBase[T] = ref object of RootRef
|
||||||
|
v: T
|
||||||
|
BarClient = ref object of FooBase[int]
|
||||||
|
|
||||||
|
proc getColor[T](f: FooBase[T]): T = 17
|
||||||
|
var b: BarClient
|
||||||
|
echo getColor(b)
|
||||||
|
|
||||||
|
var z: Peach[float64, BaseFruit[int], string]
|
||||||
|
z.setColor(13)
|
||||||
|
echo z
|
||||||
|
|
||||||
|
z.setColor()
|
||||||
|
echo z
|
||||||
|
|
||||||
|
# bug #5411
|
||||||
|
type
|
||||||
|
Foo[T] = ref object of RootRef
|
||||||
|
v: T
|
||||||
|
Bar = ref object of Foo[int]
|
||||||
|
|
||||||
|
method m(o: RootRef) {.base.} = assert(false, "Abstract method called")
|
||||||
|
method m[T](o: Foo[T]) = echo "cool"
|
||||||
|
|
||||||
|
var v: Bar
|
||||||
|
v.new()
|
||||||
|
v.m() # Abstract method not called anymore
|
||||||
13
tests/js/ttimes.nim
Normal file
13
tests/js/ttimes.nim
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# test times module with js
|
||||||
|
discard """
|
||||||
|
action: run
|
||||||
|
"""
|
||||||
|
|
||||||
|
import times
|
||||||
|
|
||||||
|
# $ date --date='@2147483647'
|
||||||
|
# Tue 19 Jan 03:14:07 GMT 2038
|
||||||
|
|
||||||
|
block yeardayTest:
|
||||||
|
# check if yearday attribute is properly set on TimeInfo creation
|
||||||
|
doAssert fromSeconds(2147483647).getGMTime().yearday == 18
|
||||||
|
|
@ -35,5 +35,6 @@ except:
|
||||||
echo "Second readLine raised an exception"
|
echo "Second readLine raised an exception"
|
||||||
|
|
||||||
echo line
|
echo line
|
||||||
|
f.close()
|
||||||
|
|
||||||
removeFile(fn)
|
removeFile(fn)
|
||||||
|
|
|
||||||
|
|
@ -96,5 +96,12 @@ suite "bug #4494":
|
||||||
check:
|
check:
|
||||||
allIt(0..3, tags[it] != tags[it + 1])
|
allIt(0..3, tags[it] != tags[it + 1])
|
||||||
|
|
||||||
|
suite "bug #5571":
|
||||||
|
test "can define gcsafe procs within tests":
|
||||||
|
proc doTest {.gcsafe.} =
|
||||||
|
let line = "a"
|
||||||
|
check: line == "a"
|
||||||
|
doTest()
|
||||||
|
|
||||||
static:
|
static:
|
||||||
echo "compile end"
|
echo "compile end"
|
||||||
|
|
|
||||||
|
|
@ -186,25 +186,11 @@ proc longGCTests(r: var TResults, cat: Category, options: string) =
|
||||||
|
|
||||||
proc threadTests(r: var TResults, cat: Category, options: string) =
|
proc threadTests(r: var TResults, cat: Category, options: string) =
|
||||||
template test(filename: untyped) =
|
template test(filename: untyped) =
|
||||||
testSpec r, makeTest("tests/threads" / filename, options, cat, actionRun)
|
testSpec r, makeTest(filename, options, cat, actionRun)
|
||||||
testSpec r, makeTest("tests/threads" / filename, options &
|
testSpec r, makeTest(filename, options & " -d:release", cat, actionRun)
|
||||||
" -d:release", cat, actionRun)
|
testSpec r, makeTest(filename, options & " --tlsEmulation:on", cat, actionRun)
|
||||||
testSpec r, makeTest("tests/threads" / filename, options &
|
for t in os.walkFiles("tests/threads/t*.nim"):
|
||||||
" --tlsEmulation:on", cat, actionRun)
|
test(t)
|
||||||
|
|
||||||
test "tactors"
|
|
||||||
test "tactors2"
|
|
||||||
test "threadex"
|
|
||||||
# deactivated because output capturing still causes problems sometimes:
|
|
||||||
#test "trecursive_actor"
|
|
||||||
#test "threadring"
|
|
||||||
#test "tthreadanalysis"
|
|
||||||
#test "tthreadsort"
|
|
||||||
test "tthreadanalysis2"
|
|
||||||
#test "tthreadanalysis3"
|
|
||||||
test "tthreadheapviolation1"
|
|
||||||
test "tonthreadcreation"
|
|
||||||
test "tracy_allocator"
|
|
||||||
|
|
||||||
# ------------------------- IO tests ------------------------------------------
|
# ------------------------- IO tests ------------------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
30
tests/threads/tmanyjoin.nim
Normal file
30
tests/threads/tmanyjoin.nim
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
discard """
|
||||||
|
outputsub: "129"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, locks
|
||||||
|
|
||||||
|
type
|
||||||
|
MarkerObj = object
|
||||||
|
lock: Lock
|
||||||
|
counter: int
|
||||||
|
Marker = ptr MarkerObj
|
||||||
|
|
||||||
|
const
|
||||||
|
ThreadsCount = 129
|
||||||
|
SleepTime = 1000
|
||||||
|
|
||||||
|
proc worker(p: Marker) {.thread.} =
|
||||||
|
acquire(p.lock)
|
||||||
|
inc(p.counter)
|
||||||
|
release(p.lock)
|
||||||
|
sleep(SleepTime)
|
||||||
|
|
||||||
|
var p = cast[Marker](allocShared0(sizeof(MarkerObj)))
|
||||||
|
initLock(p.lock)
|
||||||
|
var ts = newSeq[Thread[Marker]](ThreadsCount)
|
||||||
|
for i in 0..<ts.len:
|
||||||
|
createThread(ts[i], worker, p)
|
||||||
|
|
||||||
|
joinThreads(ts)
|
||||||
|
echo p.counter
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
|
disabled: yes
|
||||||
outputsub: "0"
|
outputsub: "0"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
28
tests/threads/treusetvar.nim
Normal file
28
tests/threads/treusetvar.nim
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
discard """
|
||||||
|
outputsub: "129"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, locks
|
||||||
|
|
||||||
|
type
|
||||||
|
MarkerObj = object
|
||||||
|
lock: Lock
|
||||||
|
counter: int
|
||||||
|
Marker = ptr MarkerObj
|
||||||
|
|
||||||
|
const
|
||||||
|
ThreadsCount = 129
|
||||||
|
|
||||||
|
proc worker(p: Marker) {.thread.} =
|
||||||
|
acquire(p.lock)
|
||||||
|
inc(p.counter)
|
||||||
|
release(p.lock)
|
||||||
|
|
||||||
|
var p = cast[Marker](allocShared0(sizeof(MarkerObj)))
|
||||||
|
initLock(p.lock)
|
||||||
|
|
||||||
|
for i in 0..(ThreadsCount - 1):
|
||||||
|
var thread: Thread[Marker]
|
||||||
|
createThread(thread, worker, p)
|
||||||
|
joinThread(thread)
|
||||||
|
echo p.counter
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
|
disabled: yes
|
||||||
outputsub: "101"
|
outputsub: "101"
|
||||||
errormsg: "'threadFunc' is not GC-safe"
|
errormsg: "'threadFunc' is not GC-safe"
|
||||||
line: 39
|
line: 39
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
|
disabled: yes
|
||||||
outputsub: "channel is empty"
|
outputsub: "channel is empty"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,13 +52,13 @@ test(OrderR, OrderG)
|
||||||
test(OrderR, OrderAlias)
|
test(OrderR, OrderAlias)
|
||||||
test(OrderG, OrderAlias)
|
test(OrderG, OrderAlias)
|
||||||
|
|
||||||
typeRep(OrderAlias, Order) # true
|
typeRep(OrderAlias.R, Order.R) # true
|
||||||
typeRep(OrderR, Order) # true
|
typeRep(OrderR.R, Order.R) # true
|
||||||
typeRep(OrderG, Order) # true
|
typeRep(OrderG.R, Order.R) # true
|
||||||
|
|
||||||
typeRep(OrderR, OrderAlias) # true
|
typeRep(OrderR.R, OrderAlias.R) # true
|
||||||
typeRep(OrderG, OrderAlias) # true
|
typeRep(OrderG.R, OrderAlias.R) # true
|
||||||
typeRep(OrderR, OrderG) # true
|
typeRep(OrderR.R, OrderG.R) # true
|
||||||
|
|
||||||
echo OrderR.R # R
|
echo OrderR.R # R
|
||||||
echo OrderG.R # R
|
echo OrderG.R # R
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,9 @@ Changes affecting backwards compatibility
|
||||||
AST that is the same as what is used to define an enum. Previously the AST
|
AST that is the same as what is used to define an enum. Previously the AST
|
||||||
returned had a repeated ``EnumTy`` node and was missing the initial pragma
|
returned had a repeated ``EnumTy`` node and was missing the initial pragma
|
||||||
node (which is currently empty for an enum).
|
node (which is currently empty for an enum).
|
||||||
|
- If the dispatcher parameter's value used in multi method is ``nil``,
|
||||||
|
a ``NilError`` exception is raised. The old behavior was that the method
|
||||||
|
would be a ``nop`` then.
|
||||||
|
|
||||||
|
|
||||||
Library Additions
|
Library Additions
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue