mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 19:33:42 +00:00
Compare commits
2 Commits
pr_lent_it
...
pr_lk
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b786db630b | ||
|
|
d914a7a81a |
2
.github/workflows/ci_publish.yml
vendored
2
.github/workflows/ci_publish.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
|||||||
run: nim c -r -d:release ci/action.nim
|
run: nim c -r -d:release ci/action.nim
|
||||||
|
|
||||||
- name: 'Comment'
|
- name: 'Comment'
|
||||||
uses: actions/github-script@v9
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|||||||
@@ -60,23 +60,17 @@ errors.
|
|||||||
- `copyDirWithPermissions` to recursively preserve attributes
|
- `copyDirWithPermissions` to recursively preserve attributes
|
||||||
|
|
||||||
- `system.setLenUninit` now supports refc, JS and VM backends.
|
- `system.setLenUninit` now supports refc, JS and VM backends.
|
||||||
- `system.setLenUninit` for the `string` type. Allows setting length without initializing new memory on growth.
|
|
||||||
|
|
||||||
- `std/parseopt` now supports multiple parser modes via a `CliMode` enum.
|
- `std/parseopt` now supports multiple parser modes via a `CliMode` enum.
|
||||||
Modes include `Nim` (default, fully compatible) and two new experimental modes:
|
Modes include `Nim` (default, fully compatible) and two new experimental modes:
|
||||||
`Lax` and `Gnu` for different option parsing behaviors.
|
`Lax` and `Gnu` for different option parsing behaviors.
|
||||||
|
|
||||||
- `std/nre2` is added to replace deprecated NRE.
|
|
||||||
|
|
||||||
[//]: # "Changes:"
|
[//]: # "Changes:"
|
||||||
|
|
||||||
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
|
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
|
||||||
- `min`, `max`, and `sequtils`' `minIndex`, `maxIndex` and `minmax` for `openArray`s now accept a comparison function.
|
- `min`, `max`, and `sequtils`' `minIndex`, `maxIndex` and `minmax` for `openArray`s now accept a comparison function.
|
||||||
- `system.substr` implementation now uses `copymem` (wrapped C `memcpy`) for copying data, if available at compilation.
|
- `system.substr` implementation now uses `copymem` (wrapped C `memcpy`) for copying data, if available at compilation.
|
||||||
- `system.newStringUninit` is now considered free of side-effects allowing it to be used with `--experimental:strictFuncs`.
|
- `system.newStringUninit` is now considered free of side-effects allowing it to be used with `--experimental:strictFuncs`.
|
||||||
- `std/re` and `std/nre` are deprecated as PCRE library is obsolete.
|
|
||||||
Use https://github.com/nitely/nim-regex or `std/nre2`.
|
|
||||||
See: https://github.com/nim-lang/Nim/issues/23668.
|
|
||||||
|
|
||||||
## Language changes
|
## Language changes
|
||||||
|
|
||||||
|
|||||||
@@ -230,11 +230,11 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
|
|||||||
of tyString, tySequence:
|
of tyString, tySequence:
|
||||||
let atyp = skipTypes(a.t, abstractInst)
|
let atyp = skipTypes(a.t, abstractInst)
|
||||||
if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and
|
if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and
|
||||||
optSeqDestructors in p.config.globalOptions and not p.config.usesSso():
|
optSeqDestructors in p.config.globalOptions and not p.config.isDefined("nimsso"):
|
||||||
let bra = byRefLoc(p, a)
|
let bra = byRefLoc(p, a)
|
||||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
||||||
bra)
|
bra)
|
||||||
if p.config.usesSso() and
|
if p.config.isDefined("nimsso") and
|
||||||
skipTypes(a.t, abstractVar + abstractInst).kind == tyString:
|
skipTypes(a.t, abstractVar + abstractInst).kind == tyString:
|
||||||
let strPtr = if atyp.kind in {tyVar} and not compileToCpp(p.module): ra
|
let strPtr = if atyp.kind in {tyVar} and not compileToCpp(p.module): ra
|
||||||
else: addrLoc(p.config, a)
|
else: addrLoc(p.config, a)
|
||||||
@@ -296,11 +296,11 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
|
|||||||
of tyString, tySequence:
|
of tyString, tySequence:
|
||||||
let ntyp = skipTypes(n.typ, abstractInst)
|
let ntyp = skipTypes(n.typ, abstractInst)
|
||||||
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
|
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
|
||||||
optSeqDestructors in p.config.globalOptions and not p.config.usesSso():
|
optSeqDestructors in p.config.globalOptions and not p.config.isDefined("nimsso"):
|
||||||
let bra = byRefLoc(p, a)
|
let bra = byRefLoc(p, a)
|
||||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
|
||||||
bra)
|
bra)
|
||||||
if p.config.usesSso() and
|
if p.config.isDefined("nimsso") and
|
||||||
skipTypes(n.typ, abstractVar + abstractInst).kind == tyString:
|
skipTypes(n.typ, abstractVar + abstractInst).kind == tyString:
|
||||||
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
|
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
|
||||||
let ra = a.rdLoc
|
let ra = a.rdLoc
|
||||||
@@ -335,7 +335,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
|
|||||||
let ra = a.rdLoc
|
let ra = a.rdLoc
|
||||||
var t = TLoc(snippet: cDeref(ra))
|
var t = TLoc(snippet: cDeref(ra))
|
||||||
let lt = lenExpr(p, t)
|
let lt = lenExpr(p, t)
|
||||||
if p.config.usesSso():
|
if p.config.isDefined("nimsso"):
|
||||||
result.add(cCall(cgsymValue(p.module, "nimStrData"), ra))
|
result.add(cCall(cgsymValue(p.module, "nimStrData"), ra))
|
||||||
result.addArgumentSeparator()
|
result.addArgumentSeparator()
|
||||||
result.add(cCall(cgsymValue(p.module, "nimStrLen"), t.snippet))
|
result.add(cCall(cgsymValue(p.module, "nimStrLen"), t.snippet))
|
||||||
@@ -370,7 +370,7 @@ proc expressionsNeedsTmp(p: BProc, a: TLoc): TLoc =
|
|||||||
proc genArgStringToCString(p: BProc, n: PNode; result: var Builder; needsTmp: bool) {.inline.} =
|
proc genArgStringToCString(p: BProc, n: PNode; result: var Builder; needsTmp: bool) {.inline.} =
|
||||||
var a = initLocExpr(p, n[0])
|
var a = initLocExpr(p, n[0])
|
||||||
let tmp = withTmpIfNeeded(p, a, needsTmp)
|
let tmp = withTmpIfNeeded(p, a, needsTmp)
|
||||||
let ra = if p.config.usesSso(): byRefLoc(p, tmp) else: tmp.rdLoc
|
let ra = if p.config.isDefined("nimsso"): addrLoc(p.config, tmp) else: tmp.rdLoc
|
||||||
result.addCall(cgsymValue(p.module, "nimToCStringConv"), ra)
|
result.addCall(cgsymValue(p.module, "nimToCStringConv"), ra)
|
||||||
|
|
||||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; needsTmp = false) =
|
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; needsTmp = false) =
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc; flags: TAssignmentFlags) =
|
|||||||
bra)
|
bra)
|
||||||
let rd = d.rdLoc
|
let rd = d.rdLoc
|
||||||
let la = lenExpr(p, a)
|
let la = lenExpr(p, a)
|
||||||
if p.config.usesSso():
|
if p.config.isDefined("nimsso"):
|
||||||
let bra = byRefLoc(p, a)
|
let bra = byRefLoc(p, a)
|
||||||
p.s(cpsStmts).addFieldAssignment(rd, "Field0",
|
p.s(cpsStmts).addFieldAssignment(rd, "Field0",
|
||||||
cCall(cgsymValue(p.module, "nimStrData"), bra))
|
cCall(cgsymValue(p.module, "nimStrData"), bra))
|
||||||
@@ -963,7 +963,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
|
|||||||
|
|
||||||
proc cowBracket(p: BProc; n: PNode) =
|
proc cowBracket(p: BProc; n: PNode) =
|
||||||
if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions and
|
if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions and
|
||||||
not p.config.usesSso():
|
not p.config.isDefined("nimsso"):
|
||||||
let strCandidate = n[0]
|
let strCandidate = n[0]
|
||||||
if strCandidate.typ.skipTypes(abstractInst).kind == tyString:
|
if strCandidate.typ.skipTypes(abstractInst).kind == tyString:
|
||||||
var a: TLoc = initLocExpr(p, strCandidate)
|
var a: TLoc = initLocExpr(p, strCandidate)
|
||||||
@@ -989,7 +989,7 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
|
|||||||
# bug #19497
|
# bug #19497
|
||||||
d.lode = e
|
d.lode = e
|
||||||
else:
|
else:
|
||||||
let ssoStrSub = p.config.usesSso() and e[0].kind == nkBracketExpr and
|
let ssoStrSub = p.config.isDefined("nimsso") and e[0].kind == nkBracketExpr and
|
||||||
e[0][0].typ.skipTypes(abstractVar).kind == tyString
|
e[0][0].typ.skipTypes(abstractVar).kind == tyString
|
||||||
var a: TLoc = initLocExpr(p, e[0], if ssoStrSub: {lfEnforceDeref, lfPrepareForMutation} else: {})
|
var a: TLoc = initLocExpr(p, e[0], if ssoStrSub: {lfEnforceDeref, lfPrepareForMutation} else: {})
|
||||||
if e[0].kind in {nkHiddenStdConv, nkHiddenSubConv, nkConv} and not ignoreConv(e[0]):
|
if e[0].kind in {nkHiddenStdConv, nkHiddenSubConv, nkConv} and not ignoreConv(e[0]):
|
||||||
@@ -1318,7 +1318,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
|||||||
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
|
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
|
||||||
a.snippet = cDeref(a.snippet)
|
a.snippet = cDeref(a.snippet)
|
||||||
|
|
||||||
if p.config.usesSso() and ty.kind == tyString:
|
if p.config.isDefined("nimsso") and ty.kind == tyString:
|
||||||
let bra = byRefLoc(p, a)
|
let bra = byRefLoc(p, a)
|
||||||
if lfPrepareForMutation in d.flags:
|
if lfPrepareForMutation in d.flags:
|
||||||
# Use nimStrAtMutV3 to get a mutable reference (char*) to the element.
|
# Use nimStrAtMutV3 to get a mutable reference (char*) to the element.
|
||||||
@@ -1889,17 +1889,10 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
|||||||
var t = e.typ.skipTypes(abstractInstOwned)
|
var t = e.typ.skipTypes(abstractInstOwned)
|
||||||
let isRef = t.kind == tyRef
|
let isRef = t.kind == tyRef
|
||||||
|
|
||||||
# check if we need to construct the object in a temporary.
|
# check if we need to construct the object in a temporary
|
||||||
# A temp is needed when:
|
|
||||||
# - the constructor produces a ref (isRef)
|
|
||||||
# - the destination is not a writable location (d.k == locNone)
|
|
||||||
# - the constructed type differs from the destination type (subtype
|
|
||||||
# assignments need the genAssignment path for ObjectAssignmentDefect)
|
|
||||||
# - the constructor's field values may alias the destination (isPartOf)
|
|
||||||
var useTemp =
|
var useTemp =
|
||||||
isRef or
|
isRef or
|
||||||
d.k == locNone or
|
(d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or
|
||||||
(d.t != nil and not sameBackendType(t, d.t.skipTypes(abstractInstOwned))) or
|
|
||||||
(isPartOf(d.lode, e) != arNo)
|
(isPartOf(d.lode, e) != arNo)
|
||||||
|
|
||||||
var tmp: TLoc = default(TLoc)
|
var tmp: TLoc = default(TLoc)
|
||||||
@@ -2150,7 +2143,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
|
|||||||
putIntoDest(p, b, e, ra & cArgumentSeparator & ra & "Len_0", a.storage)
|
putIntoDest(p, b, e, ra & cArgumentSeparator & ra & "Len_0", a.storage)
|
||||||
of tyString, tySequence:
|
of tyString, tySequence:
|
||||||
let la = lenExpr(p, a)
|
let la = lenExpr(p, a)
|
||||||
if p.config.usesSso() and
|
if p.config.isDefined("nimsso") and
|
||||||
skipTypes(a.t, abstractVarRange).kind == tyString:
|
skipTypes(a.t, abstractVarRange).kind == tyString:
|
||||||
let bra = byRefLoc(p, a)
|
let bra = byRefLoc(p, a)
|
||||||
putIntoDest(p, b, e,
|
putIntoDest(p, b, e,
|
||||||
@@ -2743,7 +2736,7 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
|
|||||||
|
|
||||||
proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
|
proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
var a: TLoc = initLocExpr(p, n[0])
|
var a: TLoc = initLocExpr(p, n[0])
|
||||||
let arg = if p.config.usesSso(): byRefLoc(p, a) else: rdLoc(a)
|
let arg = if p.config.isDefined("nimsso"): addrLoc(p.config, a) else: rdLoc(a)
|
||||||
putIntoDest(p, d, n,
|
putIntoDest(p, d, n,
|
||||||
cgCall(p, "nimToCStringConv", arg),
|
cgCall(p, "nimToCStringConv", arg),
|
||||||
a.storage)
|
a.storage)
|
||||||
@@ -2822,7 +2815,7 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
|||||||
var src: TLoc = initLocExpr(p, n[2])
|
var src: TLoc = initLocExpr(p, n[2])
|
||||||
let destVal = rdLoc(a)
|
let destVal = rdLoc(a)
|
||||||
let srcVal = rdLoc(src)
|
let srcVal = rdLoc(src)
|
||||||
if p.config.usesSso() and
|
if p.config.isDefined("nimsso") and
|
||||||
n[1].typ.skipTypes(abstractVar).kind == tyString:
|
n[1].typ.skipTypes(abstractVar).kind == tyString:
|
||||||
# SmallString: destroy dst then struct-copy src; no .p field aliasing needed
|
# SmallString: destroy dst then struct-copy src; no .p field aliasing needed
|
||||||
genStmts(p, n[3])
|
genStmts(p, n[3])
|
||||||
@@ -2871,7 +2864,7 @@ proc genDestroy(p: BProc; n: PNode) =
|
|||||||
case t.kind
|
case t.kind
|
||||||
of tyString:
|
of tyString:
|
||||||
var a: TLoc = initLocExpr(p, arg)
|
var a: TLoc = initLocExpr(p, arg)
|
||||||
if p.config.usesSso():
|
if p.config.isDefined("nimsso"):
|
||||||
# SmallString: delegate to nimDestroyStrV1 (rc-based, handles static strings)
|
# SmallString: delegate to nimDestroyStrV1 (rc-based, handles static strings)
|
||||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimDestroyStrV1"), rdLoc(a))
|
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimDestroyStrV1"), rdLoc(a))
|
||||||
else:
|
else:
|
||||||
@@ -4243,7 +4236,7 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul
|
|||||||
genConstObjConstr(p, n, isConst, result)
|
genConstObjConstr(p, n, isConst, result)
|
||||||
of tyString, tyCstring:
|
of tyString, tyCstring:
|
||||||
if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString:
|
if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString:
|
||||||
if p.config.usesSso():
|
if p.config.isDefined("nimsso"):
|
||||||
genStringLiteralV3Const(p.module, n, isConst, result)
|
genStringLiteralV3Const(p.module, n, isConst, result)
|
||||||
else:
|
else:
|
||||||
genStringLiteralV2Const(p.module, n, isConst, result)
|
genStringLiteralV2Const(p.module, n, isConst, result)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ template detectVersion(field, corename) =
|
|||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
proc detectStrVersion(m: BModule): int =
|
proc detectStrVersion(m: BModule): int =
|
||||||
if m.g.config.usesSso() and
|
if m.g.config.isDefined("nimsso") and
|
||||||
m.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc, gcHooks}:
|
m.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc, gcHooks}:
|
||||||
result = 3
|
result = 3
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1165,7 +1165,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
// C++ exception occurred, not under Nim's control.
|
// C++ exception occured, not under Nim's control.
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
/* finally: */
|
/* finally: */
|
||||||
@@ -1940,7 +1940,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
|
|||||||
elif optFieldCheck in p.options and isDiscriminantField(e[0]):
|
elif optFieldCheck in p.options and isDiscriminantField(e[0]):
|
||||||
genLineDir(p, e)
|
genLineDir(p, e)
|
||||||
asgnFieldDiscriminant(p, e)
|
asgnFieldDiscriminant(p, e)
|
||||||
elif p.config.usesSso() and e[0].kind == nkBracketExpr and
|
elif p.config.isDefined("nimsso") and e[0].kind == nkBracketExpr and
|
||||||
e[0][0].typ.skipTypes(abstractVar).kind == tyString:
|
e[0][0].typ.skipTypes(abstractVar).kind == tyString:
|
||||||
# nimsso: s[i] = c → nimStrPutV3(&s, i, c) (handles COW internally)
|
# nimsso: s[i] = c → nimStrPutV3(&s, i, c) (handles COW internally)
|
||||||
genLineDir(p, e)
|
genLineDir(p, e)
|
||||||
|
|||||||
@@ -389,7 +389,7 @@ proc lenField(p: BProc, val: Rope): Rope {.inline.} =
|
|||||||
|
|
||||||
proc lenExpr(p: BProc; a: TLoc): Rope =
|
proc lenExpr(p: BProc; a: TLoc): Rope =
|
||||||
if optSeqDestructors in p.config.globalOptions:
|
if optSeqDestructors in p.config.globalOptions:
|
||||||
if p.config.usesSso() and a.lode != nil and a.t != nil and
|
if p.config.isDefined("nimsso") and a.lode != nil and a.t != nil and
|
||||||
a.t.skipTypes(abstractInst).kind == tyString:
|
a.t.skipTypes(abstractInst).kind == tyString:
|
||||||
result = cCall(cgsymValue(p.module, "nimStrLen"), rdLoc(a))
|
result = cCall(cgsymValue(p.module, "nimStrLen"), rdLoc(a))
|
||||||
else:
|
else:
|
||||||
@@ -534,7 +534,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
|
|||||||
|
|
||||||
let atyp = skipTypes(loc.t, abstractInst)
|
let atyp = skipTypes(loc.t, abstractInst)
|
||||||
let rl = rdLoc(loc)
|
let rl = rdLoc(loc)
|
||||||
if typ.kind == tyString and p.config.usesSso():
|
if typ.kind == tyString and p.config.isDefined("nimsso"):
|
||||||
# SmallString zero state: bytes=0 (slen=0 in low byte, all inline chars zeroed)
|
# SmallString zero state: bytes=0 (slen=0 in low byte, all inline chars zeroed)
|
||||||
if atyp.kind in {tyVar, tyLent}:
|
if atyp.kind in {tyVar, tyLent}:
|
||||||
p.s(cpsStmts).addAssignment(derefField(rl, "bytes"), cIntValue(0))
|
p.s(cpsStmts).addAssignment(derefField(rl, "bytes"), cIntValue(0))
|
||||||
@@ -592,7 +592,7 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
|
|||||||
let typ = loc.t
|
let typ = loc.t
|
||||||
if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}:
|
if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}:
|
||||||
let rl = rdLoc(loc)
|
let rl = rdLoc(loc)
|
||||||
if skipTypes(typ, abstractInst + {tyStatic}).kind == tyString and p.config.usesSso():
|
if skipTypes(typ, abstractInst + {tyStatic}).kind == tyString and p.config.isDefined("nimsso"):
|
||||||
# SmallString zero state: bytes=0 (slen=0 in low byte, all inline chars zeroed)
|
# SmallString zero state: bytes=0 (slen=0 in low byte, all inline chars zeroed)
|
||||||
p.s(cpsStmts).addFieldAssignment(rl, "bytes", cIntValue(0))
|
p.s(cpsStmts).addFieldAssignment(rl, "bytes", cIntValue(0))
|
||||||
p.s(cpsStmts).addFieldAssignment(rl, "more", NimNil)
|
p.s(cpsStmts).addFieldAssignment(rl, "more", NimNil)
|
||||||
|
|||||||
@@ -250,7 +250,6 @@ const
|
|||||||
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
|
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
|
||||||
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
|
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
|
||||||
errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found"
|
errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found"
|
||||||
errDefaultOrSsoExpectedButXFound = "'default' or 'sso' expected, but '$1' found"
|
|
||||||
|
|
||||||
template warningOptionNoop(switch: string) =
|
template warningOptionNoop(switch: string) =
|
||||||
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
|
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
|
||||||
@@ -307,13 +306,6 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
|
|||||||
else:
|
else:
|
||||||
result = false
|
result = false
|
||||||
localError(conf, info, errInvalidExceptionSystem % arg)
|
localError(conf, info, errInvalidExceptionSystem % arg)
|
||||||
of "strings":
|
|
||||||
case arg.normalize
|
|
||||||
of "default": result = conf.selectedStrings == stringDefault
|
|
||||||
of "sso": result = conf.selectedStrings == stringSso
|
|
||||||
else:
|
|
||||||
result = false
|
|
||||||
localError(conf, info, errDefaultOrSsoExpectedButXFound % arg)
|
|
||||||
of "experimental":
|
of "experimental":
|
||||||
try:
|
try:
|
||||||
result = conf.features.contains parseEnum[Feature](arg)
|
result = conf.features.contains parseEnum[Feature](arg)
|
||||||
@@ -758,17 +750,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
|||||||
processMemoryManagementOption(switch, arg, pass, info, conf)
|
processMemoryManagementOption(switch, arg, pass, info, conf)
|
||||||
of "mm":
|
of "mm":
|
||||||
processMemoryManagementOption(switch, arg, pass, info, conf)
|
processMemoryManagementOption(switch, arg, pass, info, conf)
|
||||||
of "strings":
|
|
||||||
expectArg(conf, switch, arg, pass, info)
|
|
||||||
if pass in {passCmd2, passPP}:
|
|
||||||
case arg.normalize
|
|
||||||
of "default":
|
|
||||||
conf.selectedStrings = stringDefault
|
|
||||||
of "sso":
|
|
||||||
conf.selectedStrings = stringSso
|
|
||||||
defineSymbol(conf.symbols, "nimsso")
|
|
||||||
else:
|
|
||||||
localError(conf, info, errDefaultOrSsoExpectedButXFound % arg)
|
|
||||||
of "warnings", "w":
|
of "warnings", "w":
|
||||||
if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
|
if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
|
||||||
of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)
|
of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)
|
||||||
|
|||||||
@@ -10,10 +10,10 @@
|
|||||||
## Generate a .build.nif file for nifmake from a Nim project.
|
## Generate a .build.nif file for nifmake from a Nim project.
|
||||||
## This enables incremental and parallel compilation using the `m` switch.
|
## This enables incremental and parallel compilation using the `m` switch.
|
||||||
|
|
||||||
import std / [os, tables, sets, times, osproc]
|
import std / [os, tables, sets, times, osproc, strutils]
|
||||||
import options, msgs, lineinfos, pathutils
|
import options, msgs, lineinfos, pathutils
|
||||||
|
|
||||||
import "../dist/nimony/src/lib" / [nifstreams, bitabs, nifreader, nifbuilder]
|
import "../dist/nimony/src/lib" / [nifstreams, nifcursors, bitabs, nifreader, nifbuilder]
|
||||||
import "../dist/nimony/src/gear2" / modnames
|
import "../dist/nimony/src/gear2" / modnames
|
||||||
|
|
||||||
type
|
type
|
||||||
@@ -79,19 +79,22 @@ proc runNifler(c: DepContext; nimFile: string): bool =
|
|||||||
let exitCode = execShellCmd(cmd)
|
let exitCode = execShellCmd(cmd)
|
||||||
result = exitCode == 0
|
result = exitCode == 0
|
||||||
|
|
||||||
proc resolveImport(c: DepContext; origin, toResolve: string): string =
|
proc resolveFile(c: DepContext; origin, toResolve: string): string =
|
||||||
## Resolve an import path using the compiler's normal module lookup rules.
|
## Resolve an import path relative to origin file
|
||||||
result = findModule(c.config, toResolve, origin).string
|
# Handle std/ prefix
|
||||||
|
var path = toResolve
|
||||||
|
if path.startsWith("std/"):
|
||||||
|
path = path.substr(4)
|
||||||
|
|
||||||
proc resolveInclude(c: DepContext; origin, toResolve: string): string =
|
# Try relative to origin first
|
||||||
## Resolve an include path relative to the including file or the search paths.
|
|
||||||
let originDir = parentDir(origin)
|
let originDir = parentDir(origin)
|
||||||
result = originDir / toResolve.addFileExt("nim")
|
result = originDir / path.addFileExt("nim")
|
||||||
if fileExists(result):
|
if fileExists(result):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
# Try search paths
|
||||||
for searchPath in c.config.searchPaths:
|
for searchPath in c.config.searchPaths:
|
||||||
result = searchPath.string / toResolve.addFileExt("nim")
|
result = searchPath.string / path.addFileExt("nim")
|
||||||
if fileExists(result):
|
if fileExists(result):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -100,7 +103,7 @@ proc resolveInclude(c: DepContext; origin, toResolve: string): string =
|
|||||||
proc traverseDeps(c: var DepContext; pair: FilePair; current: Node)
|
proc traverseDeps(c: var DepContext; pair: FilePair; current: Node)
|
||||||
|
|
||||||
proc processInclude(c: var DepContext; includePath: string; current: Node) =
|
proc processInclude(c: var DepContext; includePath: string; current: Node) =
|
||||||
let resolved = resolveInclude(c, current.files[current.files.len - 1].nimFile, includePath)
|
let resolved = resolveFile(c, current.files[current.files.len - 1].nimFile, includePath)
|
||||||
if resolved.len == 0 or not fileExists(resolved):
|
if resolved.len == 0 or not fileExists(resolved):
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -115,7 +118,7 @@ proc processInclude(c: var DepContext; includePath: string; current: Node) =
|
|||||||
discard c.includeStack.pop()
|
discard c.includeStack.pop()
|
||||||
|
|
||||||
proc processImport(c: var DepContext; importPath: string; current: Node) =
|
proc processImport(c: var DepContext; importPath: string; current: Node) =
|
||||||
let resolved = resolveImport(c, current.files[0].nimFile, importPath)
|
let resolved = resolveFile(c, current.files[0].nimFile, importPath)
|
||||||
if resolved.len == 0 or not fileExists(resolved):
|
if resolved.len == 0 or not fileExists(resolved):
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -137,171 +140,6 @@ proc processImport(c: var DepContext; importPath: string; current: Node) =
|
|||||||
if existingIdx notin current.deps:
|
if existingIdx notin current.deps:
|
||||||
current.deps.add existingIdx
|
current.deps.add existingIdx
|
||||||
|
|
||||||
proc skipSubtree(s: var Stream; first: PackedToken) =
|
|
||||||
## Consume tokens until the ParLe at `first` is balanced. Caller has
|
|
||||||
## already obtained `first`.
|
|
||||||
if first.kind != ParLe: return
|
|
||||||
var depth = 1
|
|
||||||
while depth > 0:
|
|
||||||
let t = next(s)
|
|
||||||
if t.kind == ParLe: inc depth
|
|
||||||
elif t.kind == ParRi: dec depth
|
|
||||||
elif t.kind == EofToken: return
|
|
||||||
|
|
||||||
proc evalCondExpr(c: DepContext; s: var Stream): bool =
|
|
||||||
## Read exactly one condition expression from `s` and return its truth
|
|
||||||
## value. Consumes tokens whether the expression is recognised or not so
|
|
||||||
## the caller stays in sync. Recognises `defined(IDENT)`, the boolean
|
|
||||||
## operators `not`/`and`/`or`, and the literals `true`/`false`. Anything
|
|
||||||
## else (e.g. a call to an arbitrary proc) is treated as `true` — the
|
|
||||||
## conservative direction, since a false negative here drops a real
|
|
||||||
## dependency from the build graph.
|
|
||||||
let t = next(s)
|
|
||||||
case t.kind
|
|
||||||
of Ident:
|
|
||||||
case pool.strings[t.litId]
|
|
||||||
of "true": result = true
|
|
||||||
of "false": result = false
|
|
||||||
else: result = true
|
|
||||||
of ParLe:
|
|
||||||
let tag = pool.tags[t.tagId]
|
|
||||||
case tag
|
|
||||||
of "call", "cmd", "callstrlit", "infix", "prefix":
|
|
||||||
# First child is the head (function/operator name).
|
|
||||||
let head = next(s)
|
|
||||||
var name = ""
|
|
||||||
if head.kind == Ident: name = pool.strings[head.litId]
|
|
||||||
case name
|
|
||||||
of "defined":
|
|
||||||
let arg = next(s)
|
|
||||||
var sym = ""
|
|
||||||
if arg.kind == Ident: sym = pool.strings[arg.litId]
|
|
||||||
result = sym.len > 0 and isDefined(c.config, sym)
|
|
||||||
of "not":
|
|
||||||
result = not evalCondExpr(c, s)
|
|
||||||
of "and":
|
|
||||||
result = evalCondExpr(c, s)
|
|
||||||
if result: result = evalCondExpr(c, s)
|
|
||||||
else: skipSubtree(s, next(s))
|
|
||||||
of "or":
|
|
||||||
result = evalCondExpr(c, s)
|
|
||||||
if not result: result = evalCondExpr(c, s)
|
|
||||||
else: skipSubtree(s, next(s))
|
|
||||||
else:
|
|
||||||
result = true
|
|
||||||
# Drain whatever remains until the matching ParRi.
|
|
||||||
var depth = 1
|
|
||||||
while depth > 0:
|
|
||||||
let n = next(s)
|
|
||||||
if n.kind == ParLe: inc depth
|
|
||||||
elif n.kind == ParRi: dec depth
|
|
||||||
elif n.kind == EofToken: return
|
|
||||||
of "not":
|
|
||||||
result = not evalCondExpr(c, s)
|
|
||||||
var depth = 1
|
|
||||||
while depth > 0:
|
|
||||||
let n = next(s)
|
|
||||||
if n.kind == ParLe: inc depth
|
|
||||||
elif n.kind == ParRi: dec depth
|
|
||||||
elif n.kind == EofToken: return
|
|
||||||
of "and":
|
|
||||||
result = evalCondExpr(c, s)
|
|
||||||
if result: result = evalCondExpr(c, s)
|
|
||||||
else: skipSubtree(s, next(s))
|
|
||||||
# consume closing ParRi
|
|
||||||
var depth = 1
|
|
||||||
while depth > 0:
|
|
||||||
let n = next(s)
|
|
||||||
if n.kind == ParLe: inc depth
|
|
||||||
elif n.kind == ParRi: dec depth
|
|
||||||
elif n.kind == EofToken: return
|
|
||||||
of "or":
|
|
||||||
result = evalCondExpr(c, s)
|
|
||||||
if not result: result = evalCondExpr(c, s)
|
|
||||||
else: skipSubtree(s, next(s))
|
|
||||||
var depth = 1
|
|
||||||
while depth > 0:
|
|
||||||
let n = next(s)
|
|
||||||
if n.kind == ParLe: inc depth
|
|
||||||
elif n.kind == ParRi: dec depth
|
|
||||||
elif n.kind == EofToken: return
|
|
||||||
else:
|
|
||||||
skipSubtree(s, t)
|
|
||||||
result = true
|
|
||||||
else:
|
|
||||||
result = true
|
|
||||||
|
|
||||||
proc whenMarkerHolds(c: DepContext; s: var Stream): bool =
|
|
||||||
## Caller has just consumed the `(when` ParLe. Read children until the
|
|
||||||
## matching `)`, AND-ing each evaluated condition.
|
|
||||||
result = true
|
|
||||||
while true:
|
|
||||||
# peek by reading; if it's ParRi, we're done
|
|
||||||
let t = next(s)
|
|
||||||
if t.kind == ParRi: return
|
|
||||||
if t.kind == EofToken: return
|
|
||||||
if t.kind == ParLe:
|
|
||||||
# Re-feed by manually evaluating the subtree starting at `t`.
|
|
||||||
# evalCondExpr expects to read its own opener, so handle it directly.
|
|
||||||
let tag = pool.tags[t.tagId]
|
|
||||||
case tag
|
|
||||||
of "call", "cmd", "callstrlit", "infix", "prefix":
|
|
||||||
let head = next(s)
|
|
||||||
var name = ""
|
|
||||||
if head.kind == Ident: name = pool.strings[head.litId]
|
|
||||||
var ok = true
|
|
||||||
case name
|
|
||||||
of "defined":
|
|
||||||
let arg = next(s)
|
|
||||||
var sym = ""
|
|
||||||
if arg.kind == Ident: sym = pool.strings[arg.litId]
|
|
||||||
ok = sym.len > 0 and isDefined(c.config, sym)
|
|
||||||
of "not":
|
|
||||||
ok = not evalCondExpr(c, s)
|
|
||||||
of "and":
|
|
||||||
ok = evalCondExpr(c, s)
|
|
||||||
if ok: ok = evalCondExpr(c, s)
|
|
||||||
of "or":
|
|
||||||
ok = evalCondExpr(c, s)
|
|
||||||
if not ok: ok = evalCondExpr(c, s)
|
|
||||||
else:
|
|
||||||
ok = true
|
|
||||||
# finish the subtree
|
|
||||||
var depth = 1
|
|
||||||
while depth > 0:
|
|
||||||
let n = next(s)
|
|
||||||
if n.kind == ParLe: inc depth
|
|
||||||
elif n.kind == ParRi: dec depth
|
|
||||||
elif n.kind == EofToken: return
|
|
||||||
if not ok: result = false
|
|
||||||
of "not", "and", "or":
|
|
||||||
# Re-emit a synthetic dispatch: rewrap by descending.
|
|
||||||
var ok = true
|
|
||||||
case tag
|
|
||||||
of "not":
|
|
||||||
ok = not evalCondExpr(c, s)
|
|
||||||
of "and":
|
|
||||||
ok = evalCondExpr(c, s)
|
|
||||||
if ok: ok = evalCondExpr(c, s)
|
|
||||||
of "or":
|
|
||||||
ok = evalCondExpr(c, s)
|
|
||||||
if not ok: ok = evalCondExpr(c, s)
|
|
||||||
else: discard
|
|
||||||
var depth = 1
|
|
||||||
while depth > 0:
|
|
||||||
let n = next(s)
|
|
||||||
if n.kind == ParLe: inc depth
|
|
||||||
elif n.kind == ParRi: dec depth
|
|
||||||
elif n.kind == EofToken: return
|
|
||||||
if not ok: result = false
|
|
||||||
else:
|
|
||||||
# Unknown — treat as true and skip.
|
|
||||||
skipSubtree(s, t)
|
|
||||||
elif t.kind == Ident:
|
|
||||||
let v = pool.strings[t.litId]
|
|
||||||
if v == "false": result = false
|
|
||||||
# else (true / unknown ident): keep result
|
|
||||||
|
|
||||||
proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
|
proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
|
||||||
## Read a .deps.nif file and process imports/includes
|
## Read a .deps.nif file and process imports/includes
|
||||||
let depsPath = c.depsFile(pair)
|
let depsPath = c.depsFile(pair)
|
||||||
@@ -323,27 +161,12 @@ proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
|
|||||||
if t.kind == ParLe:
|
if t.kind == ParLe:
|
||||||
let tag = pool.tags[t.tagId]
|
let tag = pool.tags[t.tagId]
|
||||||
case tag
|
case tag
|
||||||
of "import", "fromimport", "include":
|
of "import", "fromimport":
|
||||||
# Read first child. May be a `(when COND...)` marker — parse and
|
# Read import path
|
||||||
# evaluate; if the condition is statically false, skip the import
|
|
||||||
# entirely. Otherwise advance past the marker and parse the path.
|
|
||||||
t = next(s)
|
t = next(s)
|
||||||
var live = true
|
# Check for "when" marker (conditional import)
|
||||||
if t.kind == ParLe and pool.tags[t.tagId] == "when":
|
if t.kind == Ident and pool.strings[t.litId] == "when":
|
||||||
# whenMarkerHolds consumes everything up to and including the
|
t = next(s) # skip it, still process the import
|
||||||
# closing `)` of the `(when ...)` subtree.
|
|
||||||
live = whenMarkerHolds(c, s)
|
|
||||||
t = next(s)
|
|
||||||
if not live:
|
|
||||||
# Drain the rest of this import/include node.
|
|
||||||
var depth = 1
|
|
||||||
while depth > 0:
|
|
||||||
let n = next(s)
|
|
||||||
if n.kind == ParLe: inc depth
|
|
||||||
elif n.kind == ParRi: dec depth
|
|
||||||
elif n.kind == EofToken: break
|
|
||||||
t = next(s)
|
|
||||||
continue
|
|
||||||
# Handle path expression (could be ident, string, or infix like std/foo)
|
# Handle path expression (could be ident, string, or infix like std/foo)
|
||||||
var importPath = ""
|
var importPath = ""
|
||||||
if t.kind == Ident:
|
if t.kind == Ident:
|
||||||
@@ -361,11 +184,26 @@ proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
|
|||||||
if t.kind == Ident: # second part (foo)
|
if t.kind == Ident: # second part (foo)
|
||||||
importPath = importPath & "/" & pool.strings[t.litId]
|
importPath = importPath & "/" & pool.strings[t.litId]
|
||||||
if importPath.len > 0:
|
if importPath.len > 0:
|
||||||
if tag == "include":
|
processImport(c, importPath, current)
|
||||||
processInclude(c, importPath, current)
|
# Skip to end of import node
|
||||||
else:
|
var depth = 1
|
||||||
processImport(c, importPath, current)
|
while depth > 0:
|
||||||
# Skip to end of node
|
t = next(s)
|
||||||
|
if t.kind == ParLe: inc depth
|
||||||
|
elif t.kind == ParRi: dec depth
|
||||||
|
of "include":
|
||||||
|
# Read include path
|
||||||
|
t = next(s)
|
||||||
|
if t.kind == Ident and pool.strings[t.litId] == "when":
|
||||||
|
t = next(s) # skip conditional marker
|
||||||
|
var includePath = ""
|
||||||
|
if t.kind == Ident:
|
||||||
|
includePath = pool.strings[t.litId]
|
||||||
|
elif t.kind == StringLit:
|
||||||
|
includePath = pool.strings[t.litId]
|
||||||
|
if includePath.len > 0:
|
||||||
|
processInclude(c, includePath, current)
|
||||||
|
# Skip to end
|
||||||
var depth = 1
|
var depth = 1
|
||||||
while depth > 0:
|
while depth > 0:
|
||||||
t = next(s)
|
t = next(s)
|
||||||
|
|||||||
@@ -1544,7 +1544,7 @@ proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
|
|||||||
r.res = s.loc.snippet
|
r.res = s.loc.snippet
|
||||||
r.address = ""
|
r.address = ""
|
||||||
r.typ = etyNone
|
r.typ = etyNone
|
||||||
of skVar, skLet, skResult, skTemp, skForVar:
|
of skVar, skLet, skResult:
|
||||||
r.kind = resExpr
|
r.kind = resExpr
|
||||||
let jsType = mapType(p):
|
let jsType = mapType(p):
|
||||||
if typ.isNil:
|
if typ.isNil:
|
||||||
|
|||||||
@@ -592,12 +592,10 @@ proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode =
|
|||||||
result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
|
result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
|
||||||
result.add lenCall
|
result.add lenCall
|
||||||
|
|
||||||
proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode; noinit = false): PNode =
|
proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
|
||||||
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
|
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
|
||||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||||
let name = if noinit: "setLenUninit" else: "setLen"
|
var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
|
||||||
let magic = if noinit: mSetLengthSeqUninit else: mSetLengthSeq
|
|
||||||
var op = getSysMagic(c.g, x.info, name, magic)
|
|
||||||
op = instantiateGeneric(c, op, t, t)
|
op = instantiateGeneric(c, op, t, t)
|
||||||
result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
|
result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
|
||||||
|
|
||||||
@@ -645,9 +643,8 @@ proc genBulkCopySeq(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
|||||||
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||||
case c.kind
|
case c.kind
|
||||||
of attachedDup:
|
of attachedDup:
|
||||||
let bulkCopy = supportsCopyMem(t.elementType)
|
body.add setLenSeqCall(c, t, x, y)
|
||||||
body.add setLenSeqCall(c, t, x, y, noinit = bulkCopy)
|
if supportsCopyMem(t.elementType):
|
||||||
if bulkCopy:
|
|
||||||
genBulkCopySeq(c, t, body, x, y)
|
genBulkCopySeq(c, t, body, x, y)
|
||||||
else:
|
else:
|
||||||
forallElements(c, t, body, x, y)
|
forallElements(c, t, body, x, y)
|
||||||
@@ -661,9 +658,8 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
|||||||
# This is usually more efficient than a destroy/create pair.
|
# This is usually more efficient than a destroy/create pair.
|
||||||
# For trivially copyable types, use bulk copyMem instead of element loop.
|
# For trivially copyable types, use bulk copyMem instead of element loop.
|
||||||
checkSelfAssignment(c, t, body, x, y)
|
checkSelfAssignment(c, t, body, x, y)
|
||||||
let bulkCopy = supportsCopyMem(t.elementType)
|
body.add setLenSeqCall(c, t, x, y)
|
||||||
body.add setLenSeqCall(c, t, x, y, noinit = bulkCopy)
|
if supportsCopyMem(t.elementType):
|
||||||
if bulkCopy:
|
|
||||||
genBulkCopySeq(c, t, body, x, y)
|
genBulkCopySeq(c, t, body, x, y)
|
||||||
else:
|
else:
|
||||||
forallElements(c, t, body, x, y)
|
forallElements(c, t, body, x, y)
|
||||||
@@ -732,7 +728,7 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
|||||||
of attachedAsgn, attachedDeepCopy, attachedDup:
|
of attachedAsgn, attachedDeepCopy, attachedDup:
|
||||||
body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y)
|
body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y)
|
||||||
of attachedSink:
|
of attachedSink:
|
||||||
if c.g.config.usesSso():
|
if c.g.config.isDefined("nimsso"):
|
||||||
# SmallString: destroy old dst, then bit-copy src (no rc increment — this is a move).
|
# SmallString: destroy old dst, then bit-copy src (no rc increment — this is a move).
|
||||||
# No .p aliasing check needed; rc-based destroy handles COW sharing correctly.
|
# No .p aliasing check needed; rc-based destroy handles COW sharing correctly.
|
||||||
doAssert t.destructor != nil
|
doAssert t.destructor != nil
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ define:nimPreviewNonVarDestructor
|
|||||||
define:nimPreviewCheckedClose
|
define:nimPreviewCheckedClose
|
||||||
define:nimPreviewAsmSemSymbol
|
define:nimPreviewAsmSemSymbol
|
||||||
define:nimPreviewCStringComparisons
|
define:nimPreviewCStringComparisons
|
||||||
#define:nimPreviewDuplicateModuleError
|
define:nimPreviewDuplicateModuleError
|
||||||
# Incompatible with Nimony's compat2.nim for now
|
|
||||||
|
|
||||||
threads:off
|
threads:off
|
||||||
|
|
||||||
|
|||||||
@@ -121,11 +121,6 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
|
|||||||
conf.cmd in {cmdGendepend, cmdNifC, cmdIc, cmdM}:
|
conf.cmd in {cmdGendepend, cmdNifC, cmdIc, cmdM}:
|
||||||
initOrcDefines(conf)
|
initOrcDefines(conf)
|
||||||
|
|
||||||
if conf.selectedStrings == stringSso and
|
|
||||||
conf.selectedGC notin {gcArc, gcOrc, gcYrc, gcAtomicArc}:
|
|
||||||
rawMessage(conf, errGenerated,
|
|
||||||
"--strings:sso requires --mm:arc, --mm:orc, --mm:yrc, or --mm:atomicArc")
|
|
||||||
|
|
||||||
mainCommand(graph)
|
mainCommand(graph)
|
||||||
if conf.hasHint(hintGCStats): echo(GC_getStatistics())
|
if conf.hasHint(hintGCStats): echo(GC_getStatistics())
|
||||||
#echo(GC_getStatistics())
|
#echo(GC_getStatistics())
|
||||||
|
|||||||
@@ -267,10 +267,6 @@ type
|
|||||||
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc,
|
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc,
|
||||||
ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl, ccHipcc, ccNvcc
|
ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl, ccHipcc, ccNvcc
|
||||||
|
|
||||||
StringsMode* = enum
|
|
||||||
stringDefault = "default"
|
|
||||||
stringSso = "sso"
|
|
||||||
|
|
||||||
ExceptionSystem* = enum
|
ExceptionSystem* = enum
|
||||||
excNone, # no exception system selected yet
|
excNone, # no exception system selected yet
|
||||||
excSetjmp, # setjmp based exception handling
|
excSetjmp, # setjmp based exception handling
|
||||||
@@ -370,7 +366,6 @@ type
|
|||||||
implicitCmd*: bool # whether some flag triggered an implicit `command`
|
implicitCmd*: bool # whether some flag triggered an implicit `command`
|
||||||
selectedGC*: TGCMode # the selected GC (+)
|
selectedGC*: TGCMode # the selected GC (+)
|
||||||
exc*: ExceptionSystem
|
exc*: ExceptionSystem
|
||||||
selectedStrings*: StringsMode
|
|
||||||
hintProcessingDots*: bool # true for dots, false for filenames
|
hintProcessingDots*: bool # true for dots, false for filenames
|
||||||
verbosity*: int # how verbose the compiler is
|
verbosity*: int # how verbose the compiler is
|
||||||
numberOfProcessors*: int # number of processors
|
numberOfProcessors*: int # number of processors
|
||||||
@@ -703,7 +698,6 @@ template quitOrRaise*(conf: ConfigRef, msg = "") =
|
|||||||
|
|
||||||
proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in cmdDocLike + {cmdIdeTools}
|
proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in cmdDocLike + {cmdIdeTools}
|
||||||
proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
|
proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
|
||||||
proc usesSso*(conf: ConfigRef): bool {.inline.} = conf.selectedStrings == stringSso
|
|
||||||
|
|
||||||
template compilationCachePresent*(conf: ConfigRef): untyped =
|
template compilationCachePresent*(conf: ConfigRef): untyped =
|
||||||
false
|
false
|
||||||
|
|||||||
@@ -582,7 +582,6 @@ proc put(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
|
|||||||
inc(g.lineLen, s.len)
|
inc(g.lineLen, s.len)
|
||||||
|
|
||||||
proc putComment(g: var TSrcGen, s: string) =
|
proc putComment(g: var TSrcGen, s: string) =
|
||||||
const SpecialWhitespace = {' ', '\t', '\r', '\n', '\0'}
|
|
||||||
if s.len == 0: return
|
if s.len == 0: return
|
||||||
var i = 0
|
var i = 0
|
||||||
let hi = s.len - 1
|
let hi = s.len - 1
|
||||||
@@ -612,12 +611,12 @@ proc putComment(g: var TSrcGen, s: string) =
|
|||||||
# gets too long:
|
# gets too long:
|
||||||
# compute length of the following word:
|
# compute length of the following word:
|
||||||
var j = i
|
var j = i
|
||||||
while j <= hi and s[j] notin SpecialWhitespace: inc(j)
|
while j <= hi and s[j] > ' ': inc(j)
|
||||||
if not isCode and (g.col + (j - i) > MaxLineLen):
|
if not isCode and (g.col + (j - i) > MaxLineLen):
|
||||||
put(g, tkComment, com)
|
put(g, tkComment, com)
|
||||||
optNL(g, ind)
|
optNL(g, ind)
|
||||||
com = "## "
|
com = "## "
|
||||||
while i <= hi and s[i] notin SpecialWhitespace:
|
while i <= hi and s[i] > ' ':
|
||||||
com.add(s[i])
|
com.add(s[i])
|
||||||
inc(i)
|
inc(i)
|
||||||
put(g, tkComment, com)
|
put(g, tkComment, com)
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
|||||||
var sym = syms[0].s
|
var sym = syms[0].s
|
||||||
let name = sym.name
|
let name = sym.name
|
||||||
var scope = syms[0].scope
|
var scope = syms[0].scope
|
||||||
c.openShadowScope
|
|
||||||
if allowTypeBoundOps:
|
if allowTypeBoundOps:
|
||||||
for a in 1 ..< n.len:
|
for a in 1 ..< n.len:
|
||||||
# for every already typed argument, add type bound ops
|
# for every already typed argument, add type bound ops
|
||||||
@@ -218,10 +218,6 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
|||||||
scope = syms[nextSymIndex].scope
|
scope = syms[nextSymIndex].scope
|
||||||
inc(nextSymIndex)
|
inc(nextSymIndex)
|
||||||
|
|
||||||
if best.state == csMatch and best.calleeSym != nil and best.calleeSym.kind in {skTemplate, skMacro}:
|
|
||||||
c.closeShadowScope
|
|
||||||
else:
|
|
||||||
c.mergeShadowScope
|
|
||||||
|
|
||||||
proc effectProblem(f, a: PType; result: var string; c: PContext) =
|
proc effectProblem(f, a: PType; result: var string; c: PContext) =
|
||||||
if f.kind == tyProc and a.kind == tyProc:
|
if f.kind == tyProc and a.kind == tyProc:
|
||||||
|
|||||||
@@ -180,12 +180,9 @@ type
|
|||||||
sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index
|
sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index
|
||||||
inUncheckedAssignSection*: int
|
inUncheckedAssignSection*: int
|
||||||
importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
|
importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
|
||||||
forwardTypeUpdates*: seq[(PSym, PType, PNode)]
|
forwardTypeUpdates*: seq[(PType, PNode)]
|
||||||
# top-level owner, type, and type node for delayed retries inside a
|
# types that need to be updated in a type section
|
||||||
# type section due to containing forward types
|
# due to containing forward types, and their corresponding nodes
|
||||||
forwardFieldUpdates*: seq[(PType, PNode, PType)]
|
|
||||||
# object/tuple field definitions whose default values mention forward
|
|
||||||
# types and need delayed const checking
|
|
||||||
inTypeofContext*: int
|
inTypeofContext*: int
|
||||||
|
|
||||||
semAsgnOpr*: proc (c: PContext; n: PNode; k: TNodeKind): PNode {.nimcall.}
|
semAsgnOpr*: proc (c: PContext; n: PNode; k: TNodeKind): PNode {.nimcall.}
|
||||||
@@ -778,17 +775,9 @@ proc replaceHookMagic*(c: PContext, n: PNode, kind: TTypeAttachedOp): PNode =
|
|||||||
result[0] = newSymNode(op)
|
result[0] = newSymNode(op)
|
||||||
analyseIfAddressTakenInCall(c, result, false)
|
analyseIfAddressTakenInCall(c, result, false)
|
||||||
of attachedSink:
|
of attachedSink:
|
||||||
result = n
|
result = c.semAsgnOpr(c, n, nkSinkAsgn)
|
||||||
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
|
|
||||||
let op = getAttachedOp(c.graph, t, kind)
|
|
||||||
if op != nil:
|
|
||||||
result[0] = newSymNode(op)
|
|
||||||
of attachedAsgn:
|
of attachedAsgn:
|
||||||
result = n
|
result = c.semAsgnOpr(c, n, nkAsgn)
|
||||||
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
|
|
||||||
let op = getAttachedOp(c.graph, t, kind)
|
|
||||||
if op != nil:
|
|
||||||
result[0] = newSymNode(op)
|
|
||||||
of attachedDeepCopy:
|
of attachedDeepCopy:
|
||||||
result = n
|
result = n
|
||||||
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
|
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
|
||||||
|
|||||||
@@ -963,15 +963,12 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
|||||||
# echo "SUCCESS evaluated at compile time: ", call.renderTree
|
# echo "SUCCESS evaluated at compile time: ", call.renderTree
|
||||||
|
|
||||||
proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
||||||
let oldErrorCount = c.config.errorCounter
|
|
||||||
inc c.inStaticContext
|
inc c.inStaticContext
|
||||||
openScope(c)
|
openScope(c)
|
||||||
let a = semExprWithType(c, n, expectedType = expectedType)
|
let a = semExprWithType(c, n, expectedType = expectedType)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
dec c.inStaticContext
|
dec c.inStaticContext
|
||||||
if a.findUnresolvedStatic != nil or
|
if a.findUnresolvedStatic != nil: return a
|
||||||
c.config.errorCounter != oldErrorCount:
|
|
||||||
return a
|
|
||||||
result = evalStaticExpr(c.module, c.idgen, c.graph, a, c.p.owner)
|
result = evalStaticExpr(c.module, c.idgen, c.graph, a, c.p.owner)
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n))
|
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n))
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
|
|||||||
t.incl tfNonConstExpr
|
t.incl tfNonConstExpr
|
||||||
else:
|
else:
|
||||||
t = base
|
t = base
|
||||||
result.typ = makeTypeDesc(c, decayTypeOfView(c, t))
|
result.typ = makeTypeDesc(c, t)
|
||||||
|
|
||||||
type
|
type
|
||||||
SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn
|
SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn
|
||||||
@@ -615,9 +615,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
|||||||
of mAsgn:
|
of mAsgn:
|
||||||
case n[0].sym.name.s
|
case n[0].sym.name.s
|
||||||
of "=", "=copy":
|
of "=", "=copy":
|
||||||
result = replaceHookMagic(c, n, attachedAsgn)
|
result = semAsgnOpr(c, n, nkAsgn)
|
||||||
of "=sink":
|
of "=sink":
|
||||||
result = replaceHookMagic(c, n, attachedSink)
|
result = semAsgnOpr(c, n, nkSinkAsgn)
|
||||||
else:
|
else:
|
||||||
result = semShallowCopy(c, n, flags)
|
result = semShallowCopy(c, n, flags)
|
||||||
of mIsPartOf: result = semIsPartOf(c, n, flags)
|
of mIsPartOf: result = semIsPartOf(c, n, flags)
|
||||||
|
|||||||
@@ -815,14 +815,6 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy
|
|||||||
lastDef[^1] = val
|
lastDef[^1] = val
|
||||||
result.add(lastDef)
|
result.add(lastDef)
|
||||||
|
|
||||||
proc materializeDirectView(n: PNode): PNode =
|
|
||||||
let t = n.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned})
|
|
||||||
if t.kind in {tyVar, tyLent}:
|
|
||||||
result = newNodeIT(nkHiddenDeref, n.info, t.elementType)
|
|
||||||
result.add n
|
|
||||||
else:
|
|
||||||
result = n
|
|
||||||
|
|
||||||
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
var b: PNode
|
var b: PNode
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
@@ -889,10 +881,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
|||||||
#changeType(def.skipConv, typ, check=true)
|
#changeType(def.skipConv, typ, check=true)
|
||||||
else:
|
else:
|
||||||
typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen)
|
typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen)
|
||||||
let directTyp = typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned})
|
|
||||||
if directTyp.kind in {tyVar, tyLent}:
|
|
||||||
def = materializeDirectView(def)
|
|
||||||
typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen)
|
|
||||||
if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass:
|
if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass:
|
||||||
typ = typ.last
|
typ = typ.last
|
||||||
if hasEmpty(typ):
|
if hasEmpty(typ):
|
||||||
@@ -920,8 +908,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
|||||||
|
|
||||||
if c.matchedConcept != nil:
|
if c.matchedConcept != nil:
|
||||||
typFlags.incl taConcept
|
typFlags.incl taConcept
|
||||||
if a.kind != nkVarTuple:
|
typeAllowedCheck(c, a.info, typ, symkind, typFlags)
|
||||||
typeAllowedCheck(c, a.info, typ, symkind, typFlags)
|
|
||||||
|
|
||||||
var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
|
var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
|
||||||
if a.kind == nkVarTuple:
|
if a.kind == nkVarTuple:
|
||||||
@@ -1821,35 +1808,15 @@ proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
|
|||||||
internalAssert c.config, false
|
internalAssert c.config, false
|
||||||
|
|
||||||
proc typeSectionFinalPass(c: PContext, n: PNode) =
|
proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||||
# each top level type needs to be processed, each epoch should reify at least one
|
for (typ, typeNode) in c.forwardTypeUpdates:
|
||||||
var remainingOwners = initIntSet()
|
# types that need to be updated due to containing forward types
|
||||||
for (owner, _, _) in c.forwardTypeUpdates:
|
# and their corresponding type nodes
|
||||||
remainingOwners.incl owner.id
|
# for example generic invocations of forward types end up here
|
||||||
|
var reified = semTypeNode(c, typeNode, nil)
|
||||||
while c.forwardTypeUpdates.len > 0:
|
assert reified != nil
|
||||||
let pending = move c.forwardTypeUpdates
|
assignType(typ, reified)
|
||||||
var madeProgress = false
|
typ.itemId = reified.itemId # same id
|
||||||
|
c.forwardTypeUpdates = @[]
|
||||||
for (owner, typ, typeNode) in pending:
|
|
||||||
# types that need to be updated due to containing forward types
|
|
||||||
# and their corresponding type nodes
|
|
||||||
# for example generic invocations of forward types end up here
|
|
||||||
var reified = semTypeNode(c, typeNode, nil)
|
|
||||||
assert reified != nil
|
|
||||||
assignType(typ, reified)
|
|
||||||
typ.itemId = reified.itemId # same id
|
|
||||||
if containsForwardType(typ):
|
|
||||||
c.forwardTypeUpdates.add (owner, typ, typeNode)
|
|
||||||
elif not remainingOwners.missingOrExcl(owner.id):
|
|
||||||
madeProgress = true
|
|
||||||
|
|
||||||
if not madeProgress:
|
|
||||||
# can't error here unfortunately
|
|
||||||
break
|
|
||||||
|
|
||||||
for (owner, field, expectedType) in c.forwardFieldUpdates:
|
|
||||||
semDelayedFieldDefault(c, owner, expectedType, field)
|
|
||||||
c.forwardFieldUpdates = @[]
|
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
var a = n[i]
|
var a = n[i]
|
||||||
if a.kind == nkCommentStmt: continue
|
if a.kind == nkCommentStmt: continue
|
||||||
@@ -2949,15 +2916,13 @@ proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
|
|||||||
proc semStaticStmt(c: PContext, n: PNode): PNode =
|
proc semStaticStmt(c: PContext, n: PNode): PNode =
|
||||||
#echo "semStaticStmt"
|
#echo "semStaticStmt"
|
||||||
#writeStackTrace()
|
#writeStackTrace()
|
||||||
let oldErrorCount = c.config.errorCounter
|
|
||||||
inc c.inStaticContext
|
inc c.inStaticContext
|
||||||
openScope(c)
|
openScope(c)
|
||||||
let a = semStmt(c, n[0], {})
|
let a = semStmt(c, n[0], {})
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
dec c.inStaticContext
|
dec c.inStaticContext
|
||||||
n[0] = a
|
n[0] = a
|
||||||
if c.config.errorCounter == oldErrorCount:
|
evalStaticStmt(c.module, c.idgen, c.graph, a, c.p.owner)
|
||||||
evalStaticStmt(c.module, c.idgen, c.graph, a, c.p.owner)
|
|
||||||
when false:
|
when false:
|
||||||
# for incremental replays, keep the AST as required for replays:
|
# for incremental replays, keep the AST as required for replays:
|
||||||
result = n
|
result = n
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
|
|||||||
if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base)
|
if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base)
|
||||||
if base.kind notin {tyGenericParam, tyGenericInvocation}:
|
if base.kind notin {tyGenericParam, tyGenericInvocation}:
|
||||||
if base.kind == tyForward:
|
if base.kind == tyForward:
|
||||||
c.forwardTypeUpdates.add (getCurrOwner(c), result, n)
|
c.forwardTypeUpdates.add (base, n[1])
|
||||||
elif not isOrdinalType(base, allowEnumWithHoles = true):
|
elif not isOrdinalType(base, allowEnumWithHoles = true):
|
||||||
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(base, preferDesc))
|
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(base, preferDesc))
|
||||||
elif lengthOrd(c.config, base) > MaxSetElements:
|
elif lengthOrd(c.config, base) > MaxSetElements:
|
||||||
@@ -318,61 +318,6 @@ proc fitDefaultNode(c: PContext, n: var PNode, expectedType: PType) =
|
|||||||
typeAllowedCheck(c, n.info, n.typ, skConst, {taProcContextIsNotMacro, taIsDefaultField})
|
typeAllowedCheck(c, n.info, n.typ, skConst, {taProcContextIsNotMacro, taIsDefaultField})
|
||||||
dec c.inStaticContext
|
dec c.inStaticContext
|
||||||
|
|
||||||
proc containsForwardTypeAux(t: PType; seen: var IntSet): bool
|
|
||||||
|
|
||||||
proc containsForwardTypeAux(n: PNode; seen: var IntSet): bool =
|
|
||||||
result = false
|
|
||||||
if n.isNil or n.kind in nkLiterals + {nkNilLit, nkEmpty, nkType}:
|
|
||||||
return
|
|
||||||
if containsForwardTypeAux(n.typ, seen) or
|
|
||||||
(n.kind == nkSym and n.sym.typ != n.typ and containsForwardTypeAux(n.sym.typ, seen)):
|
|
||||||
return true
|
|
||||||
|
|
||||||
for i in 0 ..< n.safeLen:
|
|
||||||
if containsForwardTypeAux(n[i], seen):
|
|
||||||
return true
|
|
||||||
|
|
||||||
proc containsForwardTypeAux(t: PType; seen: var IntSet): bool =
|
|
||||||
result = false
|
|
||||||
if t.isNil:
|
|
||||||
return
|
|
||||||
if t.kind == tyForward:
|
|
||||||
return true
|
|
||||||
|
|
||||||
if not containsOrIncl(seen, t.id):
|
|
||||||
if containsForwardTypeAux(t.n, seen):
|
|
||||||
return true
|
|
||||||
|
|
||||||
for i in 0 ..< t.len:
|
|
||||||
if containsForwardTypeAux(t[i], seen):
|
|
||||||
return true
|
|
||||||
|
|
||||||
proc containsForwardType(arg: PNode): bool =
|
|
||||||
var seen = initIntSet()
|
|
||||||
containsForwardTypeAux(arg, seen)
|
|
||||||
|
|
||||||
proc containsForwardType(t: PType): bool =
|
|
||||||
var seen = initIntSet()
|
|
||||||
containsForwardTypeAux(t, seen)
|
|
||||||
|
|
||||||
proc semFieldDefault(c: PContext; owner, expectedType: PType; field: PNode): PType =
|
|
||||||
result = expectedType
|
|
||||||
field[^1] = semExprWithType(c, field[^1], {efDetermineType, efAllowSymChoice}, result)
|
|
||||||
if result == nil:
|
|
||||||
result = field[^1].typ
|
|
||||||
|
|
||||||
if c.inGenericContext == 0:
|
|
||||||
if containsForwardType(field[^1]):
|
|
||||||
c.forwardFieldUpdates.add (owner, field, result)
|
|
||||||
else:
|
|
||||||
fitDefaultNode(c, field[^1], result)
|
|
||||||
result = field[^1].typ.skipIntLit(c.idgen)
|
|
||||||
propagateToOwner(owner, result)
|
|
||||||
|
|
||||||
proc semDelayedFieldDefault(c: PContext; owner, expectedType: PType; field: PNode) =
|
|
||||||
fitDefaultNode(c, field[^1], expectedType)
|
|
||||||
propagateToOwner(owner, field[^1].typ.skipIntLit(c.idgen))
|
|
||||||
|
|
||||||
proc isRecursiveType*(t: PType): bool =
|
proc isRecursiveType*(t: PType): bool =
|
||||||
# handle simple recusive types before typeFinalPass
|
# handle simple recusive types before typeFinalPass
|
||||||
var cycleDetector = initIntSet()
|
var cycleDetector = initIntSet()
|
||||||
@@ -605,7 +550,13 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
|
|||||||
var hasDefaultField = a[^1].kind != nkEmpty
|
var hasDefaultField = a[^1].kind != nkEmpty
|
||||||
if hasDefaultField:
|
if hasDefaultField:
|
||||||
typ = if a[^2].kind != nkEmpty: semTypeNode(c, a[^2], nil) else: nil
|
typ = if a[^2].kind != nkEmpty: semTypeNode(c, a[^2], nil) else: nil
|
||||||
typ = semFieldDefault(c, result, typ, a)
|
if c.inGenericContext > 0:
|
||||||
|
a[^1] = semExprWithType(c, a[^1], {efDetermineType, efAllowSymChoice}, typ)
|
||||||
|
if typ == nil:
|
||||||
|
typ = a[^1].typ
|
||||||
|
else:
|
||||||
|
fitDefaultNode(c, a[^1], typ)
|
||||||
|
typ = a[^1].typ.skipIntLit(c.idgen)
|
||||||
elif a[^2].kind != nkEmpty:
|
elif a[^2].kind != nkEmpty:
|
||||||
typ = semTypeNode(c, a[^2], nil)
|
typ = semTypeNode(c, a[^2], nil)
|
||||||
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
|
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
|
||||||
@@ -971,7 +922,14 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
|||||||
var hasDefaultField = n[^1].kind != nkEmpty
|
var hasDefaultField = n[^1].kind != nkEmpty
|
||||||
if hasDefaultField:
|
if hasDefaultField:
|
||||||
typ = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
|
typ = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
|
||||||
typ = semFieldDefault(c, rectype, typ, n)
|
if c.inGenericContext > 0:
|
||||||
|
n[^1] = semExprWithType(c, n[^1], {efDetermineType, efAllowSymChoice}, typ)
|
||||||
|
if typ == nil:
|
||||||
|
typ = n[^1].typ
|
||||||
|
else:
|
||||||
|
fitDefaultNode(c, n[^1], typ)
|
||||||
|
typ = n[^1].typ.skipIntLit(c.idgen)
|
||||||
|
propagateToOwner(rectype, typ)
|
||||||
elif n[^2].kind == nkEmpty:
|
elif n[^2].kind == nkEmpty:
|
||||||
localError(c.config, n.info, errTypeExpected)
|
localError(c.config, n.info, errTypeExpected)
|
||||||
typ = errorType(c)
|
typ = errorType(c)
|
||||||
@@ -1114,7 +1072,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
|
|||||||
if needsForwardUpdate:
|
if needsForwardUpdate:
|
||||||
# if the inherited object is a forward type,
|
# if the inherited object is a forward type,
|
||||||
# the entire object needs to be checked again
|
# the entire object needs to be checked again
|
||||||
c.forwardTypeUpdates.add (getCurrOwner(c), result, n) # we retry in the final pass
|
c.forwardTypeUpdates.add (result, n) # we retry in the final pass
|
||||||
rawAddSon(result, realBase)
|
rawAddSon(result, realBase)
|
||||||
if realBase == nil and tfInheritable in flags:
|
if realBase == nil and tfInheritable in flags:
|
||||||
result.incl tfInheritable
|
result.incl tfInheritable
|
||||||
@@ -1762,7 +1720,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
|||||||
for i in 1..<n.len:
|
for i in 1..<n.len:
|
||||||
var elem = semGenericParamInInvocation(c, n[i])
|
var elem = semGenericParamInInvocation(c, n[i])
|
||||||
addToResult(elem, true)
|
addToResult(elem, true)
|
||||||
c.forwardTypeUpdates.add (getCurrOwner(c), result, n)
|
c.forwardTypeUpdates.add (result, n)
|
||||||
return
|
return
|
||||||
elif t.kind != tyGenericBody:
|
elif t.kind != tyGenericBody:
|
||||||
# we likely got code of the form TypeA[TypeB] where TypeA is
|
# we likely got code of the form TypeA[TypeB] where TypeA is
|
||||||
@@ -1815,14 +1773,10 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
|||||||
localError(c.config, n.info, errCannotInstantiateX % s.name.s)
|
localError(c.config, n.info, errCannotInstantiateX % s.name.s)
|
||||||
result = newOrPrevType(tyError, prev, c)
|
result = newOrPrevType(tyError, prev, c)
|
||||||
elif containsGenericInvocationWithForward(n[0]) or hasForwardTypeParam:
|
elif containsGenericInvocationWithForward(n[0]) or hasForwardTypeParam:
|
||||||
# isConcrete == false means this generic type is not instanciated here because
|
# isConcrete == false means this generic type is not instanciated here because it invoked with generic parameters.
|
||||||
# it invoked with generic parameters.
|
# Even if isConcrete == true, don't instanciate it now if there are any `tyForward` type params.
|
||||||
# Even if isConcrete == true, don't instanciate it now if there are
|
# Such `tyForward` type params will be semchecked later and we can instanciate this next time.
|
||||||
# unresolved `tyForward` type params.
|
# Some generic types like std/options.Option[T] needs a type kinds of the given type argument.
|
||||||
# Such `tyForward` type params will be semchecked later and we can
|
|
||||||
# instanciate this next time.
|
|
||||||
# Some generic types like std/options.Option[T] need the kind of the
|
|
||||||
# given type argument before their fields can be resolved.
|
|
||||||
|
|
||||||
# return `tyForward` instead of `tyGenericInvocation` because:
|
# return `tyForward` instead of `tyGenericInvocation` because:
|
||||||
# ```nim
|
# ```nim
|
||||||
@@ -1838,7 +1792,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
|||||||
else:
|
else:
|
||||||
assignType(result, newTypeS(tyForward, c))
|
assignType(result, newTypeS(tyForward, c))
|
||||||
result.sym = s
|
result.sym = s
|
||||||
c.forwardTypeUpdates.add (getCurrOwner(c), result, n) #fixes 1500
|
c.forwardTypeUpdates.add (result, n) #fixes 1500
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
result = instGenericContainer(c, n.info, result,
|
result = instGenericContainer(c, n.info, result,
|
||||||
@@ -1879,38 +1833,6 @@ proc fixupTypeOf(c: PContext, prev: PType, typ: PType) =
|
|||||||
if prev.kind != tyGenericBody:
|
if prev.kind != tyGenericBody:
|
||||||
assignType(prev, result)
|
assignType(prev, result)
|
||||||
|
|
||||||
proc decayTypeOfView(c: PContext, typ: PType): PType =
|
|
||||||
if typ == nil: return nil
|
|
||||||
let t = typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
|
||||||
case t.kind
|
|
||||||
of tyVar, tyLent:
|
|
||||||
result = decayTypeOfView(c, t.elementType)
|
|
||||||
of tyTuple:
|
|
||||||
var changed = false
|
|
||||||
var kids = newSeq[PType](t.len)
|
|
||||||
for i in 0..<t.len:
|
|
||||||
kids[i] = decayTypeOfView(c, t[i])
|
|
||||||
if kids[i] != t[i]: changed = true
|
|
||||||
if changed:
|
|
||||||
result = copyType(t, c.idgen, t.owner)
|
|
||||||
for i in 0..<kids.len:
|
|
||||||
result[i] = kids[i]
|
|
||||||
if t.n != nil:
|
|
||||||
result.n = copyNode(t.n)
|
|
||||||
for it in t.n:
|
|
||||||
if it.kind == nkSym and it.sym.kind == skField:
|
|
||||||
let field = copySym(it.sym, c.idgen)
|
|
||||||
field.ast = it.sym.ast
|
|
||||||
if field.position >= 0 and field.position < kids.len:
|
|
||||||
field.typ = kids[field.position]
|
|
||||||
result.n.add newSymNode(field, it.info)
|
|
||||||
else:
|
|
||||||
result.n.add copyTree(it)
|
|
||||||
else:
|
|
||||||
result = typ
|
|
||||||
else:
|
|
||||||
result = typ
|
|
||||||
|
|
||||||
proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
|
proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
|
||||||
var n = semExprWithType(c, n, {efDetermineType})
|
var n = semExprWithType(c, n, {efDetermineType})
|
||||||
if n.typ.kind == tyTypeDesc:
|
if n.typ.kind == tyTypeDesc:
|
||||||
@@ -2110,7 +2032,6 @@ proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
|
|||||||
result.incl tfNonConstExpr
|
result.incl tfNonConstExpr
|
||||||
else:
|
else:
|
||||||
result = base
|
result = base
|
||||||
result = decayTypeOfView(c, result)
|
|
||||||
fixupTypeOf(c, prev, result)
|
fixupTypeOf(c, prev, result)
|
||||||
|
|
||||||
proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
|
proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
|
||||||
@@ -2136,7 +2057,6 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
|
|||||||
result.incl tfNonConstExpr
|
result.incl tfNonConstExpr
|
||||||
else:
|
else:
|
||||||
result = base
|
result = base
|
||||||
result = decayTypeOfView(c, result)
|
|
||||||
fixupTypeOf(c, prev, result)
|
fixupTypeOf(c, prev, result)
|
||||||
|
|
||||||
proc semTypeIdent(c: PContext, n: PNode): PSym =
|
proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||||
@@ -2414,7 +2334,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
|||||||
else:
|
else:
|
||||||
result = typeExpr.typ.base
|
result = typeExpr.typ.base
|
||||||
if result.isMetaType and
|
if result.isMetaType and
|
||||||
result.kind notin tyTypeClasses:
|
result.kind != tyUserTypeClass:
|
||||||
# the dot expression may refer to a concept type in
|
# the dot expression may refer to a concept type in
|
||||||
# a different module. allow a normal alias then.
|
# a different module. allow a normal alias then.
|
||||||
let preprocessed = semGenericStmt(c, n)
|
let preprocessed = semGenericStmt(c, n)
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ import
|
|||||||
magicsys, idents, lexer, options, parampatterns, trees,
|
magicsys, idents, lexer, options, parampatterns, trees,
|
||||||
linter, lineinfos, lowerings, modulegraphs, concepts, layeredtable
|
linter, lineinfos, lowerings, modulegraphs, concepts, layeredtable
|
||||||
|
|
||||||
import typeallowed
|
|
||||||
|
|
||||||
import std/[intsets, strutils, tables]
|
import std/[intsets, strutils, tables]
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
when defined(nimPreviewSlimSystem):
|
||||||
@@ -125,58 +123,6 @@ proc initCandidate*(ctx: PContext, callee: PType): TCandidate =
|
|||||||
result.calleeSym = nil
|
result.calleeSym = nil
|
||||||
result.bindings = initLayeredTypeMap()
|
result.bindings = initLayeredTypeMap()
|
||||||
|
|
||||||
proc materializeTupleViewType(t: PType; idgen: IdGenerator): PType =
|
|
||||||
case t.kind
|
|
||||||
of tyVar, tyLent:
|
|
||||||
result = materializeTupleViewType(t.elementType, idgen)
|
|
||||||
of tyTuple:
|
|
||||||
if classifyViewType(t) == noView:
|
|
||||||
result = t
|
|
||||||
else:
|
|
||||||
result = copyType(t, idgen, t.owner)
|
|
||||||
for i in 0..<t.len:
|
|
||||||
result[i] = materializeTupleViewType(t[i], idgen)
|
|
||||||
if t.n != nil:
|
|
||||||
result.n = copyNode(t.n)
|
|
||||||
for it in t.n:
|
|
||||||
if it.kind == nkSym and it.sym.kind == skField:
|
|
||||||
let field = copySym(it.sym, idgen)
|
|
||||||
field.ast = it.sym.ast
|
|
||||||
if field.position >= 0 and field.position < result.len:
|
|
||||||
field.typ = result[field.position]
|
|
||||||
result.n.add newSymNode(field, it.info)
|
|
||||||
else:
|
|
||||||
result.n.add copyTree(it)
|
|
||||||
else:
|
|
||||||
result = t
|
|
||||||
|
|
||||||
proc materializeTupleViewArg(c: PContext; targetType: PType; arg: PNode): PNode =
|
|
||||||
let targetTuple = targetType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct, tyInferred})
|
|
||||||
var tupleArg = arg
|
|
||||||
var prefix: PNode = nil
|
|
||||||
if targetTuple.len > 1 and arg.kind notin {nkHiddenAddr, nkSym}:
|
|
||||||
prefix = evalOnce(c.graph, arg, c.idgen, getCurrOwner(c))
|
|
||||||
tupleArg = prefix[^1]
|
|
||||||
|
|
||||||
let tupleConstr = newNodeIT(nkTupleConstr, arg.info, targetType)
|
|
||||||
for i in 0..<targetTuple.len:
|
|
||||||
let targetField = targetTuple[i]
|
|
||||||
var field = newTupleAccess(c.graph, tupleArg, i)
|
|
||||||
let sourceField = field.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct, tyInferred})
|
|
||||||
if sourceField.kind in {tyVar, tyLent}:
|
|
||||||
field = newDeref(field)
|
|
||||||
elif targetField.kind == tyTuple and classifyViewType(sourceField) != noView:
|
|
||||||
field = materializeTupleViewArg(c, targetField, field)
|
|
||||||
tupleConstr.add field
|
|
||||||
|
|
||||||
if prefix == nil:
|
|
||||||
result = tupleConstr
|
|
||||||
else:
|
|
||||||
result = newNodeIT(nkStmtListExpr, arg.info, targetType)
|
|
||||||
for i in 0..<(prefix.len - 1):
|
|
||||||
result.add prefix[i]
|
|
||||||
result.add tupleConstr
|
|
||||||
|
|
||||||
proc put(c: var TCandidate, key, val: PType) {.inline.} =
|
proc put(c: var TCandidate, key, val: PType) {.inline.} =
|
||||||
## Given: proc foo[T](x: T); foo(4)
|
## Given: proc foo[T](x: T); foo(4)
|
||||||
## key: 'T'
|
## key: 'T'
|
||||||
@@ -189,12 +135,7 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} =
|
|||||||
writeStackTrace()
|
writeStackTrace()
|
||||||
if c.c.module.name.s == "temp3":
|
if c.c.module.name.s == "temp3":
|
||||||
echo "binding ", key, " -> ", val
|
echo "binding ", key, " -> ", val
|
||||||
|
put(c.bindings, key, val.skipIntLit(c.c.idgen))
|
||||||
let normalized = val.skipIntLit(c.c.idgen)
|
|
||||||
if normalized.kind == tyTuple and classifyViewType(normalized) != noView:
|
|
||||||
put(c.bindings, key, materializeTupleViewType(normalized, c.c.idgen))
|
|
||||||
else:
|
|
||||||
put(c.bindings, key, normalized)
|
|
||||||
|
|
||||||
proc typeRel*(c: var TCandidate, f, aOrig: PType,
|
proc typeRel*(c: var TCandidate, f, aOrig: PType,
|
||||||
flags: TTypeRelFlags = {}): TTypeRelation
|
flags: TTypeRelFlags = {}): TTypeRelation
|
||||||
@@ -2259,16 +2200,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
|
|||||||
|
|
||||||
if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv")
|
if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv")
|
||||||
result.add c.graph.emptyNode
|
result.add c.graph.emptyNode
|
||||||
let targetTuple = result.typ.skipTypes({tyVar, tyGenericInst, tyAlias, tySink, tyDistinct, tyInferred})
|
if arg.typ != nil and arg.typ.kind == tyLent:
|
||||||
let sourceTuple =
|
|
||||||
if arg.typ != nil:
|
|
||||||
arg.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct, tyInferred})
|
|
||||||
else:
|
|
||||||
nil
|
|
||||||
if sourceTuple != nil and sourceTuple.kind == tyTuple and targetTuple.kind == tyTuple and
|
|
||||||
classifyViewType(arg.typ) != noView and classifyViewType(result.typ) == noView:
|
|
||||||
result.add materializeTupleViewArg(c, targetTuple, arg)
|
|
||||||
elif arg.typ != nil and arg.typ.kind == tyLent:
|
|
||||||
let a = newNodeIT(nkHiddenDeref, arg.info, arg.typ.elementType)
|
let a = newNodeIT(nkHiddenDeref, arg.info, arg.typ.elementType)
|
||||||
a.add arg
|
a.add arg
|
||||||
result.add a
|
result.add a
|
||||||
@@ -2902,11 +2834,9 @@ proc findFirstArgBlock(m: var TCandidate, n: PNode): int =
|
|||||||
else: break
|
else: break
|
||||||
|
|
||||||
proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) =
|
proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) =
|
||||||
|
|
||||||
template noMatch() =
|
template noMatch() =
|
||||||
if m.calleeSym != nil and m.calleeSym.kind notin {skTemplate, skMacro}:
|
c.mergeShadowScope #merge so that we don't have to resem for later overloads
|
||||||
c.mergeShadowScope
|
|
||||||
else:
|
|
||||||
c.closeShadowScope
|
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
m.firstMismatch.arg = a
|
m.firstMismatch.arg = a
|
||||||
m.firstMismatch.formal = formal
|
m.firstMismatch.formal = formal
|
||||||
|
|||||||
@@ -118,24 +118,6 @@ proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite
|
|||||||
le.flags.incl nfFirstWrite
|
le.flags.incl nfFirstWrite
|
||||||
result[1] = ri
|
result[1] = ri
|
||||||
|
|
||||||
proc resolveBorrowedRoutineSym(c: PTransf; s: PSym; info: TLineInfo): PSym =
|
|
||||||
# Follow borrow aliases to the underlying implementation symbol.
|
|
||||||
var s = s
|
|
||||||
while true:
|
|
||||||
# Skips over all borrowed procs getting the last proc symbol without an implementation
|
|
||||||
let body = getBody(c.graph, s)
|
|
||||||
if body.kind == nkSym and sfBorrow in body.sym.flags and getBody(c.graph, body.sym).kind == nkSym:
|
|
||||||
s = body.sym
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
let body = getBody(c.graph, s)
|
|
||||||
if body.kind == nkSym:
|
|
||||||
result = body.sym
|
|
||||||
else:
|
|
||||||
result = nil
|
|
||||||
internalError(c.graph.config, info, "wrong AST for borrowed symbol")
|
|
||||||
|
|
||||||
proc transformSymAux(c: PTransf, n: PNode): PNode =
|
proc transformSymAux(c: PTransf, n: PNode): PNode =
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
if s.typ != nil and s.typ.callConv == ccClosure:
|
if s.typ != nil and s.typ.callConv == ccClosure:
|
||||||
@@ -154,7 +136,17 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
|
|||||||
var tc = c.transCon
|
var tc = c.transCon
|
||||||
if sfBorrow in s.flags and s.kind in routineKinds:
|
if sfBorrow in s.flags and s.kind in routineKinds:
|
||||||
# simply exchange the symbol:
|
# simply exchange the symbol:
|
||||||
b = newSymNode(resolveBorrowedRoutineSym(c, s, n.info), n.info)
|
var s = s
|
||||||
|
while true:
|
||||||
|
# Skips over all borrowed procs getting the last proc symbol without an implementation
|
||||||
|
let body = getBody(c.graph, s)
|
||||||
|
if body.kind == nkSym and sfBorrow in body.sym.flags and getBody(c.graph, body.sym).kind == nkSym:
|
||||||
|
s = body.sym
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
b = getBody(c.graph, s)
|
||||||
|
if b.kind != nkSym: internalError(c.graph.config, n.info, "wrong AST for borrowed symbol")
|
||||||
|
b = newSymNode(b.sym, n.info)
|
||||||
elif c.inlining > 0:
|
elif c.inlining > 0:
|
||||||
# see bug #13596: we use ref-based equality in the DFA for destruction
|
# see bug #13596: we use ref-based equality in the DFA for destruction
|
||||||
# injections so we need to ensure unique nodes after iterator inlining
|
# injections so we need to ensure unique nodes after iterator inlining
|
||||||
@@ -336,7 +328,7 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PNode =
|
|||||||
if a.kind == nkSym:
|
if a.kind == nkSym:
|
||||||
n[1] = transformSymAux(c, a)
|
n[1] = transformSymAux(c, a)
|
||||||
return n
|
return n
|
||||||
of nkLambdaKinds, nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: # todo optimize nosideeffects?
|
of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: # todo optimize nosideeffects?
|
||||||
result = newTransNode(n)
|
result = newTransNode(n)
|
||||||
let x = newSymNode(copySym(n[namePos].sym, c.idgen))
|
let x = newSymNode(copySym(n[namePos].sym, c.idgen))
|
||||||
c.transCon.mapping[n[namePos].sym.itemId] = x
|
c.transCon.mapping[n[namePos].sym.itemId] = x
|
||||||
@@ -702,11 +694,6 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
|
|||||||
of nkAddr, nkHiddenAddr:
|
of nkAddr, nkHiddenAddr:
|
||||||
result = putArgInto(arg[0], formal)
|
result = putArgInto(arg[0], formal)
|
||||||
if result == paViaIndirection: result = paFastAsgn
|
if result == paViaIndirection: result = paFastAsgn
|
||||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
|
||||||
if compareTypes(arg.typ, arg[1].typ, dcEqIgnoreDistinct, {IgnoreRangeShallow}):
|
|
||||||
result = putArgInto(arg[1], formal)
|
|
||||||
else:
|
|
||||||
result = paFastAsgn
|
|
||||||
of nkCurly, nkBracket:
|
of nkCurly, nkBracket:
|
||||||
for i in 0..<arg.len:
|
for i in 0..<arg.len:
|
||||||
if putArgInto(arg[i], formal) != paDirectMapping:
|
if putArgInto(arg[i], formal) != paDirectMapping:
|
||||||
@@ -798,9 +785,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
|
|||||||
|
|
||||||
discard c.breakSyms.pop
|
discard c.breakSyms.pop
|
||||||
|
|
||||||
var iter = call[0].sym
|
let iter = call[0].sym
|
||||||
if sfBorrow in iter.flags and iter.kind in routineKinds:
|
|
||||||
iter = resolveBorrowedRoutineSym(c, iter, n.info)
|
|
||||||
|
|
||||||
var v = newNodeI(nkVarSection, n.info)
|
var v = newNodeI(nkVarSection, n.info)
|
||||||
for i in 0..<n.len - 2:
|
for i in 0..<n.len - 2:
|
||||||
|
|||||||
3
koch.nim
3
koch.nim
@@ -16,11 +16,10 @@ const
|
|||||||
ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1
|
ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1
|
||||||
SatStableCommit = "e63eaea8baf00bed8bcd5a29ffd8823abb265b39"
|
SatStableCommit = "e63eaea8baf00bed8bcd5a29ffd8823abb265b39"
|
||||||
|
|
||||||
NimonyStableCommit = "c189ef438598878b2f02f6a2ff91d08febafc04b" # unversioned \
|
NimonyStableCommit = "bbfb21529845567c55b67d176354daef0e7d6c29" # unversioned \
|
||||||
# Note that Nimony uses Nim as a git submodule but we don't want to install
|
# Note that Nimony uses Nim as a git submodule but we don't want to install
|
||||||
# Nimony's dependency to Nim as we are Nim. So a `git clone` without --recursive
|
# Nimony's dependency to Nim as we are Nim. So a `git clone` without --recursive
|
||||||
# is **required** here.
|
# is **required** here.
|
||||||
# Commit from 2026-04-27
|
|
||||||
|
|
||||||
# examples of possible values for fusion: #head, #ea82b54, 1.2.3
|
# examples of possible values for fusion: #head, #ea82b54, 1.2.3
|
||||||
FusionStableHash = "#562467452b32cb7a97410ea177f083e6d8405734"
|
FusionStableHash = "#562467452b32cb7a97410ea177f083e6d8405734"
|
||||||
|
|||||||
@@ -1559,8 +1559,6 @@ macro expandMacros*(body: typed): untyped =
|
|||||||
echo body.toStrLit
|
echo body.toStrLit
|
||||||
result = body
|
result = body
|
||||||
|
|
||||||
proc getTypeInstSkipAlias(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
|
|
||||||
|
|
||||||
proc extractTypeImpl(n: NimNode): NimNode =
|
proc extractTypeImpl(n: NimNode): NimNode =
|
||||||
## attempts to extract the type definition of the given symbol
|
## attempts to extract the type definition of the given symbol
|
||||||
case n.kind
|
case n.kind
|
||||||
@@ -1575,17 +1573,11 @@ proc extractTypeImpl(n: NimNode): NimNode =
|
|||||||
result = n[0].getImpl()
|
result = n[0].getImpl()
|
||||||
of nnkTypeDef:
|
of nnkTypeDef:
|
||||||
result = n[2]
|
result = n[2]
|
||||||
if result.kind notin {nnkSym, nnkObjectTy, nnkRefTy, nnkPtrTy, nnkBracketExpr}:
|
|
||||||
# Handle typeof() and similar unresolvable type expressions
|
|
||||||
let typSym = if n[0].kind == nnkPragmaExpr: n[0][0] else: n[0]
|
|
||||||
if typSym.kind == nnkSym:
|
|
||||||
let resolved = typSym.getTypeInstSkipAlias()
|
|
||||||
if resolved.kind == nnkSym:
|
|
||||||
return resolved.getImpl.extractTypeImpl()
|
|
||||||
error("Invalid node to retrieve type implementation of: " & $result.kind)
|
|
||||||
else: error("Invalid node to retrieve type implementation of: " & $n.kind)
|
else: error("Invalid node to retrieve type implementation of: " & $n.kind)
|
||||||
|
|
||||||
|
|
||||||
|
proc getTypeInstSkipAlias(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
|
||||||
|
|
||||||
proc customPragmaNode(n: NimNode): NimNode =
|
proc customPragmaNode(n: NimNode): NimNode =
|
||||||
result = nil
|
result = nil
|
||||||
expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkType, nnkCheckedFieldExpr})
|
expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkType, nnkCheckedFieldExpr})
|
||||||
@@ -1626,15 +1618,6 @@ proc customPragmaNode(n: NimNode): NimNode =
|
|||||||
var typDef = getImpl(typInst)
|
var typDef = getImpl(typInst)
|
||||||
while typDef != nil:
|
while typDef != nil:
|
||||||
typDef.expectKind(nnkTypeDef)
|
typDef.expectKind(nnkTypeDef)
|
||||||
# Resolve typeof() and similar unresolvable type expressions
|
|
||||||
if typDef[2].kind notin {nnkSym, nnkObjectTy, nnkRefTy, nnkPtrTy, nnkBracketExpr}:
|
|
||||||
let typSym = if typDef[0].kind == nnkPragmaExpr: typDef[0][0] else: typDef[0]
|
|
||||||
if typSym.kind == nnkSym:
|
|
||||||
let resolved = typSym.getTypeInstSkipAlias()
|
|
||||||
if resolved.kind == nnkSym:
|
|
||||||
typDef = getImpl(resolved)
|
|
||||||
continue
|
|
||||||
break
|
|
||||||
let typ = typDef[2].extractTypeImpl()
|
let typ = typDef[2].extractTypeImpl()
|
||||||
if typ.kind notin {nnkRefTy, nnkPtrTy, nnkObjectTy}: break
|
if typ.kind notin {nnkRefTy, nnkPtrTy, nnkObjectTy}: break
|
||||||
let isRef = typ.kind in {nnkRefTy, nnkPtrTy}
|
let isRef = typ.kind in {nnkRefTy, nnkPtrTy}
|
||||||
|
|||||||
@@ -9,11 +9,6 @@
|
|||||||
when defined(js):
|
when defined(js):
|
||||||
{.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".}
|
{.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".}
|
||||||
|
|
||||||
## .. warning:: NRE is deprecated.
|
|
||||||
## Use [Regex](https://github.com/nitely/nim-regex) or
|
|
||||||
## `NRE2 <nre2.html>`_ that wraps Regex so that you can easily replace NRE.
|
|
||||||
## PCRE library is now at end of life.
|
|
||||||
##
|
|
||||||
## What is NRE?
|
## What is NRE?
|
||||||
## ============
|
## ============
|
||||||
##
|
##
|
||||||
@@ -89,7 +84,7 @@ type
|
|||||||
Regex* = ref RegexDesc
|
Regex* = ref RegexDesc
|
||||||
## Represents the pattern that things are matched against, constructed with
|
## Represents the pattern that things are matched against, constructed with
|
||||||
## `re(string)`. Examples: `re"foo"`, `re(r"(*ANYCRLF)(?x)foo #
|
## `re(string)`. Examples: `re"foo"`, `re(r"(*ANYCRLF)(?x)foo #
|
||||||
## comment")`
|
## comment".`
|
||||||
##
|
##
|
||||||
## `pattern: string`
|
## `pattern: string`
|
||||||
## : the string that was used to create the pattern. For details on how
|
## : the string that was used to create the pattern. For details on how
|
||||||
@@ -159,7 +154,7 @@ type
|
|||||||
## will need to pass these as separate flags to PCRE.
|
## will need to pass these as separate flags to PCRE.
|
||||||
|
|
||||||
RegexMatch* = object
|
RegexMatch* = object
|
||||||
## Usually seen as `Option[RegexMatch]`, it represents the result of an
|
## Usually seen as Option[RegexMatch], it represents the result of an
|
||||||
## execution. On failure, it is none, on success, it is some.
|
## execution. On failure, it is none, on success, it is some.
|
||||||
##
|
##
|
||||||
## `pattern: Regex`
|
## `pattern: Regex`
|
||||||
|
|||||||
@@ -10,10 +10,6 @@
|
|||||||
when defined(js):
|
when defined(js):
|
||||||
{.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".}
|
{.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".}
|
||||||
|
|
||||||
## .. warning:: This module is deprecated.
|
|
||||||
## Use [Regex](https://github.com/nitely/nim-regex).
|
|
||||||
## PCRE library is now at end of life.
|
|
||||||
##
|
|
||||||
## Regular expression support for Nim.
|
## Regular expression support for Nim.
|
||||||
##
|
##
|
||||||
## This module is implemented by providing a wrapper around the
|
## This module is implemented by providing a wrapper around the
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ proc getContentLength*(): string =
|
|||||||
|
|
||||||
proc getContentType*(): string =
|
proc getContentType*(): string =
|
||||||
## Returns contents of the `CONTENT_TYPE` environment variable.
|
## Returns contents of the `CONTENT_TYPE` environment variable.
|
||||||
return getEnv("CONTENT_TYPE")
|
return getEnv("CONTENT_Type")
|
||||||
|
|
||||||
proc getDocumentRoot*(): string =
|
proc getDocumentRoot*(): string =
|
||||||
## Returns contents of the `DOCUMENT_ROOT` environment variable.
|
## Returns contents of the `DOCUMENT_ROOT` environment variable.
|
||||||
|
|||||||
@@ -739,7 +739,7 @@ template withValue*[A, B](t: Table[A, B], key: A,
|
|||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
||||||
iterator pairs*[A, B](t: Table[A, B]): (lent A, lent B) =
|
iterator pairs*[A, B](t: Table[A, B]): (A, B) =
|
||||||
## Iterates over any `(key, value)` pair in the table `t`.
|
## Iterates over any `(key, value)` pair in the table `t`.
|
||||||
##
|
##
|
||||||
## See also:
|
## See also:
|
||||||
@@ -1201,7 +1201,7 @@ proc `==`*[A, B](s, t: TableRef[A, B]): bool =
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
iterator pairs*[A, B](t: TableRef[A, B]): (lent A, lent B) =
|
iterator pairs*[A, B](t: TableRef[A, B]): (A, B) =
|
||||||
## Iterates over any `(key, value)` pair in the table `t`.
|
## Iterates over any `(key, value)` pair in the table `t`.
|
||||||
##
|
##
|
||||||
## See also:
|
## See also:
|
||||||
@@ -1789,7 +1789,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
iterator pairs*[A, B](t: OrderedTable[A, B]): (lent A, lent B) =
|
iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) =
|
||||||
## Iterates over any `(key, value)` pair in the table `t` in insertion
|
## Iterates over any `(key, value)` pair in the table `t` in insertion
|
||||||
## order.
|
## order.
|
||||||
##
|
##
|
||||||
@@ -2212,7 +2212,7 @@ proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool =
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
iterator pairs*[A, B](t: OrderedTableRef[A, B]): (lent A, lent B) =
|
iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
|
||||||
## Iterates over any `(key, value)` pair in the table `t` in insertion
|
## Iterates over any `(key, value)` pair in the table `t` in insertion
|
||||||
## order.
|
## order.
|
||||||
##
|
##
|
||||||
@@ -2622,7 +2622,7 @@ proc `==`*[A](s, t: CountTable[A]): bool =
|
|||||||
equalsImpl(s, t)
|
equalsImpl(s, t)
|
||||||
|
|
||||||
|
|
||||||
iterator pairs*[A](t: CountTable[A]): (lent A, int) =
|
iterator pairs*[A](t: CountTable[A]): (A, int) =
|
||||||
## Iterates over any `(key, value)` pair in the table `t`.
|
## Iterates over any `(key, value)` pair in the table `t`.
|
||||||
##
|
##
|
||||||
## See also:
|
## See also:
|
||||||
@@ -2899,7 +2899,7 @@ proc `==`*[A](s, t: CountTableRef[A]): bool =
|
|||||||
else: result = s[] == t[]
|
else: result = s[] == t[]
|
||||||
|
|
||||||
|
|
||||||
iterator pairs*[A](t: CountTableRef[A]): (lent A, int) =
|
iterator pairs*[A](t: CountTableRef[A]): (A, int) =
|
||||||
## Iterates over any `(key, value)` pair in the table `t`.
|
## Iterates over any `(key, value)` pair in the table `t`.
|
||||||
##
|
##
|
||||||
## See also:
|
## See also:
|
||||||
|
|||||||
@@ -588,9 +588,7 @@ proc handleShortOption(p: var OptParser; cmd: string) =
|
|||||||
|
|
||||||
template next(): untyped = p.cmds[p.idx + 1]
|
template next(): untyped = p.cmds[p.idx + 1]
|
||||||
|
|
||||||
let canTakeVal = card(p.shortNoVal) > 0 and
|
let canTakeVal = card(p.shortNoVal) > 0 and p.key[0] notin p.shortNoVal
|
||||||
p.key.len > 0 and p.key[0] notin p.shortNoVal
|
|
||||||
|
|
||||||
if i < cmd.len and cmd[i] in p.separators:
|
if i < cmd.len and cmd[i] in p.separators:
|
||||||
# separator case
|
# separator case
|
||||||
if prShortAllowSep in p.rules:
|
if prShortAllowSep in p.rules:
|
||||||
|
|||||||
@@ -36,8 +36,6 @@ elif defined(linux):
|
|||||||
# Android
|
# Android
|
||||||
"/data/data/com.termux/files/usr/etc/tls/cert.pem",
|
"/data/data/com.termux/files/usr/etc/tls/cert.pem",
|
||||||
"/system/etc/security/cacerts",
|
"/system/etc/security/cacerts",
|
||||||
# Nix
|
|
||||||
"/etc/ssl/certs/ca-bundle.crt"
|
|
||||||
]
|
]
|
||||||
elif defined(bsd):
|
elif defined(bsd):
|
||||||
const certificatePaths = [
|
const certificatePaths = [
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ proc readDataStr*(s: Stream, buffer: var string, slice: Slice[int]): int =
|
|||||||
result = s.readDataStrImpl(s, buffer, slice)
|
result = s.readDataStrImpl(s, buffer, slice)
|
||||||
else:
|
else:
|
||||||
# fallback
|
# fallback
|
||||||
result = s.readData(beginStore(buffer, buffer.len, slice.a), slice.b + 1 - slice.a)
|
result = s.readData(beginStore(buffer, slice.b + 1 - slice.a, slice.a), slice.b + 1 - slice.a)
|
||||||
endStore(buffer)
|
endStore(buffer)
|
||||||
|
|
||||||
template jsOrVmBlock(caseJsOrVm, caseElse: untyped): untyped =
|
template jsOrVmBlock(caseJsOrVm, caseElse: untyped): untyped =
|
||||||
@@ -1226,7 +1226,7 @@ else: # after 1.3 or JS not defined
|
|||||||
jsOrVmBlock:
|
jsOrVmBlock:
|
||||||
buffer[slice.a..<slice.a+result] = s.data[s.pos..<s.pos+result]
|
buffer[slice.a..<slice.a+result] = s.data[s.pos..<s.pos+result]
|
||||||
do:
|
do:
|
||||||
copyMem(beginStore(buffer, buffer.len, slice.a), readRawData(s.data, s.pos), result)
|
copyMem(beginStore(buffer, result, slice.a), readRawData(s.data, s.pos), result)
|
||||||
endStore(buffer)
|
endStore(buffer)
|
||||||
inc(s.pos, result)
|
inc(s.pos, result)
|
||||||
else:
|
else:
|
||||||
@@ -1267,16 +1267,16 @@ else: # after 1.3 or JS not defined
|
|||||||
var s = StringStream(s)
|
var s = StringStream(s)
|
||||||
if bufLen <= 0:
|
if bufLen <= 0:
|
||||||
return
|
return
|
||||||
|
if s.pos + bufLen > s.data.len:
|
||||||
|
setLen(s.data, s.pos + bufLen)
|
||||||
when defined(js):
|
when defined(js):
|
||||||
if s.pos + bufLen > s.data.len:
|
|
||||||
setLen(s.data, s.pos + bufLen)
|
|
||||||
try:
|
try:
|
||||||
s.data[s.pos..<s.pos+bufLen] = cast[ptr string](buffer)[][0..<bufLen]
|
s.data[s.pos..<s.pos+bufLen] = cast[ptr string](buffer)[][0..<bufLen]
|
||||||
except:
|
except:
|
||||||
raise newException(Defect, "could not write to string stream, " &
|
raise newException(Defect, "could not write to string stream, " &
|
||||||
"did you use a non-string buffer pointer?", getCurrentException())
|
"did you use a non-string buffer pointer?", getCurrentException())
|
||||||
elif not defined(nimscript):
|
elif not defined(nimscript):
|
||||||
copyMem(beginStore(s.data, s.pos + bufLen, s.pos), buffer, bufLen)
|
copyMem(beginStore(s.data, bufLen, s.pos), buffer, bufLen)
|
||||||
endStore(s.data)
|
endStore(s.data)
|
||||||
inc(s.pos, bufLen)
|
inc(s.pos, bufLen)
|
||||||
|
|
||||||
@@ -1346,7 +1346,7 @@ proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
|
|||||||
|
|
||||||
proc fsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int =
|
proc fsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int =
|
||||||
let len = slice.b + 1 - slice.a
|
let len = slice.b + 1 - slice.a
|
||||||
result = readBuffer(FileStream(s).f, beginStore(buffer, buffer.len, slice.a), len)
|
result = readBuffer(FileStream(s).f, beginStore(buffer, len, slice.a), len)
|
||||||
endStore(buffer)
|
endStore(buffer)
|
||||||
|
|
||||||
proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
|
proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ proc addCstringN(result: var string, buf: cstring; buflen: int) =
|
|||||||
# no nimvm support needed, so it doesn't need to be fast here either
|
# no nimvm support needed, so it doesn't need to be fast here either
|
||||||
let oldLen = result.len
|
let oldLen = result.len
|
||||||
let newLen = oldLen + buflen
|
let newLen = oldLen + buflen
|
||||||
|
result.setLen newLen
|
||||||
{.cast(noSideEffect).}:
|
{.cast(noSideEffect).}:
|
||||||
when declared(beginStore):
|
when declared(completeStore):
|
||||||
c_memcpy(beginStore(result, newLen, oldLen), buf, buflen.csize_t)
|
c_memcpy(beginStore(result, buflen, oldLen), buf, buflen.csize_t)
|
||||||
endStore(result)
|
endStore(result)
|
||||||
else:
|
else:
|
||||||
result.setLen newLen
|
|
||||||
discard c_memcpy(result[oldLen].addr, buf, buflen.csize_t)
|
discard c_memcpy(result[oldLen].addr, buf, buflen.csize_t)
|
||||||
|
|
||||||
import std/private/[dragonbox, schubfach]
|
import std/private/[dragonbox, schubfach]
|
||||||
|
|||||||
344
lib/std/nre2.nim
344
lib/std/nre2.nim
@@ -1,344 +0,0 @@
|
|||||||
#
|
|
||||||
# Nim's Runtime Library
|
|
||||||
# (c) Copyright 2026 Nim Contributors
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## What is NRE2?
|
|
||||||
## =============
|
|
||||||
##
|
|
||||||
## A regular expression library for Nim to replace deprecated NRE.
|
|
||||||
## It is implemented with `Regex<https://github.com/nitely/nim-regex>`_ ,
|
|
||||||
## that is pure Nim regex engine and guarantees linear time matching.
|
|
||||||
## It supports compiling regex and matching at compile-time and
|
|
||||||
## works with JS backend.
|
|
||||||
##
|
|
||||||
## NRE2 is mostly compatible with NRE and the syntax of regular expression is similar to PCRE.
|
|
||||||
## But it lacks a few features and how to set options in a pattern is different.
|
|
||||||
##
|
|
||||||
## The syntax of regular expression is explained in https://nitely.github.io/nim-regex/regex.html
|
|
||||||
runnableExamples:
|
|
||||||
import std/sugar
|
|
||||||
let vowels = re"[aeoui]"
|
|
||||||
let bounds = collect:
|
|
||||||
for match in "moiga".findIter(vowels): match.matchBounds
|
|
||||||
assert bounds == @[1 .. 1, 2 .. 2, 4 .. 4]
|
|
||||||
from std/sequtils import toSeq
|
|
||||||
let s = sequtils.toSeq("moiga".findIter(vowels))
|
|
||||||
# fully qualified to avoid confusion with nre.toSeq
|
|
||||||
assert s.len == 3
|
|
||||||
|
|
||||||
let firstVowel = "foo".find(vowels)
|
|
||||||
let hasVowel = firstVowel.isSome()
|
|
||||||
assert hasVowel
|
|
||||||
let matchBounds = firstVowel.get().captureBounds[-1]
|
|
||||||
assert matchBounds.a == 1
|
|
||||||
|
|
||||||
# as with module `re`, unless specified otherwise, `start` parameter in each
|
|
||||||
# proc indicates where the scan starts, but outputs are relative to the start
|
|
||||||
# of the input string, not to `start`:
|
|
||||||
assert find("uxabc", re"(?<=x|y)ab", start = 1).get.captures[-1] == "ab"
|
|
||||||
assert find("uxabc", re"ab", start = 3).isNone
|
|
||||||
|
|
||||||
import std/[options, tables]
|
|
||||||
import regex, regex/nfatype
|
|
||||||
|
|
||||||
export options
|
|
||||||
export regex.RegexFlags, regex.RegexError
|
|
||||||
|
|
||||||
type
|
|
||||||
Regex* = regex.Regex2
|
|
||||||
## Represents the pattern that things are matched against, constructed with
|
|
||||||
## `re(string)`. Examples: `re"foo"`, `re(r"(?x)foo #comment")`
|
|
||||||
##
|
|
||||||
## `captureCount: int`
|
|
||||||
## : the number of captures that the pattern has.
|
|
||||||
##
|
|
||||||
## `captureNameId: Table[string, int]`
|
|
||||||
## : a table from the capture names to their numeric id.
|
|
||||||
##
|
|
||||||
## The syntax of regular expression of Regex is explained in https://nitely.github.io/nim-regex/regex.html
|
|
||||||
|
|
||||||
RegexMatch* = object
|
|
||||||
## Usually seen as `Option[RegexMatch]`, it represents the result of an
|
|
||||||
## execution. On failure, it is none, on success, it is some.
|
|
||||||
##
|
|
||||||
## `str: string`
|
|
||||||
## : the string that was matched against
|
|
||||||
##
|
|
||||||
## `captures[]: string`
|
|
||||||
## : the string value of whatever was captured at that id. If the value
|
|
||||||
## is invalid, then behavior is undefined. If the id is `-1`, then
|
|
||||||
## the whole match is returned. If the given capture was not matched,
|
|
||||||
## `nil` is returned. See examples for `match`.
|
|
||||||
##
|
|
||||||
## `captureBounds[]: HSlice[int, int]`
|
|
||||||
## : gets the bounds of the given capture according to the same rules as
|
|
||||||
## the above. If the capture is not filled, then `None` is returned.
|
|
||||||
## The bounds are both inclusive. See examples for `match`.
|
|
||||||
##
|
|
||||||
## `match: string`
|
|
||||||
## : the full text of the match.
|
|
||||||
##
|
|
||||||
## `matchBounds: HSlice[int, int]`
|
|
||||||
## : the bounds of the match, as in `captureBounds[]`
|
|
||||||
##
|
|
||||||
## `(captureBounds|captures).toTable`
|
|
||||||
## : returns a table with each named capture as a key.
|
|
||||||
##
|
|
||||||
## `(captureBounds|captures).toSeq`
|
|
||||||
## : returns all the captures by their number.
|
|
||||||
##
|
|
||||||
## `$: string`
|
|
||||||
## : same as `match`
|
|
||||||
str*: string ## The string that was matched against.
|
|
||||||
matchImpl: regex.RegexMatch2
|
|
||||||
|
|
||||||
Captures* {.borrow: `.`.} = distinct RegexMatch
|
|
||||||
CaptureBounds* {.borrow: `.`.} = distinct RegexMatch
|
|
||||||
|
|
||||||
func captureCount*(pattern: Regex): int {.inline.} =
|
|
||||||
pattern.toRegex().groupsCount
|
|
||||||
|
|
||||||
func captureNameId*(pattern: Regex): Table[string, int] =
|
|
||||||
result = initTable[string, int](pattern.toRegex().namedGroups.len)
|
|
||||||
for k, v in pattern.toRegex().namedGroups:
|
|
||||||
result[k] = v
|
|
||||||
|
|
||||||
func captureBounds*(match: RegexMatch): CaptureBounds {.inline.} =
|
|
||||||
CaptureBounds(match)
|
|
||||||
|
|
||||||
func captures*(match: RegexMatch): Captures {.inline.} =
|
|
||||||
Captures(match)
|
|
||||||
|
|
||||||
func contains*(match: Captures or CaptureBounds, i: int): bool {.inline.} =
|
|
||||||
i >= -1 and i < match.matchImpl.groupsCount and match.matchImpl.group(i) != reNonCapture
|
|
||||||
|
|
||||||
func len*(match: Captures or CaptureBounds): int {.inline.} =
|
|
||||||
## Return the number of capturing groups
|
|
||||||
match.matchImpl.groupsCount
|
|
||||||
|
|
||||||
func `[]`*(match: CaptureBounds; i: int): HSlice[int, int] {.inline.} =
|
|
||||||
if i == -1: match.matchImpl.boundaries else: match.matchImpl.group(i)
|
|
||||||
|
|
||||||
func `[]`*(match: CaptureBounds; name: string): HSlice[int, int] {.inline.} =
|
|
||||||
result = match.matchImpl.group(name)
|
|
||||||
if result == reNonCapture:
|
|
||||||
raise newException(KeyError, "Group '" & name & "' was not captured")
|
|
||||||
|
|
||||||
func `[]`*(match: Captures; i: int): string {.inline.} =
|
|
||||||
match.str[CaptureBounds(match)[i]]
|
|
||||||
|
|
||||||
func `[]`*(match: Captures, name: string): string {.inline.} =
|
|
||||||
match.str[CaptureBounds(match)[name]]
|
|
||||||
|
|
||||||
func match*(match: RegexMatch): string {.inline.} =
|
|
||||||
match.str[match.matchImpl.boundaries]
|
|
||||||
|
|
||||||
func matchBounds*(match: RegexMatch): HSlice[int, int] {.inline.} =
|
|
||||||
match.matchImpl.boundaries
|
|
||||||
|
|
||||||
func contains*(match: CaptureBounds or Captures, name: string): bool {.inline.} =
|
|
||||||
name in match.matchImpl.namedGroups and
|
|
||||||
match.matchImpl.group(name) != reNonCapture
|
|
||||||
|
|
||||||
func toTable*(match: Captures): Table[string, string] =
|
|
||||||
result = initTable[string, string]()
|
|
||||||
for k, i in match.matchImpl.namedGroups:
|
|
||||||
let r = match.matchImpl.group(i)
|
|
||||||
if r != reNonCapture:
|
|
||||||
result[k] = match.str[r]
|
|
||||||
|
|
||||||
func toTable*(match: CaptureBounds): Table[string, HSlice[int, int]] =
|
|
||||||
result = initTable[string, HSlice[int, int]]()
|
|
||||||
for k, i in match.matchImpl.namedGroups:
|
|
||||||
let r = match.matchImpl.group(i)
|
|
||||||
if r != reNonCapture:
|
|
||||||
result[k] = match.matchImpl.group(i)
|
|
||||||
|
|
||||||
iterator items*(match: CaptureBounds; default = none(HSlice[int, int])): Option[HSlice[int, int]] =
|
|
||||||
for i in 0 ..< match.len:
|
|
||||||
yield if i in match: some(match[i]) else: default
|
|
||||||
|
|
||||||
iterator items*(match: Captures; default = none(string)): Option[string] =
|
|
||||||
for i in 0 ..< match.len:
|
|
||||||
yield if i in match: some(match[i]) else: default
|
|
||||||
|
|
||||||
func toSeq*(match: CaptureBounds;
|
|
||||||
default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] =
|
|
||||||
result = @[]
|
|
||||||
for it in match.items(default): result.add it
|
|
||||||
|
|
||||||
func toSeq*(match: Captures;
|
|
||||||
default: Option[string] = none(string)): seq[Option[string]] =
|
|
||||||
result = @[]
|
|
||||||
for it in match.items(default): result.add it
|
|
||||||
|
|
||||||
func `$`*(match: RegexMatch): string =
|
|
||||||
match.match
|
|
||||||
|
|
||||||
func re*(pattern: static string; flags: static RegexFlags = {}): static[Regex2] =
|
|
||||||
## Parse and compile a regular expression at compile-time
|
|
||||||
result = regex.re2(pattern, flags)
|
|
||||||
|
|
||||||
func re*(pattern: string; flags: RegexFlags = {}): Regex =
|
|
||||||
## Parse and compile a regular expression at run-time
|
|
||||||
result = regex.re2(pattern, flags)
|
|
||||||
|
|
||||||
func match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
|
|
||||||
## Like `find(...)<#find,string,Regex,int>`_, but anchored to the start of the
|
|
||||||
## string.
|
|
||||||
runnableExamples:
|
|
||||||
assert "foo".match(re"f").isSome
|
|
||||||
assert "foo".match(re"o").isNone
|
|
||||||
|
|
||||||
assert "abc".match(re"(\w)").get.captures[0] == "a"
|
|
||||||
assert "abc".match(re"(?P<letter>\w)").get.captures["letter"] == "a"
|
|
||||||
assert "abc".match(re"(\w)\w").get.captures[-1] == "ab"
|
|
||||||
|
|
||||||
assert "abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0
|
|
||||||
assert 0 in "abc".match(re"(\w)").get.captureBounds
|
|
||||||
assert "abc".match(re"").get.captureBounds[-1] == 0 .. -1
|
|
||||||
assert "abc".match(re"abc").get.captureBounds[-1] == 0 .. 2
|
|
||||||
var mat = default(RegexMatch)
|
|
||||||
let r = regex.startsWith(str.toOpenArray(0, min(str.high, endpos)), pattern, mat.matchImpl, start)
|
|
||||||
if r:
|
|
||||||
mat.str = str
|
|
||||||
some(mat)
|
|
||||||
else:
|
|
||||||
none(RegexMatch)
|
|
||||||
|
|
||||||
iterator findIter*(str: string; pattern: Regex; start = 0, endpos = int.high): RegexMatch =
|
|
||||||
## Works the same as `find(...)<#find,string,Regex,int>`_, but finds every
|
|
||||||
## non-overlapping match:
|
|
||||||
runnableExamples:
|
|
||||||
import std/sugar
|
|
||||||
assert collect(for a in "2222".findIter(re"22"): a.match) == @["22", "22"]
|
|
||||||
# not @["22", "22", "22"]
|
|
||||||
## Arguments are the same as `find(...)<#find,string,Regex,int>`_
|
|
||||||
##
|
|
||||||
## Variants:
|
|
||||||
##
|
|
||||||
## - `proc findAll(...)` returns a `seq[string]`
|
|
||||||
var mat = RegexMatch(str: str)
|
|
||||||
# TODO:
|
|
||||||
# needs following PR to remove `substr` call.
|
|
||||||
# https://github.com/nitely/nim-regex/pull/162
|
|
||||||
for m in regex.findAll(str.substr(start, endpos), pattern):
|
|
||||||
mat.matchImpl = m
|
|
||||||
yield mat
|
|
||||||
|
|
||||||
proc find*(str: string; pattern: Regex; start = 0; endpos = int.high): Option[RegexMatch] =
|
|
||||||
## Finds the given pattern in the string between the end and start
|
|
||||||
## positions.
|
|
||||||
##
|
|
||||||
## `start`
|
|
||||||
## : The start point at which to start matching. `|abc` is `0`;
|
|
||||||
## `a|bc` is `1`
|
|
||||||
##
|
|
||||||
## `endpos`
|
|
||||||
## : The maximum index for a match; `int.high` means the end of the
|
|
||||||
## string, otherwise it’s an inclusive upper bound.
|
|
||||||
var mat = default(RegexMatch)
|
|
||||||
let r = regex.find(str.substr(start, endpos), pattern, mat.matchImpl)
|
|
||||||
|
|
||||||
# remove following code after regex.find get `start`/`last` parameter
|
|
||||||
for v in mat.matchImpl.captures.mitems:
|
|
||||||
v.a += start
|
|
||||||
v.b += start
|
|
||||||
mat.matchImpl.boundaries.a += start
|
|
||||||
mat.matchImpl.boundaries.b += start
|
|
||||||
|
|
||||||
if r:
|
|
||||||
mat.str = str
|
|
||||||
some(mat)
|
|
||||||
else:
|
|
||||||
none(RegexMatch)
|
|
||||||
|
|
||||||
proc findAll*(str: string; pattern: Regex; start = 0; endpos = int.high): seq[string] =
|
|
||||||
result = @[]
|
|
||||||
for match in str.findIter(pattern, start, endpos):
|
|
||||||
result.add(match.match)
|
|
||||||
|
|
||||||
proc contains*(str: string; pattern: Regex; start = 0; endpos = int.high): bool =
|
|
||||||
## Determine if the string contains the given pattern between the end and
|
|
||||||
## start positions:
|
|
||||||
## This function is equivalent to `isSome(str.find(pattern, start, endpos))`.
|
|
||||||
runnableExamples:
|
|
||||||
assert "abc".contains(re"bc")
|
|
||||||
assert not "abc".contains(re"cd")
|
|
||||||
assert not "abc".contains(re"a", start = 1)
|
|
||||||
|
|
||||||
isSome(str.find(pattern, start, endpos))
|
|
||||||
|
|
||||||
proc split*(str: string; pattern: Regex; maxSplit = -1; start = 0): seq[string] =
|
|
||||||
## Splits the string with the given regex. This works according to the
|
|
||||||
## rules that Perl and Javascript use.
|
|
||||||
##
|
|
||||||
## `start` behaves the same as in `find(...)<#find,string,Regex,int>`_.
|
|
||||||
##
|
|
||||||
runnableExamples:
|
|
||||||
# - If the match is zero-width, then the string is still split:
|
|
||||||
assert "123".split(re"") == @["1", "2", "3"]
|
|
||||||
|
|
||||||
# - If the pattern has a capture in it, it is added after the string
|
|
||||||
# split:
|
|
||||||
assert "12".split(re"(\d)") == @["", "1", "", "2", ""]
|
|
||||||
|
|
||||||
# - If `maxsplit != -1`, then the string will only be split
|
|
||||||
# `maxsplit - 1` times. This means that there will be `maxsplit`
|
|
||||||
# strings in the output seq.
|
|
||||||
assert "1.2.3".split(re"\.", maxsplit = 2) == @["1", "2.3"]
|
|
||||||
|
|
||||||
result = splitIncl(str, pattern, maxSplit, start)
|
|
||||||
|
|
||||||
proc replace*(str: string; pattern: Regex;
|
|
||||||
subproc: proc (match: RegexMatch): string): string =
|
|
||||||
## Replaces each match of Regex in the string with `subproc`, which should
|
|
||||||
## never be or return `nil`.
|
|
||||||
##
|
|
||||||
## If `subproc` is a `proc (RegexMatch): string`, then it is executed with
|
|
||||||
## each match and the return value is the replacement value.
|
|
||||||
##
|
|
||||||
## If `subproc` is a `proc (string): string`, then it is executed with the
|
|
||||||
## full text of the match and the return value is the replacement value.
|
|
||||||
##
|
|
||||||
## If `subproc` is a string, the syntax is as follows:
|
|
||||||
##
|
|
||||||
## - `$$` - literal `$`
|
|
||||||
## - `$123` - capture number `123`
|
|
||||||
## - `$1$#` - first and second captures
|
|
||||||
## - `$#` - first capture
|
|
||||||
##
|
|
||||||
## Following syntax is not supported in NRE2
|
|
||||||
##
|
|
||||||
## - `$foo` - named capture `foo`
|
|
||||||
## - `${foo}` - same as above
|
|
||||||
## - `$0` - full match
|
|
||||||
##
|
|
||||||
## If a given capture is missing, `ValueError` is thrown.
|
|
||||||
proc by(m: RegexMatch2, s: string): string =
|
|
||||||
let mat = RegexMatch(str: s, matchImpl: m)
|
|
||||||
result = subproc(mat)
|
|
||||||
|
|
||||||
result = regex.replace(str, pattern, by)
|
|
||||||
|
|
||||||
proc replace*(str: string; pattern: Regex;
|
|
||||||
subproc: proc (match: string): string): string =
|
|
||||||
proc by(m: RegexMatch2; s: string): string =
|
|
||||||
result = subproc(s)
|
|
||||||
|
|
||||||
result = regex.replace(str, pattern, by)
|
|
||||||
|
|
||||||
proc replace*(str: string; pattern: Regex; sub: string): string =
|
|
||||||
result = regex.replace(str, pattern, sub)
|
|
||||||
|
|
||||||
func escapeRe*(str: string): string =
|
|
||||||
## Escapes the string so it doesn't match any special characters.
|
|
||||||
runnableExamples:
|
|
||||||
assert escapeRe("fly+wind") == "fly\\+wind"
|
|
||||||
assert escapeRe("nim*") == "nim\\*"
|
|
||||||
|
|
||||||
result = regex.escapeRe(str)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import std/os
|
|
||||||
|
|
||||||
if getCommand() == "doc":
|
|
||||||
# std/nre2 requires nim-regex and it requires nim-unicodedb.
|
|
||||||
# when build documentation on CI, git clone them as nimble is not available
|
|
||||||
|
|
||||||
const PkgDir = "build/deps"
|
|
||||||
const Pkgs = ["nim-regex", "nim-unicodedb"]
|
|
||||||
|
|
||||||
for n in Pkgs:
|
|
||||||
if not dirExists(PkgDir / n):
|
|
||||||
exec("git clone -q https://github.com/nitely/" & n & " " & (PkgDir / n))
|
|
||||||
|
|
||||||
switch("path", "$nim" / PkgDir / n / "src")
|
|
||||||
@@ -294,11 +294,7 @@ proc containsOrIncl*[A](s: var PackedSet[A], key: A): bool =
|
|||||||
for i in 0..<s.elems:
|
for i in 0..<s.elems:
|
||||||
if s.a[i] == ord(key):
|
if s.a[i] == ord(key):
|
||||||
return true
|
return true
|
||||||
if s.elems < s.a.len:
|
incl(s, key)
|
||||||
s.a[s.elems] = ord(key)
|
|
||||||
inc(s.elems)
|
|
||||||
else:
|
|
||||||
incl(s, key)
|
|
||||||
result = false
|
result = false
|
||||||
else:
|
else:
|
||||||
var t = packedSetGet(s, ord(key) shr TrunkShift)
|
var t = packedSetGet(s, ord(key) shr TrunkShift)
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ func setSlice*(s: var string, slice: Slice[int]) =
|
|||||||
when not declared(moveMem):
|
when not declared(moveMem):
|
||||||
impl()
|
impl()
|
||||||
else:
|
else:
|
||||||
let p = beginStore(s, s.len)
|
let p = beginStore(s, last - first + 1)
|
||||||
moveMem(p, addr p[first], last - first + 1)
|
moveMem(p, addr p[first], last - first + 1)
|
||||||
endStore(s)
|
endStore(s)
|
||||||
s.setLen(last - first + 1)
|
s.setLen(last - first + 1)
|
||||||
|
|||||||
@@ -485,7 +485,7 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect],
|
|||||||
while true:
|
while true:
|
||||||
# fixes #9634; this pattern may need to be abstracted as a template if reused;
|
# fixes #9634; this pattern may need to be abstracted as a template if reused;
|
||||||
# likely other io procs need this for correctness.
|
# likely other io procs need this for correctness.
|
||||||
fgetsSuccess = c_fgets(cast[cstring](beginStore(line, pos + sp, pos)), sp.cint, f) != nil
|
fgetsSuccess = c_fgets(cast[cstring](beginStore(line, sp, pos)), sp.cint, f) != nil
|
||||||
endStore(line)
|
endStore(line)
|
||||||
if fgetsSuccess: break
|
if fgetsSuccess: break
|
||||||
when not defined(nimscript):
|
when not defined(nimscript):
|
||||||
|
|||||||
@@ -1703,8 +1703,7 @@ when not (notJSnotNims and defined(nimSeqsV2)):
|
|||||||
# Needed so modules imported by system (e.g. syncio) can reference these without guards.
|
# Needed so modules imported by system (e.g. syncio) can reference these without guards.
|
||||||
when notJSnotNims:
|
when notJSnotNims:
|
||||||
# mm:refc: string = ptr NimStringDesc with data: UncheckedArray[char]
|
# mm:refc: string = ptr NimStringDesc with data: UncheckedArray[char]
|
||||||
proc beginStore*(s: var string; newLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} =
|
proc beginStore*(s: var string; ensuredLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} =
|
||||||
{.cast(noSideEffect).}: s.setLen(newLen)
|
|
||||||
let ns = cast[NimString](s)
|
let ns = cast[NimString](s)
|
||||||
if ns == nil: nil
|
if ns == nil: nil
|
||||||
else: cast[ptr UncheckedArray[char]](addr ns.data[start])
|
else: cast[ptr UncheckedArray[char]](addr ns.data[start])
|
||||||
@@ -1715,7 +1714,7 @@ when not (notJSnotNims and defined(nimSeqsV2)):
|
|||||||
else: cast[ptr UncheckedArray[char]](addr ns.data[start])
|
else: cast[ptr UncheckedArray[char]](addr ns.data[start])
|
||||||
else:
|
else:
|
||||||
# JS/nimscript: callers are guarded by whenNotVmJsNims/when not defined(js)
|
# JS/nimscript: callers are guarded by whenNotVmJsNims/when not defined(js)
|
||||||
proc beginStore*(s: var string; newLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} = nil
|
proc beginStore*(s: var string; ensuredLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} = nil
|
||||||
proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} = discard
|
proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} = discard
|
||||||
template readRawData*(s: string; start = 0): ptr UncheckedArray[char] = nil
|
template readRawData*(s: string; start = 0): ptr UncheckedArray[char] = nil
|
||||||
|
|
||||||
@@ -2419,33 +2418,6 @@ when notJSnotNims and hasAlloc:
|
|||||||
when not defined(nimV2):
|
when not defined(nimV2):
|
||||||
include "system/repr"
|
include "system/repr"
|
||||||
|
|
||||||
func setLenUninit*(s: var string, newlen: Natural) {.nodestroy.} =
|
|
||||||
## Sets the length of string `s` to `newlen`.
|
|
||||||
## New slots will not be initialized.
|
|
||||||
##
|
|
||||||
## If the new length is smaller than the new length,
|
|
||||||
## `s` will be truncated.
|
|
||||||
let n = max(newLen, 0)
|
|
||||||
when nimvm:
|
|
||||||
s.setLen(n)
|
|
||||||
else:
|
|
||||||
when notJSnotNims:
|
|
||||||
when defined(nimSeqsV2):
|
|
||||||
{.noSideEffect.}:
|
|
||||||
let str = unsafeAddr s
|
|
||||||
when defined(nimsso):
|
|
||||||
setLengthStrV3Uninit(cast[ptr SmallString](str)[], newlen)
|
|
||||||
else:
|
|
||||||
setLengthStrV2Uninit(cast[ptr NimStringV2](str)[], newlen)
|
|
||||||
else:
|
|
||||||
{.noSideEffect.}:
|
|
||||||
when hasAlloc:
|
|
||||||
setLengthStrUninit(s, newlen)
|
|
||||||
else:
|
|
||||||
s.setLen(n)
|
|
||||||
else: s.setLen(n)
|
|
||||||
|
|
||||||
|
|
||||||
when notJSnotNims and hasThreadSupport and hostOS != "standalone":
|
when notJSnotNims and hasThreadSupport and hostOS != "standalone":
|
||||||
when not defined(nimPreviewSlimSystem):
|
when not defined(nimPreviewSlimSystem):
|
||||||
include "system/channels_builtin"
|
include "system/channels_builtin"
|
||||||
|
|||||||
@@ -158,26 +158,6 @@ proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
|
|||||||
s.p.data[newLen] = '\0'
|
s.p.data[newLen] = '\0'
|
||||||
s.len = newLen
|
s.len = newLen
|
||||||
|
|
||||||
proc setLengthStrV2Uninit(s: var NimStringV2, newLen: int) =
|
|
||||||
if newLen == 0:
|
|
||||||
discard "do not free the buffer here, pattern 's.setLen 0' is common for avoiding allocations"
|
|
||||||
else:
|
|
||||||
if isLiteral(s):
|
|
||||||
let oldP = s.p
|
|
||||||
s.p = allocPayload(newLen)
|
|
||||||
s.p.cap = newLen
|
|
||||||
if s.len > 0:
|
|
||||||
copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], min(s.len, newLen))
|
|
||||||
s.p.data[newLen] = '\0'
|
|
||||||
elif newLen > s.len:
|
|
||||||
let oldCap = s.p.cap and not strlitFlag
|
|
||||||
if newLen > oldCap:
|
|
||||||
let newCap = max(newLen, resize(oldCap))
|
|
||||||
s.p = reallocPayload0(s.p, oldCap, newCap)
|
|
||||||
s.p.cap = newCap
|
|
||||||
s.p.data[newLen] = '\0'
|
|
||||||
s.len = newLen
|
|
||||||
|
|
||||||
proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
|
proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
|
||||||
if a.p == b.p and a.len == b.len: return
|
if a.p == b.p and a.len == b.len: return
|
||||||
if isLiteral(b):
|
if isLiteral(b):
|
||||||
@@ -236,17 +216,13 @@ func capacity*(self: string): int {.inline.} =
|
|||||||
let str = cast[ptr NimStringV2](unsafeAddr self)
|
let str = cast[ptr NimStringV2](unsafeAddr self)
|
||||||
result = if str.p != nil: str.p.cap and not strlitFlag else: 0
|
result = if str.p != nil: str.p.cap and not strlitFlag else: 0
|
||||||
|
|
||||||
proc beginStore*(s: var string; newLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} =
|
proc beginStore*(s: var string; ensuredLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} =
|
||||||
## Sets s.len to `newLen` (new bytes are uninitialized), ensures unique
|
## Returns a writable pointer for bulk write of `ensuredLen` bytes starting at `start`.
|
||||||
## ownership, and returns a pointer to s[start] for bulk writing.
|
|
||||||
## Call `endStore(s)` afterwards for portability.
|
## Call `endStore(s)` afterwards for portability.
|
||||||
## To keep the current length, pass `s.len`.
|
{.cast(noSideEffect).}: prepareMutation(s)
|
||||||
{.cast(noSideEffect).}:
|
let str = cast[ptr NimStringV2](unsafeAddr s)
|
||||||
let p = cast[ptr NimStringV2](addr s)
|
if str.p == nil: nil
|
||||||
setLengthStrV2Uninit(p[], newLen)
|
else: cast[ptr UncheckedArray[char]](addr str.p.data[start])
|
||||||
prepareMutation(s)
|
|
||||||
if p.p == nil: nil
|
|
||||||
else: cast[ptr UncheckedArray[char]](addr p.p.data[start])
|
|
||||||
|
|
||||||
proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} =
|
proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} =
|
||||||
## No-op for non-SSO strings; call after bulk writes via `beginStore`.
|
## No-op for non-SSO strings; call after bulk writes via `beginStore`.
|
||||||
|
|||||||
@@ -496,75 +496,28 @@ proc mnewString(len: int): SmallString {.compilerproc.} =
|
|||||||
result.more = p
|
result.more = p
|
||||||
setSSLen(result, HeapSlen)
|
setSSLen(result, HeapSlen)
|
||||||
|
|
||||||
proc setLengthStr(s: var SmallString; newLen: int; zeroing: bool) =
|
proc setLengthStrV2(s: var SmallString; newLen: int) {.compilerRtl.} =
|
||||||
# Shared implementation for setLengthStrV2 (zeroing) and setLengthStrV3Uninit
|
## Sets the length of s to newLen, zeroing new bytes on growth.
|
||||||
# Difference between the two modes:
|
|
||||||
# - inline/medium -> long growth: alloc0 (zeroing) vs alloc (uninit)
|
|
||||||
# - long -> long growth: zeroMem the new tail (zeroing) or skip it (uninit)
|
|
||||||
let slen = ssLen(s)
|
let slen = ssLen(s)
|
||||||
let curLen = if slen > PayloadSize: s.more.fullLen else: slen
|
let curLen = if slen > PayloadSize: s.more.fullLen else: slen
|
||||||
if newLen == curLen: return
|
if newLen == curLen: return
|
||||||
if newLen < curLen:
|
if newLen <= 0:
|
||||||
# Shrinking:
|
|
||||||
if slen > PayloadSize:
|
if slen > PayloadSize:
|
||||||
if slen == HeapSlen and s.more.rc == 1:
|
if slen == HeapSlen and s.more.rc == 1:
|
||||||
# Unique heap block: keep the buffer allocated to avoid alloc/dealloc
|
s.more.fullLen = 0
|
||||||
# ping-pong when callers shrink then grow (e.g. setLen(0) + add loops).
|
s.more.data[0] = '\0'
|
||||||
s.more.fullLen = newLen
|
|
||||||
s.more.data[newLen] = '\0'
|
|
||||||
else:
|
else:
|
||||||
# shared or static block: detach and go back to inline
|
# shared or static block: detach and go back to empty inline
|
||||||
if newLen <= 0:
|
nimDestroyStrV1(s)
|
||||||
nimDestroyStrV1(s)
|
s.bytes = 0 # slen=0, all inline chars zeroed
|
||||||
s.bytes = 0
|
|
||||||
else:
|
|
||||||
let old = s.more
|
|
||||||
let inl = inlinePtr(s)
|
|
||||||
copyMem(inl, addr old.data[0], newLen)
|
|
||||||
inl[newLen] = '\0'
|
|
||||||
if slen == HeapSlen and atomicSubFetch(old.rc, 1) == 0:
|
|
||||||
dealloc(old)
|
|
||||||
if newLen < AlwaysAvail:
|
|
||||||
when system.cpuEndian == littleEndian:
|
|
||||||
let keepBits = (newLen + 1) * 8
|
|
||||||
let charMask = ((uint(1) shl keepBits) - 1'u) and not 0xFF'u
|
|
||||||
s.bytes = (s.bytes and charMask) or uint(newLen)
|
|
||||||
else:
|
|
||||||
let discardBits = (AlwaysAvail - newLen) * 8
|
|
||||||
let slenBit = 8 * (sizeof(uint) - 1)
|
|
||||||
let charMask = not ((uint(1) shl discardBits) - 1'u) and not (0xFF'u shl slenBit)
|
|
||||||
s.bytes = (s.bytes and charMask) or (uint(newLen) shl slenBit)
|
|
||||||
else:
|
|
||||||
setSSLen(s, newLen)
|
|
||||||
else:
|
else:
|
||||||
# inline/medium shrink
|
s.bytes = 0 # slen=0, all inline chars zeroed (SWAR safe)
|
||||||
if newLen <= 0:
|
|
||||||
s.bytes = 0
|
|
||||||
else:
|
|
||||||
let inl = inlinePtr(s)
|
|
||||||
inl[newLen] = '\0'
|
|
||||||
if newLen < AlwaysAvail:
|
|
||||||
when system.cpuEndian == littleEndian:
|
|
||||||
let keepBits = (newLen + 1) * 8
|
|
||||||
let charMask = ((uint(1) shl keepBits) - 1'u) and not 0xFF'u
|
|
||||||
s.bytes = (s.bytes and charMask) or uint(newLen)
|
|
||||||
else:
|
|
||||||
let discardBits = (AlwaysAvail - newLen) * 8
|
|
||||||
let slenBit = 8 * (sizeof(uint) - 1)
|
|
||||||
let charMask = not ((uint(1) shl discardBits) - 1'u) and not (0xFF'u shl slenBit)
|
|
||||||
s.bytes = (s.bytes and charMask) or (uint(newLen) shl slenBit)
|
|
||||||
else:
|
|
||||||
setSSLen(s, newLen)
|
|
||||||
return
|
return
|
||||||
if slen <= PayloadSize:
|
if slen <= PayloadSize:
|
||||||
if newLen <= PayloadSize:
|
if newLen <= PayloadSize:
|
||||||
let inl = inlinePtr(s)
|
let inl = inlinePtr(s)
|
||||||
if newLen > curLen:
|
if newLen > curLen:
|
||||||
# Grow within inline/medium
|
zeroMem(addr inl[curLen], newLen - curLen)
|
||||||
# Bytes above newLen already zero by the SWAR invariant,
|
|
||||||
# so setSSLen is sufficient.
|
|
||||||
if zeroing:
|
|
||||||
zeroMem(addr inl[curLen], newLen - curLen)
|
|
||||||
inl[newLen] = '\0'
|
inl[newLen] = '\0'
|
||||||
setSSLen(s, newLen)
|
setSSLen(s, newLen)
|
||||||
else:
|
else:
|
||||||
@@ -589,33 +542,43 @@ proc setLengthStr(s: var SmallString; newLen: int; zeroing: bool) =
|
|||||||
else:
|
else:
|
||||||
# grow into long
|
# grow into long
|
||||||
let newCap = resize(newLen)
|
let newCap = resize(newLen)
|
||||||
let p = if zeroing:
|
let p = cast[ptr LongString](alloc0(LongStringDataOffset + newCap + 1))
|
||||||
# bytes [curLen..newLen] and p.data[newLen] zeroed by alloc0
|
|
||||||
cast[ptr LongString](alloc0(LongStringDataOffset + newCap + 1))
|
|
||||||
else:
|
|
||||||
let p = cast[ptr LongString](alloc(LongStringDataOffset + newCap + 1))
|
|
||||||
p.data[newLen] = '\0'
|
|
||||||
p
|
|
||||||
p.rc = 1
|
p.rc = 1
|
||||||
p.fullLen = newLen
|
p.fullLen = newLen
|
||||||
p.capImpl = newCap
|
p.capImpl = newCap
|
||||||
copyMem(addr p.data[0], inlinePtr(s), curLen)
|
copyMem(addr p.data[0], inlinePtr(s), curLen)
|
||||||
|
# bytes [curLen..newLen] zeroed by alloc0; p.data[newLen] = '\0' by alloc0
|
||||||
s.more = p
|
s.more = p
|
||||||
setSSLen(s, HeapSlen)
|
setSSLen(s, HeapSlen)
|
||||||
else:
|
else:
|
||||||
# currently long: grow within the heap buffer (shrinking already returned above)
|
# currently long
|
||||||
ensureUniqueLong(s, curLen, newLen) # sets fullLen = newLen
|
if newLen <= PayloadSize:
|
||||||
if zeroing and newLen > curLen:
|
# shrink back to inline
|
||||||
zeroMem(addr s.more.data[curLen], newLen - curLen)
|
let old = s.more
|
||||||
s.more.data[newLen] = '\0'
|
let inl = inlinePtr(s)
|
||||||
|
copyMem(inl, addr old.data[0], newLen)
|
||||||
proc setLengthStrV2(s: var SmallString; newLen: int) {.compilerRtl.} =
|
inl[newLen] = '\0'
|
||||||
## Sets the length of `s` to `newLen`, zeroing new bytes on growth.
|
if slen == HeapSlen and atomicSubFetch(old.rc, 1) == 0:
|
||||||
setLengthStr(s, newLen, zeroing = true)
|
dealloc(old)
|
||||||
|
# Zero padding bytes in `bytes` for SWAR invariant
|
||||||
proc setLengthStrV3Uninit(s: var SmallString; newLen: int) {.compilerRtl.} =
|
if newLen < AlwaysAvail:
|
||||||
## Sets the length of `s` to `newLen`, NOT zeroing new bytes on growth.
|
when system.cpuEndian == littleEndian:
|
||||||
setLengthStr(s, newLen, zeroing = false)
|
let keepBits = (newLen + 1) * 8
|
||||||
|
let charMask = ((uint(1) shl keepBits) - 1'u) and not 0xFF'u
|
||||||
|
s.bytes = (s.bytes and charMask) or uint(newLen)
|
||||||
|
else:
|
||||||
|
let discardBits = (AlwaysAvail - newLen) * 8
|
||||||
|
let slenBit = 8 * (sizeof(uint) - 1)
|
||||||
|
let charMask = not ((uint(1) shl discardBits) - 1'u) and not (0xFF'u shl slenBit)
|
||||||
|
s.bytes = (s.bytes and charMask) or (uint(newLen) shl slenBit)
|
||||||
|
else:
|
||||||
|
setSSLen(s, newLen)
|
||||||
|
else:
|
||||||
|
ensureUniqueLong(s, curLen, newLen)
|
||||||
|
if newLen > curLen:
|
||||||
|
zeroMem(addr s.more.data[curLen], newLen - curLen)
|
||||||
|
s.more.data[newLen] = '\0'
|
||||||
|
s.more.fullLen = newLen
|
||||||
|
|
||||||
proc nimAsgnStrV2(a: var SmallString; b: SmallString) {.compilerRtl, inline.} =
|
proc nimAsgnStrV2(a: var SmallString; b: SmallString) {.compilerRtl, inline.} =
|
||||||
if ssLen(b) <= PayloadSize:
|
if ssLen(b) <= PayloadSize:
|
||||||
@@ -721,37 +684,18 @@ proc completeStore(s: var SmallString) {.compilerproc, inline.} =
|
|||||||
proc completeStore*(s: var string) {.inline.} =
|
proc completeStore*(s: var string) {.inline.} =
|
||||||
completeStore(cast[ptr SmallString](addr s)[])
|
completeStore(cast[ptr SmallString](addr s)[])
|
||||||
|
|
||||||
proc beginStore*(s: var string; newLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} =
|
proc beginStore*(s: var string; ensuredLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} =
|
||||||
## Sets s.len to `newLen` (new bytes are uninitialized), ensures unique
|
## Prepares `s` for a bulk write of `ensuredLen` bytes starting at `start`.
|
||||||
## ownership, and returns a pointer to s[start] for bulk writing.
|
## The caller must ensure `s.len >= start + ensuredLen` (e.g. via `newString` or `setLen`).
|
||||||
## Call `endStore(s)` afterwards to sync the inline cache.
|
## Call `endStore(s)` afterwards to sync the inline cache.
|
||||||
## To keep the current length, pass `s.len`.
|
|
||||||
{.cast(noSideEffect).}:
|
{.cast(noSideEffect).}:
|
||||||
let ss = cast[ptr SmallString](addr s)
|
let ss = cast[ptr SmallString](addr s)
|
||||||
let slen = ssLen(ss[])
|
let slen = ssLen(ss[])
|
||||||
let curLen = if slen > PayloadSize: ss[].more.fullLen else: slen
|
if slen > PayloadSize:
|
||||||
if newLen <= PayloadSize and slen <= PayloadSize:
|
ensureUniqueLong(ss[], ss[].more.fullLen, ss[].more.fullLen)
|
||||||
# Stay inline/medium.
|
|
||||||
if newLen != curLen:
|
|
||||||
setSSLen(ss[], newLen)
|
|
||||||
result = cast[ptr UncheckedArray[char]](cast[uint](inlinePtr(ss[])) + uint(start))
|
|
||||||
elif slen <= PayloadSize:
|
|
||||||
# Inline/medium → long.
|
|
||||||
let newCap = resize(newLen)
|
|
||||||
let p = cast[ptr LongString](alloc(LongStringDataOffset + newCap + 1))
|
|
||||||
p.rc = 1
|
|
||||||
p.fullLen = newLen
|
|
||||||
p.capImpl = newCap
|
|
||||||
copyMem(addr p.data[0], inlinePtr(ss[]), curLen)
|
|
||||||
p.data[newLen] = '\0'
|
|
||||||
ss[].more = p
|
|
||||||
setSSLen(ss[], HeapSlen)
|
|
||||||
result = cast[ptr UncheckedArray[char]](addr ss[].more.data[start])
|
result = cast[ptr UncheckedArray[char]](addr ss[].more.data[start])
|
||||||
else:
|
else:
|
||||||
# Already long: resize within heap (no transition back to inline).
|
result = cast[ptr UncheckedArray[char]](cast[uint](inlinePtr(ss[])) + uint(start))
|
||||||
ensureUniqueLong(ss[], curLen, newLen)
|
|
||||||
ss[].more.data[newLen] = '\0'
|
|
||||||
result = cast[ptr UncheckedArray[char]](addr ss[].more.data[start])
|
|
||||||
|
|
||||||
proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} =
|
proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} =
|
||||||
## Syncs the inline cache after bulk writes via `beginStore`. No-op for short/medium strings.
|
## Syncs the inline cache after bulk writes via `beginStore`. No-op for short/medium strings.
|
||||||
|
|||||||
@@ -244,31 +244,6 @@ proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
|
|||||||
result.len = n
|
result.len = n
|
||||||
result.data[n] = '\0'
|
result.data[n] = '\0'
|
||||||
|
|
||||||
proc setLengthStrUninit(s: var string, newlen: Natural) {.nodestroy.} =
|
|
||||||
## Sets the `s` length to `newlen` without zeroing memory on growth.
|
|
||||||
## Terminating zero for cstring compatibility is set.
|
|
||||||
var str = cast[NimString](s)
|
|
||||||
let n = max(newLen, 0)
|
|
||||||
if str == nil:
|
|
||||||
if n == 0: return
|
|
||||||
else:
|
|
||||||
str = rawNewStringNoInit(n)
|
|
||||||
str.data[n] = '\0'
|
|
||||||
str.len = n
|
|
||||||
s = cast[string](str)
|
|
||||||
else:
|
|
||||||
if n > str.space:
|
|
||||||
let sp = max(resize(str.space), n)
|
|
||||||
str = rawNewStringNoInit(sp)
|
|
||||||
copyMem(addr str.data[0], unsafeAddr s[0], s.len)
|
|
||||||
str.data[n] = '\0'
|
|
||||||
str.len = n
|
|
||||||
s = cast[string](str)
|
|
||||||
elif n != s.len:
|
|
||||||
str.data[n] = '\0'
|
|
||||||
str.len = n
|
|
||||||
else: return
|
|
||||||
|
|
||||||
# ----------------- sequences ----------------------------------------------
|
# ----------------- sequences ----------------------------------------------
|
||||||
|
|
||||||
proc incrSeq(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compilerproc.} =
|
proc incrSeq(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compilerproc.} =
|
||||||
@@ -299,22 +274,12 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.} =
|
|||||||
# since we steal the content from 's', it's crucial to set s's len to 0.
|
# since we steal the content from 's', it's crucial to set s's len to 0.
|
||||||
s.len = 0
|
s.len = 0
|
||||||
|
|
||||||
proc newSeqUninitRaw(typ: PNimType; len: int): pointer {.inline.} =
|
|
||||||
## Creates a sequence payload with capacity and length `len` without
|
|
||||||
## forcing zero-initialization for `ntfNoRefs` element types.
|
|
||||||
result = nimNewSeqOfCap(typ, len)
|
|
||||||
cast[PGenericSeq](result).len = len
|
|
||||||
|
|
||||||
proc extendCapacityRaw(src: PGenericSeq; typ: PNimType;
|
proc extendCapacityRaw(src: PGenericSeq; typ: PNimType;
|
||||||
elemSize, elemAlign, newLen: int;
|
elemSize, elemAlign, newLen: int): PGenericSeq {.inline.} =
|
||||||
doInit: static bool): PGenericSeq {.inline.} =
|
|
||||||
## Reallocs `src` to fit `newLen` elements without any checks.
|
## Reallocs `src` to fit `newLen` elements without any checks.
|
||||||
## Capacity always increases to at least next `resize` step.
|
## Capacity always increases to at least next `resize` step.
|
||||||
let newCap = max(resize(src.space), newLen)
|
let newCap = max(resize(src.space), newLen)
|
||||||
when doInit:
|
result = cast[PGenericSeq](newSeq(typ, newCap))
|
||||||
result = cast[PGenericSeq](newSeq(typ, newCap))
|
|
||||||
else:
|
|
||||||
result = cast[PGenericSeq](newSeqUninitRaw(typ, newCap))
|
|
||||||
copyMem(dataPointer(result, elemAlign), dataPointer(src, elemAlign), src.len * elemSize)
|
copyMem(dataPointer(result, elemAlign), dataPointer(src, elemAlign), src.len * elemSize)
|
||||||
# since we steal the content from 's', it's crucial to set s's len to 0.
|
# since we steal the content from 's', it's crucial to set s's len to 0.
|
||||||
src.len = 0
|
src.len = 0
|
||||||
@@ -345,19 +310,15 @@ proc truncateRaw(src: PGenericSeq; baseFlags: set[TNimTypeFlag]; isTrivial: bool
|
|||||||
((result.len-%newLen) *% elemSize))
|
((result.len-%newLen) *% elemSize))
|
||||||
|
|
||||||
template setLengthSeqImpl(s: PGenericSeq, typ: PNimType, newLen: int; isTrivial: bool;
|
template setLengthSeqImpl(s: PGenericSeq, typ: PNimType, newLen: int; isTrivial: bool;
|
||||||
doInit: static bool) =
|
doInit: static bool) =
|
||||||
if s == nil:
|
if s == nil:
|
||||||
if newLen == 0: return s
|
if newLen == 0: return s
|
||||||
else:
|
else: return cast[PGenericSeq](newSeq(typ, newLen)) # newSeq zeroes!
|
||||||
when doInit:
|
|
||||||
return cast[PGenericSeq](newSeq(typ, newLen)) # newSeq zeroes!
|
|
||||||
else:
|
|
||||||
return cast[PGenericSeq](newSeqUninitRaw(typ, newLen))
|
|
||||||
else:
|
else:
|
||||||
let elemSize = typ.base.size
|
let elemSize = typ.base.size
|
||||||
let elemAlign = typ.base.align
|
let elemAlign = typ.base.align
|
||||||
result = if newLen > s.space:
|
result = if newLen > s.space:
|
||||||
s.extendCapacityRaw(typ, elemSize, elemAlign, newLen, doInit)
|
s.extendCapacityRaw(typ, elemSize, elemAlign, newLen)
|
||||||
elif newLen < s.len:
|
elif newLen < s.len:
|
||||||
s.truncateRaw(typ.base.flags, isTrivial, elemSize, elemAlign, newLen)
|
s.truncateRaw(typ.base.flags, isTrivial, elemSize, elemAlign, newLen)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ architecture combinations:
|
|||||||
|--------------------------------|----------------------------------------|
|
|--------------------------------|----------------------------------------|
|
||||||
| Windows (Windows XP or greater) | x86 and x86_64 |
|
| Windows (Windows XP or greater) | x86 and x86_64 |
|
||||||
| Linux (most distributions) | x86, x86_64, ppc64, and armv6l |
|
| Linux (most distributions) | x86, x86_64, ppc64, and armv6l |
|
||||||
| Mac OS X (10.4 or greater) | x86, x86_64, ppc64, and Apple Silicon (ARM64) |
|
| Mac OS X (10.04 or greater) | x86, x86_64, ppc64, and Apple Silicon (ARM64) |
|
||||||
|
|
||||||
More platforms are supported, however, they are not tested regularly and they
|
More platforms are supported, however, they are not tested regularly and they
|
||||||
may not be as stable as the above-listed platforms.
|
may not be as stable as the above-listed platforms.
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
discard """
|
|
||||||
matrix: "--mm:orc"
|
|
||||||
output: '''
|
|
||||||
found entry
|
|
||||||
'''
|
|
||||||
"""
|
|
||||||
|
|
||||||
import std/tables
|
|
||||||
type NoCopies = object
|
|
||||||
|
|
||||||
proc `=copy`(a: var NoCopies, b: NoCopies) {.error.}
|
|
||||||
|
|
||||||
# bug #24720
|
|
||||||
proc foo() =
|
|
||||||
var t: Table[int, NoCopies]
|
|
||||||
t[3] = NoCopies() # only moves
|
|
||||||
for k, v in t.pairs(): # lent values, no need to copy!
|
|
||||||
echo "found entry"
|
|
||||||
|
|
||||||
foo()
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
discard """
|
|
||||||
cmd: '''nim c --mm:arc --expandArc:foo $file'''
|
|
||||||
nimout: '''
|
|
||||||
--expandArc: foo
|
|
||||||
|
|
||||||
var broken_cursor
|
|
||||||
block :tmp:
|
|
||||||
var i
|
|
||||||
var i_1 = 0
|
|
||||||
let L = len(seq[Large](broken_cursor))
|
|
||||||
block :tmp_1:
|
|
||||||
while i_1 < L:
|
|
||||||
i = seq[Large](broken_cursor)[i_1]
|
|
||||||
discard i
|
|
||||||
{.push, overflowChecks: false.}
|
|
||||||
inc(i_1, 1)
|
|
||||||
{.pop.}
|
|
||||||
-- end of expandArc ------------------------
|
|
||||||
'''
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
type
|
|
||||||
Large = array[1024, byte]
|
|
||||||
List = distinct seq[Large]
|
|
||||||
|
|
||||||
proc foo =
|
|
||||||
var
|
|
||||||
broken: List
|
|
||||||
for i in seq[Large](broken):
|
|
||||||
discard i
|
|
||||||
|
|
||||||
foo()
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
discard """
|
|
||||||
output: '''
|
|
||||||
246
|
|
||||||
246
|
|
||||||
'''
|
|
||||||
"""
|
|
||||||
|
|
||||||
# issue #25730
|
|
||||||
|
|
||||||
type
|
|
||||||
Inner[T] = object
|
|
||||||
x: T
|
|
||||||
Foo[T] = object
|
|
||||||
inner: Inner[T]
|
|
||||||
Bar[T] = object
|
|
||||||
foo: Foo[T]
|
|
||||||
|
|
||||||
proc `=sink`[T](a: var Inner[T], b: Inner[T]) {.nodestroy.} =
|
|
||||||
a.x = b.x * 2
|
|
||||||
|
|
||||||
proc `=copy`[T](a: var Inner[T], b: Inner[T]) {.nodestroy.} =
|
|
||||||
a.x = b.x * 2
|
|
||||||
|
|
||||||
when true:
|
|
||||||
proc `=sink`[T](a: var Bar[T], b: Bar[T]) {.nodestroy.} =
|
|
||||||
`=sink`(a.foo, b.foo)
|
|
||||||
|
|
||||||
proc `=copy`[T](a: var Bar[T], b: Bar[T]) {.nodestroy.} =
|
|
||||||
`=copy`(a.foo, b.foo)
|
|
||||||
|
|
||||||
proc useSink() =
|
|
||||||
let a = Bar[int](foo: Foo[int](inner: Inner[int](x: 123)))
|
|
||||||
var b: Bar[int]
|
|
||||||
`=sink`(b, a)
|
|
||||||
echo b.foo.inner.x
|
|
||||||
|
|
||||||
useSink()
|
|
||||||
|
|
||||||
proc useCopy() =
|
|
||||||
let a = Bar[int](foo: Foo[int](inner: Inner[int](x: 123)))
|
|
||||||
var b: Bar[int]
|
|
||||||
`=copy`(b, a)
|
|
||||||
echo b.foo.inner.x
|
|
||||||
|
|
||||||
useCopy()
|
|
||||||
@@ -130,14 +130,3 @@ block: # issue #22646
|
|||||||
var x: Vec[3, float]
|
var x: Vec[3, float]
|
||||||
let y = Color(x)
|
let y = Color(x)
|
||||||
doAssert Vec3[float](y) == x
|
doAssert Vec3[float](y) == x
|
||||||
|
|
||||||
block: # bug #25697
|
|
||||||
type MyList = distinct seq[int]
|
|
||||||
|
|
||||||
iterator items(x: MyList): lent int {.borrow.}
|
|
||||||
|
|
||||||
let s = MyList(@[1, 2, 3])
|
|
||||||
var count = 0
|
|
||||||
for item in s:
|
|
||||||
count += 1
|
|
||||||
doAssert count == 3, "Expected 3 items, got " & $count
|
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
discard """
|
|
||||||
cmd: "nim check --hints:off $file"
|
|
||||||
action: "reject"
|
|
||||||
nimout: '''
|
|
||||||
t25732.nim(15, 32) Error: undeclared identifier: 'a'
|
|
||||||
t25732.nim(15, 32) Error: expression 'a' has no type (or is ambiguous)
|
|
||||||
t25732.nim(15, 33) Error: undeclared field: 'b'
|
|
||||||
t25732.nim(15, 33) Error: undeclared field: '.'
|
|
||||||
t25732.nim(15, 33) Error: undeclared field: '.'
|
|
||||||
'''
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static: (for f in [0]: discard a.b == f)
|
|
||||||
@@ -7,10 +7,8 @@ discard """
|
|||||||
1.0
|
1.0
|
||||||
2.0
|
2.0
|
||||||
55
|
55
|
||||||
@[1, 2]
|
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
import std/strbasics
|
|
||||||
|
|
||||||
# Object variant / case object
|
# Object variant / case object
|
||||||
type
|
type
|
||||||
@@ -81,14 +79,3 @@ let x = compute:
|
|||||||
|
|
||||||
echo x
|
echo x
|
||||||
|
|
||||||
# Crash: bridge.nim(206, 5) `allowEmpty` unexpected nkEmpty [AssertionDefect]
|
|
||||||
# Bare closure iterator type alias
|
|
||||||
type IntIter = iterator(): int {.closure.}
|
|
||||||
proc run(it: IntIter): seq[int] =
|
|
||||||
result = @[]
|
|
||||||
for x in it():
|
|
||||||
result.add(x)
|
|
||||||
let gen: IntIter = iterator(): int {.closure.} =
|
|
||||||
yield 1
|
|
||||||
yield 2
|
|
||||||
echo run(gen)
|
|
||||||
|
|||||||
@@ -457,12 +457,3 @@ let runes1 = buggyVersion("en") # <-- CRASHES HERE
|
|||||||
|
|
||||||
doAssert runes1.len == runes2.len
|
doAssert runes1.len == runes2.len
|
||||||
# echo "Got ", runes1.len, " runes"
|
# echo "Got ", runes1.len, " runes"
|
||||||
|
|
||||||
|
|
||||||
block: # bug #25724
|
|
||||||
iterator c(): int =
|
|
||||||
when nimvm: yield 0
|
|
||||||
else: yield 1
|
|
||||||
for w in c():
|
|
||||||
let n = w
|
|
||||||
(proc() = discard n)()
|
|
||||||
@@ -4,12 +4,7 @@ js 3.14
|
|||||||
7
|
7
|
||||||
1
|
1
|
||||||
-21550
|
-21550
|
||||||
-21550
|
-21550'''
|
||||||
none(TT)
|
|
||||||
()
|
|
||||||
destroyed
|
|
||||||
destroyed
|
|
||||||
'''
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# This file tests the JavaScript generator
|
# This file tests the JavaScript generator
|
||||||
@@ -61,15 +56,3 @@ proc foo09() =
|
|||||||
const y = 86400
|
const y = 86400
|
||||||
echo (x - (y - 1)) div y # Still gives `-21551`
|
echo (x - (y - 1)) div y # Still gives `-21551`
|
||||||
foo09()
|
foo09()
|
||||||
|
|
||||||
import std/options
|
|
||||||
|
|
||||||
type TT = object
|
|
||||||
|
|
||||||
proc `=destroy`(x: TT) = echo "destroyed"
|
|
||||||
|
|
||||||
func test1: Option[TT] = discard
|
|
||||||
func test2: TT = discard
|
|
||||||
|
|
||||||
echo test1() # Crash in JS backend, not crash in C backend
|
|
||||||
echo test2() # Not crash
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
type
|
|
||||||
A* = object
|
|
||||||
discard
|
|
||||||
|
|
||||||
B* = object
|
|
||||||
discard
|
|
||||||
|
|
||||||
C* = A | B
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
discard """
|
|
||||||
action: "compile"
|
|
||||||
"""
|
|
||||||
import deps/cisaorb
|
|
||||||
|
|
||||||
when true:
|
|
||||||
# These work fine.
|
|
||||||
discard default(cisaorb.A)
|
|
||||||
proc f1(x: cisaorb.A) = discard
|
|
||||||
discard default(cisaorb.B)
|
|
||||||
proc f2(x: cisaorb.B) = discard
|
|
||||||
discard default(A)
|
|
||||||
proc f3(x: A) = discard
|
|
||||||
discard default(B)
|
|
||||||
proc f4(x: B) = discard
|
|
||||||
proc f5(x: C) = discard
|
|
||||||
proc f6(x: cisaorb.C | C) = discard
|
|
||||||
|
|
||||||
proc doesWork(x: A | B) = discard
|
|
||||||
|
|
||||||
# Doesn't compile.
|
|
||||||
proc f(x: cisaorb.C) = discard
|
|
||||||
@@ -31,9 +31,6 @@ cmdShortOption key: v value: ''
|
|||||||
cmdArgument key: ABC value: ''
|
cmdArgument key: ABC value: ''
|
||||||
cmdShortOption key: j value: '4'
|
cmdShortOption key: j value: '4'
|
||||||
cmdArgument key: ok value: ''
|
cmdArgument key: ok value: ''
|
||||||
parseopt stdin
|
|
||||||
cmdShortOption key: j value: '4'
|
|
||||||
cmdShortOption key: value: ''
|
|
||||||
'''
|
'''
|
||||||
joinable: false
|
joinable: false
|
||||||
"""
|
"""
|
||||||
@@ -157,9 +154,3 @@ arg 6 ai.len:4 :{a7'b}"""
|
|||||||
var n = parseopt.initOptParser("-j4 ok", shortnoVal = {'n'}, longnoVal = @["novalue"])
|
var n = parseopt.initOptParser("-j4 ok", shortnoVal = {'n'}, longnoVal = @["novalue"])
|
||||||
for kind, key, val in parseopt.getopt(n):
|
for kind, key, val in parseopt.getopt(n):
|
||||||
echo kind," key: ", key, " value: '", val, "'"
|
echo kind," key: ", key, " value: '", val, "'"
|
||||||
|
|
||||||
block: # fix #25738
|
|
||||||
echo "parseopt stdin"
|
|
||||||
var p = parseopt.initOptParser("-j4 -", shortNoVal = {'n'})
|
|
||||||
for kind, key, val in parseopt.getopt(p):
|
|
||||||
echo kind," key: ", key, " value: '", val, "'"
|
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
# issue #25627
|
|
||||||
|
|
||||||
import std/tables
|
|
||||||
|
|
||||||
type
|
|
||||||
FsoKind = enum
|
|
||||||
fsoFile
|
|
||||||
fsoDir
|
|
||||||
fsoLink
|
|
||||||
|
|
||||||
FakeFso = ref object
|
|
||||||
kind: FsoKind
|
|
||||||
dirName: string
|
|
||||||
files: OrderedTable[string, FakeFso]
|
|
||||||
|
|
||||||
DirStruct = object
|
|
||||||
root = FakeFso(kind: fsoDir, dirName: "/")
|
|
||||||
|
|
||||||
let dir = DirStruct()
|
|
||||||
doAssert dir.root.kind == fsoDir
|
|
||||||
doAssert dir.root.dirName == "/"
|
|
||||||
doAssert dir.root.files.len == 0
|
|
||||||
|
|
||||||
block:
|
|
||||||
type
|
|
||||||
Opt[T] = object
|
|
||||||
when T is ref:
|
|
||||||
val: T
|
|
||||||
x: int
|
|
||||||
else:
|
|
||||||
val: T
|
|
||||||
x: string
|
|
||||||
|
|
||||||
DefaultOpt = ref object
|
|
||||||
files: Opt[DefaultOpt]
|
|
||||||
|
|
||||||
OptDirStruct = object
|
|
||||||
root = DefaultOpt()
|
|
||||||
|
|
||||||
let dir = OptDirStruct()
|
|
||||||
doAssert dir.root.files.x is int
|
|
||||||
|
|
||||||
block:
|
|
||||||
type
|
|
||||||
Opt[T] = object
|
|
||||||
when T is ref:
|
|
||||||
x: int
|
|
||||||
else:
|
|
||||||
x: string
|
|
||||||
|
|
||||||
Foo[T] = object
|
|
||||||
x: Opt[T]
|
|
||||||
|
|
||||||
Nested = ref object
|
|
||||||
files: Foo[Nested]
|
|
||||||
|
|
||||||
let nested = Nested()
|
|
||||||
doAssert nested.files.x.x is int
|
|
||||||
|
|
||||||
block:
|
|
||||||
type
|
|
||||||
Foo[T] = object
|
|
||||||
x = sizeof(T)
|
|
||||||
|
|
||||||
Sized = ref object
|
|
||||||
files: Foo[Sized]
|
|
||||||
|
|
||||||
let sized = Sized()
|
|
||||||
doAssert sized.files.x == sizeof(Sized)
|
|
||||||
|
|
||||||
block:
|
|
||||||
type
|
|
||||||
Generic[T] = object
|
|
||||||
t: T
|
|
||||||
|
|
||||||
WindowObj = object
|
|
||||||
svgCache: Generic[SVGSVGElement]
|
|
||||||
|
|
||||||
SVGSVGElement = Generic[SVGSVGElementObj]
|
|
||||||
|
|
||||||
SVGSVGElementObj = object
|
|
||||||
|
|
||||||
proc foo() =
|
|
||||||
let p: pointer = nil
|
|
||||||
discard cast[ptr WindowObj](p)
|
|
||||||
|
|
||||||
foo()
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
proc temp(one: int, two: int, three: int) =
|
|
||||||
discard
|
|
||||||
|
|
||||||
template temp(body: untyped): untyped =
|
|
||||||
body
|
|
||||||
|
|
||||||
temp:
|
|
||||||
proc a(tp: int) =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc mixedTemp(x: int) =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc mixedTemp(x: bool) =
|
|
||||||
discard
|
|
||||||
|
|
||||||
template mixedTemp(body: untyped): untyped =
|
|
||||||
body
|
|
||||||
|
|
||||||
# The `bool` proc should win here so `xx` survives
|
|
||||||
mixedTemp (let xx = 1; true)
|
|
||||||
discard xx
|
|
||||||
|
|
||||||
proc sinkTemp(x: int) =
|
|
||||||
discard
|
|
||||||
|
|
||||||
template sinkTemp(body: untyped): untyped =
|
|
||||||
discard
|
|
||||||
|
|
||||||
# Here the template should win here so `let xy` is sunk into template as AST
|
|
||||||
sinkTemp (let xy = "template"; xy)
|
|
||||||
|
|
||||||
when declared(xy):
|
|
||||||
{.error: "xy leaked from failed proc candidate".}
|
|
||||||
@@ -549,20 +549,3 @@ block:
|
|||||||
|
|
||||||
type X {.p.} = object
|
type X {.p.} = object
|
||||||
doAssert foo(X())
|
doAssert foo(X())
|
||||||
|
|
||||||
block: # typeof() type alias preserves field pragmas
|
|
||||||
template myFieldPragma {.pragma.}
|
|
||||||
type Orig = object
|
|
||||||
x {.myFieldPragma.}: int
|
|
||||||
|
|
||||||
var orig: Orig
|
|
||||||
|
|
||||||
# Direct typeof alias
|
|
||||||
type TAlias = typeof(orig)
|
|
||||||
var a: TAlias
|
|
||||||
doAssert a.x.hasCustomPragma(myFieldPragma)
|
|
||||||
|
|
||||||
# Indirect alias of typeof alias
|
|
||||||
type TAlias2 = TAlias
|
|
||||||
var b: TAlias2
|
|
||||||
doAssert b.x.hasCustomPragma(myFieldPragma)
|
|
||||||
|
|||||||
@@ -1,196 +0,0 @@
|
|||||||
import std/[assertions, options, sequtils, strutils, tables]
|
|
||||||
import std/nre2
|
|
||||||
|
|
||||||
block:
|
|
||||||
let pattern = "[0-9"
|
|
||||||
doAssertRaises(RegexError): discard re(pattern)
|
|
||||||
|
|
||||||
block: # captures
|
|
||||||
block: # capture bounds are correct
|
|
||||||
let ex1 = re("([0-9])")
|
|
||||||
doAssert "1 23".find(ex1).get.matchBounds == 0 .. 0
|
|
||||||
doAssert "1 23".find(ex1).get.captureBounds[0] == 0 .. 0
|
|
||||||
doAssert "1 23".find(ex1, 1).get.matchBounds == 2 .. 2
|
|
||||||
doAssert "1 23".find(ex1, 3).get.matchBounds == 3 .. 3
|
|
||||||
|
|
||||||
let ex2 = re("()()()()()()()()()()([0-9])")
|
|
||||||
doAssert "824".find(ex2).get.captureBounds[0] == 0 .. -1
|
|
||||||
doAssert "824".find(ex2).get.captureBounds[10] == 0 .. 0
|
|
||||||
|
|
||||||
let ex3 = re("([0-9]+)")
|
|
||||||
doAssert "824".find(ex3).get.captureBounds[0] == 0 .. 2
|
|
||||||
|
|
||||||
block: # named captures
|
|
||||||
let ex1 = "foobar".find(re("(?P<foo>foo)(?P<bar>bar)"))
|
|
||||||
doAssert ex1.get.captures["foo"] == "foo"
|
|
||||||
doAssert ex1.get.captures["bar"] == "bar"
|
|
||||||
|
|
||||||
let ex2 = "foo".find(re("(?P<foo>foo)(?P<bar>bar)?"))
|
|
||||||
doAssert "foo" in ex2.get.captureBounds
|
|
||||||
doAssert ex2.get.captures["foo"] == "foo"
|
|
||||||
doAssert not ("bar" in ex2.get.captures)
|
|
||||||
doAssertRaises(KeyError):
|
|
||||||
discard ex2.get.captures["bar"]
|
|
||||||
|
|
||||||
block: # named capture bounds
|
|
||||||
let ex1 = "foo".find(re("(?P<foo>foo)(?P<bar>bar)?"))
|
|
||||||
doAssert "foo" in ex1.get.captureBounds
|
|
||||||
doAssert ex1.get.captureBounds["foo"] == 0..2
|
|
||||||
doAssert not ("bar" in ex1.get.captures)
|
|
||||||
doAssertRaises(KeyError):
|
|
||||||
discard ex1.get.captureBounds["bar"]
|
|
||||||
|
|
||||||
block: # capture count
|
|
||||||
let ex1 = re("(?P<foo>foo)(?P<bar>bar)?")
|
|
||||||
doAssert ex1.captureCount == 2
|
|
||||||
doAssert ex1.captureNameId == {"foo" : 0, "bar" : 1}.toTable()
|
|
||||||
|
|
||||||
block: # named capture table
|
|
||||||
let ex1 = "foo".find(re("(?P<foo>foo)(?P<bar>bar)?"))
|
|
||||||
doAssert ex1.get.captures.toTable == {"foo" : "foo"}.toTable()
|
|
||||||
doAssert ex1.get.captureBounds.toTable == {"foo" : 0..2}.toTable()
|
|
||||||
|
|
||||||
let ex2 = "foobar".find(re("(?P<foo>foo)(?P<bar>bar)?"))
|
|
||||||
doAssert ex2.get.captures.toTable == {"foo" : "foo", "bar" : "bar"}.toTable()
|
|
||||||
|
|
||||||
block: # capture sequence
|
|
||||||
let ex1 = "foo".find(re("(?P<foo>foo)(?P<bar>bar)?"))
|
|
||||||
doAssert ex1.get.captures.toSeq == @[some("foo"), none(string)]
|
|
||||||
doAssert ex1.get.captureBounds.toSeq == @[some(0..2), none(Slice[int])]
|
|
||||||
doAssert ex1.get.captures.toSeq(some("")) == @[some("foo"), some("")]
|
|
||||||
|
|
||||||
let ex2 = "foobar".find(re("(?P<foo>foo)(?P<bar>bar)?"))
|
|
||||||
doAssert ex2.get.captures.toSeq == @[some("foo"), some("bar")]
|
|
||||||
|
|
||||||
block: # match
|
|
||||||
block: # upper bound must be inclusive
|
|
||||||
doAssert "abc".match(re"abc", endpos = -1) == none(RegexMatch)
|
|
||||||
doAssert "abc".match(re"abc", endpos = 1) == none(RegexMatch)
|
|
||||||
doAssert "abc".match(re"abc", endpos = 2) != none(RegexMatch)
|
|
||||||
|
|
||||||
block: # match examples
|
|
||||||
doAssert "abc".match(re"(\w)").get.captures[0] == "a"
|
|
||||||
doAssert "abc".match(re"(?P<letter>\w)").get.captures["letter"] == "a"
|
|
||||||
doAssert "abc".match(re"(\w)\w").get.captures[-1] == "ab"
|
|
||||||
doAssert "abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0
|
|
||||||
doAssert "abc".match(re"").get.captureBounds[-1] == 0 .. -1
|
|
||||||
doAssert "abc".match(re"abc").get.captureBounds[-1] == 0 .. 2
|
|
||||||
|
|
||||||
let cap1 = "abc".match(re"(\w)(\w)+").get.captures
|
|
||||||
doAssert cap1.len == 2
|
|
||||||
doAssert 0 in cap1
|
|
||||||
doAssert 1 in cap1
|
|
||||||
doAssert cap1[0] == "a" and cap1[1] == "c"
|
|
||||||
doAssert 0 in "abc".match(re"(\w)+").get.captureBounds
|
|
||||||
|
|
||||||
block: # match test cases
|
|
||||||
doAssert "123".match(re"").get.matchBounds == 0 .. -1
|
|
||||||
let mat1 = "123".match(re"123").get
|
|
||||||
doAssert mat1.matchBounds == 0 .. 2
|
|
||||||
doAssert mat1.match == "123"
|
|
||||||
|
|
||||||
block: # find
|
|
||||||
block: # find text
|
|
||||||
doAssert "3213a".find(re"[a-z]").get.match == "a"
|
|
||||||
doAssert sequtils.toSeq(findIter("1 2 3 4 5 6 7 8 ", re" ")).mapIt(
|
|
||||||
it.match
|
|
||||||
) == @[" ", " ", " ", " ", " ", " ", " ", " "]
|
|
||||||
|
|
||||||
block: # find bounds
|
|
||||||
doAssert sequtils.toSeq(findIter("1 2 3 4 5 ", re" ")).mapIt(
|
|
||||||
it.matchBounds
|
|
||||||
) == @[1..1, 3..3, 5..5, 7..7, 9..9]
|
|
||||||
|
|
||||||
block: # overlapping find
|
|
||||||
doAssert "222".findAll(re"22") == @["22"]
|
|
||||||
doAssert "2222".findAll(re"22") == @["22", "22"]
|
|
||||||
|
|
||||||
block: # len 0 find
|
|
||||||
doAssert "".findAll(re"\ ") == newSeq[string]()
|
|
||||||
doAssert "".findAll(re"") == @[""]
|
|
||||||
doAssert "abc".findAll(re"") == @["", "", "", ""]
|
|
||||||
doAssert "word word".findAll(re"\b") == @["", "", "", ""]
|
|
||||||
doAssert "word\r\lword".findAll(re"(?m)$") == @["", ""]
|
|
||||||
doAssert "слово слово".findAll(re"\b") == @["", "", "", ""]
|
|
||||||
|
|
||||||
block: # contains
|
|
||||||
doAssert "abc".contains(re"bc")
|
|
||||||
doAssert not "abc".contains(re"cd")
|
|
||||||
doAssert not "abc".contains(re"a", start = 1)
|
|
||||||
|
|
||||||
block: # string splitting
|
|
||||||
block: # splitting strings
|
|
||||||
doAssert "1 2 3 4 5 6 ".split(re" ") == @["1", "2", "3", "4", "5", "6", ""]
|
|
||||||
doAssert "1 2 ".split(re(" ")) == @["1", "", "2", "", ""]
|
|
||||||
doAssert "1 2".split(re(" ")) == @["1", "2"]
|
|
||||||
doAssert "foo".split(re("foo")) == @["", ""]
|
|
||||||
doAssert "".split(re"foo") == @[""]
|
|
||||||
doAssert "9".split(re"\son\s") == @["9"]
|
|
||||||
|
|
||||||
block: # captured patterns
|
|
||||||
doAssert "12".split(re"(\d)") == @["", "1", "", "2", ""]
|
|
||||||
|
|
||||||
block: # maxsplit
|
|
||||||
doAssert "123".split(re"", maxsplit = 2) == @["1", "23"]
|
|
||||||
doAssert "123".split(re"", maxsplit = 1) == @["123"]
|
|
||||||
doAssert "123".split(re"", maxsplit = -1) == @["1", "2", "3"]
|
|
||||||
doAssert "1 2 3".split(re" ", maxsplit = 1) == @["1 2 3"]
|
|
||||||
doAssert "1 2 3".split(re" ", maxsplit = 2) == @["1", "2 3"]
|
|
||||||
doAssert "1 2 3".split(re"( )", maxsplit = 2) == @["1", " ", "2 3"]
|
|
||||||
|
|
||||||
block: # split with 0-length match
|
|
||||||
doAssert "12345".split(re("")) == @["1", "2", "3", "4", "5"]
|
|
||||||
doAssert "".split(re"") == newSeq[string]()
|
|
||||||
doAssert "word word".split(re"\b") == @["word", " ", "word"]
|
|
||||||
#doAssert "word\r\lword".split(re"(?m)$") == @["word", "\r\lword"]
|
|
||||||
doAssert "слово слово".split(re"(\b)") == @["слово", "", " ", "", "слово", ""]
|
|
||||||
|
|
||||||
block: # perl split tests
|
|
||||||
doAssert "forty-two" .split(re"") .join(",") == "f,o,r,t,y,-,t,w,o"
|
|
||||||
doAssert "forty-two" .split(re"", 3) .join(",") == "f,o,rty-two"
|
|
||||||
doAssert "split this string" .split(re" ") .join(",") == "split,this,string"
|
|
||||||
doAssert "split this string" .split(re" ", 2) .join(",") == "split,this string"
|
|
||||||
doAssert "try$this$string" .split(re"\$") .join(",") == "try,this,string"
|
|
||||||
doAssert "try$this$string" .split(re"\$", 2) .join(",") == "try,this$string"
|
|
||||||
doAssert "comma, separated, values" .split(re", ") .join("|") == "comma|separated|values"
|
|
||||||
doAssert "comma, separated, values" .split(re", ", 2) .join("|") == "comma|separated, values"
|
|
||||||
doAssert "Perl6::Camelia::Test" .split(re"::") .join(",") == "Perl6,Camelia,Test"
|
|
||||||
doAssert "Perl6::Camelia::Test" .split(re"::", 2) .join(",") == "Perl6,Camelia::Test"
|
|
||||||
doAssert "split,me,please" .split(re",") .join("|") == "split|me|please"
|
|
||||||
doAssert "split,me,please" .split(re",", 2) .join("|") == "split|me,please"
|
|
||||||
doAssert "Hello World Goodbye Mars".split(re"\s+") .join(",") == "Hello,World,Goodbye,Mars"
|
|
||||||
doAssert "Hello World Goodbye Mars".split(re"\s+", 3).join(",") == "Hello,World,Goodbye Mars"
|
|
||||||
doAssert "Hello test" .split(re"(\s+)") .join(",") == "Hello, ,test"
|
|
||||||
doAssert "this will be split" .split(re" ") .join(",") == "this,will,be,split"
|
|
||||||
doAssert "this will be split" .split(re" ", 3) .join(",") == "this,will,be split"
|
|
||||||
doAssert "a.b" .split(re"\.") .join(",") == "a,b"
|
|
||||||
doAssert "" .split(re"") .len == 0
|
|
||||||
doAssert ":" .split(re"") .len == 1
|
|
||||||
|
|
||||||
block: # start position
|
|
||||||
doAssert "abc".split(re"", start = 1) == @["b", "c"]
|
|
||||||
doAssert "abc".split(re"", start = 2) == @["c"]
|
|
||||||
doAssert "abc".split(re"", start = 3) == newSeq[string]()
|
|
||||||
doAssert "abc".split(re"^b", start = 1) == @["bc"]
|
|
||||||
|
|
||||||
block: # replace
|
|
||||||
block: # replace with 0-length strings
|
|
||||||
doAssert "".replace(re"1", proc (v: RegexMatch): string = "1") == ""
|
|
||||||
doAssert " ".replace(re"", proc (v: RegexMatch): string = "1") == "1 1"
|
|
||||||
doAssert "".replace(re"", proc (v: RegexMatch): string = "1") == "1"
|
|
||||||
|
|
||||||
block: # regular replace
|
|
||||||
doAssert "123".replace(re"\d", "foo") == "foofoofoo"
|
|
||||||
doAssert "123".replace(re"(\d)", "$1$1") == "112233"
|
|
||||||
doAssert "123".replace(re"(\d)(\d)", "$1$2") == "123"
|
|
||||||
doAssert "123".replace(re"(\d)(\d)", "$#$#") == "123"
|
|
||||||
doAssert "abcdefghijklm".replace(re"(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)(m)", "$12") == "l"
|
|
||||||
|
|
||||||
block: # replacing missing captures should throw instead of segfaulting
|
|
||||||
doAssertRaises(ValueError): discard "ab".replace(re"(a)", "$1$2")
|
|
||||||
|
|
||||||
block: # escape strings
|
|
||||||
block: # escape strings
|
|
||||||
doAssert "123".escapeRe() == "123"
|
|
||||||
doAssert "[]".escapeRe() == r"\[\]"
|
|
||||||
doAssert "()".escapeRe() == r"\(\)"
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# std/nre2 requires nim-regex and it requires nim-unicodedb
|
|
||||||
exec("nimble --nimbleDir:build/deps install unicodedb@#head")
|
|
||||||
exec("nimble --nimbleDir:build/deps install regex@#head")
|
|
||||||
@@ -350,12 +350,3 @@ else:
|
|||||||
discard"""
|
discard"""
|
||||||
|
|
||||||
a()
|
a()
|
||||||
|
|
||||||
# bug: form feed character in comment should not hang renderTree
|
|
||||||
macro formfeedComment(): untyped =
|
|
||||||
result = newNimNode(nnkStmtList)
|
|
||||||
var c = newNimNode(nnkCommentStmt)
|
|
||||||
c.strVal = "hello\x0Cworld"
|
|
||||||
result.add c
|
|
||||||
|
|
||||||
formfeedComment()
|
|
||||||
|
|||||||
@@ -1,24 +1,20 @@
|
|||||||
discard """
|
discard """
|
||||||
matrix: "--backend:c --mm:refc; --backend:c --mm:orc; --backend:c --mm:orc --strings:sso; --backend:cpp --mm:refc; --backend:cpp --mm:orc; --backend:js --mm:refc; --backend:js --mm:orc"
|
matrix: "--mm:refc; --mm:orc"
|
||||||
|
targets: "c cpp js"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from std/sequtils import toSeq, map
|
from std/sequtils import toSeq, map
|
||||||
from std/sugar import `=>`
|
from std/sugar import `=>`
|
||||||
import std/assertions
|
import std/assertions
|
||||||
|
|
||||||
const hasNativeSso = defined(nimsso) and
|
|
||||||
(defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc) or defined(gcYrc))
|
|
||||||
|
|
||||||
proc tester[T](x: T) =
|
proc tester[T](x: T) =
|
||||||
let test = toSeq(0..4).map(i => newSeq[int]())
|
let test = toSeq(0..4).map(i => newSeq[int]())
|
||||||
doAssert $test == "@[@[], @[], @[], @[], @[]]"
|
doAssert $test == "@[@[], @[], @[], @[], @[]]"
|
||||||
|
|
||||||
when not hasNativeSso:
|
func reverse*(a: string): string =
|
||||||
func reverse*(a: string): string =
|
result = a
|
||||||
result = a
|
for i in 0 ..< a.len div 2:
|
||||||
for i in 0 ..< a.len div 2:
|
swap(result[i], result[^(i + 1)])
|
||||||
let j = result.len - i - 1
|
|
||||||
swap(result[i], result[j])
|
|
||||||
|
|
||||||
proc main() =
|
proc main() =
|
||||||
block: # ..
|
block: # ..
|
||||||
@@ -98,164 +94,31 @@ proc main() =
|
|||||||
block: # bug #7816
|
block: # bug #7816
|
||||||
tester(1)
|
tester(1)
|
||||||
|
|
||||||
when not hasNativeSso:
|
block: # bug #14497, reverse
|
||||||
block: # bug #14497, reverse
|
doAssert reverse("hello") == "olleh"
|
||||||
doAssert reverse("hello") == "olleh"
|
|
||||||
|
|
||||||
block: # len, high
|
block: # len, high
|
||||||
var a = "ab\0cd"
|
var a = "ab\0cd"
|
||||||
|
var b = a.cstring
|
||||||
doAssert a.len == 5
|
doAssert a.len == 5
|
||||||
doAssert a.high == a.len - 1
|
block: # bug #16405
|
||||||
|
when defined(js):
|
||||||
|
when nimvm: doAssert b.len == 2
|
||||||
|
else: doAssert b.len == 5
|
||||||
|
else: doAssert b.len == 2
|
||||||
|
|
||||||
when not (hasNativeSso and defined(cpp)):
|
doAssert a.high == a.len - 1
|
||||||
let b = a.cstring
|
doAssert b.high == b.len - 1
|
||||||
block: # bug #16405
|
|
||||||
when defined(js):
|
|
||||||
when nimvm: doAssert b.len == 2
|
|
||||||
else: doAssert b.len == 5
|
|
||||||
else: doAssert b.len == 2
|
|
||||||
doAssert b.high == b.len - 1
|
|
||||||
|
|
||||||
doAssert "".len == 0
|
doAssert "".len == 0
|
||||||
doAssert "".high == -1
|
doAssert "".high == -1
|
||||||
when not (hasNativeSso and defined(cpp)):
|
doAssert "".cstring.len == 0
|
||||||
doAssert "".cstring.len == 0
|
doAssert "".cstring.high == -1
|
||||||
doAssert "".cstring.high == -1
|
|
||||||
|
|
||||||
block: # bug #16674
|
block: # bug #16674
|
||||||
var c: cstring = nil
|
var c: cstring = nil
|
||||||
doAssert c.len == 0
|
doAssert c.len == 0
|
||||||
doAssert c.high == -1
|
doAssert c.high == -1
|
||||||
|
|
||||||
block: # setLen, setLenUninit
|
|
||||||
when hasNativeSso:
|
|
||||||
const
|
|
||||||
alwaysAvail = sizeof(uint) - 1
|
|
||||||
payloadSize = sizeof(uint) + sizeof(pointer) - 2
|
|
||||||
longStringDataOffset = 3 * sizeof(int)
|
|
||||||
|
|
||||||
template rawSlenOf(s: string): int =
|
|
||||||
int(cast[ptr byte](unsafeAddr s)[])
|
|
||||||
|
|
||||||
template inlineDataOf(s: string): ptr UncheckedArray[char] =
|
|
||||||
cast[ptr UncheckedArray[char]](cast[uint](unsafeAddr s) + 1'u)
|
|
||||||
|
|
||||||
template longDataOf(s: string): ptr UncheckedArray[char] =
|
|
||||||
let ssPtr = cast[ptr tuple[bytes: uint, more: pointer]](unsafeAddr s)
|
|
||||||
cast[ptr UncheckedArray[char]](
|
|
||||||
cast[uint](ssPtr.more) + uint(longStringDataOffset))
|
|
||||||
|
|
||||||
proc checkStrInternals(s: string; expectedLen: int) =
|
|
||||||
doAssert s.len == expectedLen, "expected " & $expectedLen & ", got " & $s.len
|
|
||||||
when nimvm:
|
|
||||||
discard
|
|
||||||
else:
|
|
||||||
when hasNativeSso and not defined(js) and not defined(nimscript):
|
|
||||||
# SSO
|
|
||||||
let rawSlen = rawSlenOf(s)
|
|
||||||
if rawSlen > payloadSize:
|
|
||||||
doAssert rawSlen == 255
|
|
||||||
let data = longDataOf(s)
|
|
||||||
doAssert data[expectedLen] == '\0'
|
|
||||||
else:
|
|
||||||
doAssert rawSlen == expectedLen
|
|
||||||
let data = inlineDataOf(s)
|
|
||||||
doAssert data[expectedLen] == '\0'
|
|
||||||
if expectedLen < alwaysAvail:
|
|
||||||
for i in expectedLen + 1 ..< alwaysAvail:
|
|
||||||
doAssert data[i] == '\0'
|
|
||||||
elif defined(UncheckedArray): # skip JS
|
|
||||||
# string V2
|
|
||||||
let cs = s.cstring
|
|
||||||
let arr = cast[ptr UncheckedArray[char]](unsafeAddr cs[0])
|
|
||||||
doAssert arr[expectedLen] == '\0'
|
|
||||||
|
|
||||||
proc makeStr(n: int): string =
|
|
||||||
result = newStringOfCap(n)
|
|
||||||
for i in 0..<n:
|
|
||||||
result.add char(ord('a') + i mod 26)
|
|
||||||
|
|
||||||
proc checkSetLenUninit(oldLen, newLen: int; cmpAfter = -1) =
|
|
||||||
## Verifies `setLenUninit`:
|
|
||||||
## - preserves the existing prefix
|
|
||||||
## - updates the string length
|
|
||||||
## - keeps internal null termination valid for both shrink and growth
|
|
||||||
##
|
|
||||||
## `cmpAfter` is used for layouts where trailing zeroed padding affects
|
|
||||||
## string comparison semantics after the resize.
|
|
||||||
var s = makeStr(oldLen)
|
|
||||||
let prefixLen = min(oldLen, newLen)
|
|
||||||
let prefix = makeStr(prefixLen)
|
|
||||||
s.setLenUninit(newLen)
|
|
||||||
s.checkStrInternals(newLen)
|
|
||||||
doAssert s[0..<prefixLen] == prefix
|
|
||||||
if newLen <= oldLen:
|
|
||||||
doAssert s == prefix
|
|
||||||
if cmpAfter >= 0:
|
|
||||||
doAssert s < makeStr(cmpAfter)
|
|
||||||
|
|
||||||
const numbers = "1234567890"
|
|
||||||
block setLen:
|
|
||||||
# Trim to zero and grow past the old end. Must keep the prefix and zero the tail.
|
|
||||||
var s = numbers
|
|
||||||
s.setLen(0)
|
|
||||||
s.checkStrInternals(0)
|
|
||||||
doAssert s == ""
|
|
||||||
|
|
||||||
s = numbers
|
|
||||||
s.setLen(numbers.len + 1)
|
|
||||||
s.checkStrInternals(numbers.len + 1)
|
|
||||||
doAssert s[0..numbers.high] == numbers
|
|
||||||
doAssert s[numbers.len] == '\0'
|
|
||||||
|
|
||||||
block setLenUninit:
|
|
||||||
# Shared baseline for both SSO and V2: noop, shrink, grow.
|
|
||||||
checkSetLenUninit(10, 10)
|
|
||||||
checkSetLenUninit(10, 5)
|
|
||||||
checkSetLenUninit(10, 11)
|
|
||||||
|
|
||||||
block growingWithinBiggerCapacity:
|
|
||||||
# Strings can reserve spare capacity even for short strings.
|
|
||||||
# Growing within that capacity must still update len and the trailing zero.
|
|
||||||
var s = newStringOfCap(10)
|
|
||||||
s.add("abc")
|
|
||||||
s.setLenUninit(6)
|
|
||||||
s.checkStrInternals(6)
|
|
||||||
doAssert s[0..2] == "abc"
|
|
||||||
|
|
||||||
when hasNativeSso:
|
|
||||||
const
|
|
||||||
shortLen = alwaysAvail
|
|
||||||
medLen = payloadSize
|
|
||||||
longLen = payloadSize + 8
|
|
||||||
|
|
||||||
# Staying short and verify short-compare padding after shrink.
|
|
||||||
checkSetLenUninit(shortLen, shortLen - 1, shortLen)
|
|
||||||
checkSetLenUninit(shortLen - 2, shortLen - 1)
|
|
||||||
checkSetLenUninit(shortLen, 0)
|
|
||||||
|
|
||||||
# Cross the short/medium boundary in both directions.
|
|
||||||
checkSetLenUninit(medLen, medLen - 1)
|
|
||||||
checkSetLenUninit(medLen, alwaysAvail - 1, alwaysAvail)
|
|
||||||
checkSetLenUninit(alwaysAvail, medLen)
|
|
||||||
|
|
||||||
# Cross the inline/long boundary in both directions and cover long growth.
|
|
||||||
checkSetLenUninit(longLen, longLen - 2)
|
|
||||||
checkSetLenUninit(longLen, medLen - 1)
|
|
||||||
checkSetLenUninit(longLen, alwaysAvail - 1, alwaysAvail)
|
|
||||||
checkSetLenUninit(medLen, longLen)
|
|
||||||
checkSetLenUninit(longLen, longLen + 10)
|
|
||||||
checkSetLenUninit(longLen, 0)
|
|
||||||
|
|
||||||
when not defined(js) and not defined(nimscript):
|
|
||||||
# shared long strings must not mutate the original when grown
|
|
||||||
let src = makeStr(longLen)
|
|
||||||
var orig = src
|
|
||||||
var copy = orig
|
|
||||||
copy.setLenUninit(longLen + 4)
|
|
||||||
copy.checkStrInternals(longLen + 4)
|
|
||||||
doAssert orig == src
|
|
||||||
doAssert copy[0..<longLen] == src
|
|
||||||
|
|
||||||
static: main()
|
static: main()
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
discard """
|
|
||||||
matrix: "--strings:sso --mm:orc"
|
|
||||||
targets: "c cpp"
|
|
||||||
"""
|
|
||||||
|
|
||||||
var s = "abc"
|
|
||||||
discard s.cstring
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
discard """
|
|
||||||
timeout: "1.0"
|
|
||||||
"""
|
|
||||||
|
|
||||||
type
|
|
||||||
Generic[T] = object
|
|
||||||
t: T
|
|
||||||
|
|
||||||
A = Generic[B]
|
|
||||||
B = Generic[A]
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
discard """
|
|
||||||
errormsg: "set is too large; use `std/sets` for ordinal types with more than 2^16 elements"
|
|
||||||
"""
|
|
||||||
|
|
||||||
type
|
|
||||||
Foo = set[Bar]
|
|
||||||
Bar = int32
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
discard """
|
|
||||||
errormsg: "set is too large; use `std/sets` for ordinal types with more than 2^16 elements"
|
|
||||||
"""
|
|
||||||
|
|
||||||
type
|
|
||||||
Foo = int32
|
|
||||||
Bar = set[Baz]
|
|
||||||
Baz = Foo
|
|
||||||
Reference in New Issue
Block a user