Merge pull request #1 from nim-lang/devel

Getting the latest from nim-lang
This commit is contained in:
Dean Thompson
2019-01-21 16:40:04 -05:00
committed by GitHub
285 changed files with 10804 additions and 6931 deletions

View File

@@ -1,37 +1,32 @@
---
name: Bug report
about: You found an unexpected behaviour? Use this template.
about: Have you found an unexpected behavior? Use this template.
---
<!-- Think about the title, twice. -->
<!-- Summarize the Problem here, keep it simple. -->
<!-- e.g. `echo` outputs the wrong string. -->
<!-- Summarize the problem here, keep it short and simple. -->
Function `echo` outputs the wrong string.
### Example
<!-- This should be a source code block.
<!-- Paste your example in the code-block below. -->
```nim
echo "Hello World!"
```
-->
### Current Output
<!--
```
Hola mundo!
```
-->
### Expected Output
<!--
<!-- What should be the correct output? -->
```
Hello World!
```
-->
### Possible Solution
@@ -40,6 +35,11 @@ Hello World!
### Additional Information
<!--- For Example:
A link to a project where the issue is relevant.
A link to a related issue or discussion.
* Your Nim version (output of `nim -v`).
* Was it working in the previous Nim releases?
* A link to a related issue or discussion.
-->
```
$ nim -v
Nim Compiler Version 0.1.2
```

View File

@@ -1,9 +1,18 @@
---
name: Feature request
about: You want to suggest a new feature? Use this template.
about: Do you want to suggest a new feature? Use this template.
---
<!---
Notice there is now a separate repository for the detailed RFCs and proposals:
https://github.com/nim-lang/RFCs
If you have a simple feature request, you can post it here using this template,
but bear in mind that adding new features to the language is currently a low priority.
-->
### Summary
<!--- Short summary of your proposed feature -->
@@ -18,6 +27,6 @@ about: You want to suggest a new feature? Use this template.
### Additional Information
<!--- For Example:
A link to a project where the issue is relevant.
A link to a related issue or discussion.
-->
* A link to a project where the issue is relevant.
* A link to a related issue or discussion.
-->

6
.gitignore vendored
View File

@@ -32,7 +32,7 @@ doc/*.html
doc/*.pdf
doc/*.idx
/web/upload
build/*
/build/*
bin/*
# iOS specific wildcards.
@@ -71,3 +71,7 @@ test.txt
tweeter.db
tweeter_test.db
megatest.nim
/outputExpected.txt
/outputGotten.txt

View File

@@ -14,8 +14,9 @@ matrix:
- os: osx
env: NIM_COMPILE_TO_CPP=true
allow_failures:
- env: NIM_COMPILE_TO_CPP=true
# To allow failures for a failing configuration, use something like:
# allow_failures:
# - env: NIM_COMPILE_TO_CPP=true
# - os: osx
addons:
@@ -40,26 +41,11 @@ before_script:
- sh build.sh
- cd ..
- export PATH=$(pwd)/bin${PATH:+:$PATH}
- echo PATH:${PATH}
script:
- nim c koch
- env NIM_COMPILE_TO_CPP=false ./koch boot
- ./koch boot -d:release
- ./koch nimble
- nim e tests/test_nimscript.nims
#- nimble install zip -y
#- nimble install opengl
#- nimble install sdl1
#- nimble install jester@#head -y
#- nimble install niminst
- nim c -d:nimCoroutines testament/tester
- testament/tester --pedantic all -d:nimCoroutines
- nim c -o:bin/nimpretty nimpretty/nimpretty.nim
- nim c -r nimpretty/tester.nim
- ./koch docs --git.commit:devel
- ./koch csource
- ./koch nimsuggest
- nim c -r nimsuggest/tester
- nim c -r nimdoc/tester
- ./koch runCI
before_deploy:
# Make https://nim-lang.github.io/Nim work the same as https://nim-lang.github.io/Nim/overview.html

View File

@@ -44,22 +44,6 @@ install:
build_script:
- bin\nim c koch
- koch boot -d:release
- koch nimble
- nim e tests/test_nimscript.nims
- nim c -o:bin/nimpretty.exe nimpretty/nimpretty.nim
- nim c -r nimpretty/tester.nim
# - nimble install zip -y
# - nimble install opengl -y
# - nimble install sdl1 -y
# - nimble install jester@#head -y
- nim c -d:nimCoroutines --os:genode -d:posix --compileOnly testament/tester
- nim c -d:nimCoroutines testament/tester
test_script:
- testament\tester --pedantic all -d:nimCoroutines
- nim c -r nimdoc\tester
# - koch csource
# - koch zip
- koch runCI
deploy: off

View File

@@ -26,6 +26,9 @@ build_nim_csources(){
[ -f $nim_csources ] || echo_run build_nim_csources
echo_run bin/nim c koch
# Note: if fails, may need to `cd csources && git pull`
# see D20190115T162028
echo_run bin/nim c --skipUserCfg --skipParentCfg koch
echo_run ./koch boot -d:release
echo_run ./koch tools # Compile Nimble and other tools.

View File

@@ -1,4 +1,5 @@
## v0.20.0 - XX/XX/2018
## v0.20.0 - XX/XX/2019
### Changes affecting backwards compatibility
@@ -23,13 +24,19 @@
- The undocumented ``#? strongSpaces`` parsing mode has been removed.
- The `not` operator is now always a unary operator, this means that code like
``assert not isFalse(3)`` compiles.
- `getImpl` on a `var` or `let` symbol will now return the full `IdentDefs`
tree from the symbol declaration instead of just the initializer portion.
#### Breaking changes in the standard library
- `osproc.execProcess` now also takes a `workingDir` parameter.
- `options.UnpackError` is no longer a ref type and inherits from `System.Defect` instead of `System.ValueError`.
- `options.UnpackError` is no longer a ref type and inherits from `system.Defect` instead of `system.ValueError`.
- `system.ValueError` now inherits from `system.CatchableError` instead of `system.Defect`.
- The procs `parseutils.parseBiggsetInt`, `parseutils.parseInt`, `parseutils.parseBiggestUInt` and `parseutils.parseUInt` now raise a `ValueError` when the parsed integer is outside of the valid range. Previously they sometimes raised a `OverflowError` and sometimes returned `0`.
- nre's `RegexMatch.{captureBounds,captures}[]` no longer return `Option` or
`nil`/`""`, respectivly. Use the newly added `n in p.captures` method to
@@ -50,6 +57,18 @@
`IndexError` for un-named captures. This is consistant with
`RegexMatch.{captureBounds,captures}[]`.
- splitFile now correctly handles edge cases, see #10047
- `isNil` is no longer false for undefined in the JavaScript backend: now it's true for both nil and undefined. Use `isNull` or `isUndefined` if you need exact equallity: `isNil` is consistent with `===`, `isNull` and `isUndefined` with `==`.
- several deprecated modules were removed: `ssl`, `matchers`, `httpserver`,
`unsigned`, `actors`, `parseurl`
- two poorly documented and not used modules (`subexes`, `scgi`) were moved to
graveyard (they are available as Nimble packages)
#### Breaking changes in the compiler
- The compiler now implements the "generic symbol prepass" for `when` statements
@@ -66,6 +85,9 @@ proc enumToString*(enums: openArray[enum]): string =
```
- ``discard x`` is now illegal when `x` is a function symbol.
- Implicit imports via ``--import: module`` in a config file are now restricted
to the main package.
### Library additions
@@ -87,11 +109,18 @@ proc enumToString*(enums: openArray[enum]): string =
is instantiation of generic proc symbol.
- Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc.
- There is a new stdlib module `std/diff` to compute the famous "diff"
of two texts by line.
- Added `os.relativePath`.
- Added `parseopt.remainingArgs`.
- Added `os.getCurrentCompilerExe` (implmented as `getAppFilename` at CT),
can be used to retrieve the currently executing compiler.
### Library changes
- The string output of `macros.lispRepr` proc has been tweaked
@@ -111,16 +140,23 @@ proc enumToString*(enums: openArray[enum]): string =
(default value: true) that can be set to `false` for better Posix
interoperability. (Bug #9619.)
- `os.joinPath` and `os.normalizePath` handle edge cases like ``"a/b/../../.."``
differently.
- `securehash` is moved to `lib/deprecated`
### Language additions
- Vm suport for float32<->int32 and float64<->int64 casts was added.
- Vm support for float32<->int32 and float64<->int64 casts was added.
- There is a new pragma block `noSideEffect` that works like
the `gcsafe` pragma block.
- added os.getCurrentProcessId()
- User defined pragmas are now allowed in the pragma blocks
- Pragma blocks are now longer eliminated from the typed AST tree to preserve
- Pragma blocks are no longer eliminated from the typed AST tree to preserve
pragmas for further analysis by macros
- Custom pragmas are now supported for `var` and `let` symbols.
### Language changes
@@ -129,13 +165,23 @@ proc enumToString*(enums: openArray[enum]): string =
it's more recognizable and allows tools like github to recognize it as Nim,
see [#9647](https://github.com/nim-lang/Nim/issues/9647).
The previous extension will continue to work.
- Pragma syntax is now consistent. Previous syntax where type pragmas did not
follow the type name is now deprecated. Also pragma before generic parameter
list is deprecated to be consistent with how pragmas are used with a proc. See
[#8514](https://github.com/nim-lang/Nim/issues/8514) and
[#1872](https://github.com/nim-lang/Nim/issues/1872) for further details.
### Tool changes
- `jsondoc` now include a `moduleDescription` field with the module
description. `jsondoc0` shows comments as it's own objects as shown in the
documentation.
- `nimpretty`: --backup now defaults to `off` instead of `on` and the flag was
un-documented; use `git` instead of relying on backup files.
### Compiler changes
- The deprecated `fmod` proc is now unavailable on the VM'.
### Bugfixes

View File

@@ -4,6 +4,6 @@ author = "Andreas Rumpf"
description = "Compiler package providing the compiler sources as a library."
license = "MIT"
installDirs = @["compiler"]
installDirs = @["compiler", "nimsuggest"]
requires "nim >= 0.14.0"

83
compiler/asciitables.nim Normal file
View File

@@ -0,0 +1,83 @@
#[
move to std/asciitables.nim once stable, or to a nimble paackage
once compiler can depend on nimble
]#
type Cell* = object
text*: string
width*, row*, col*, ncols*, nrows*: int
iterator parseTableCells*(s: string, delim = '\t'): Cell =
## iterates over all cells in a `delim`-delimited `s`, after a 1st
## pass that computes number of rows, columns, and width of each column.
var widths: seq[int]
var cell: Cell
template update() =
if widths.len<=cell.col:
widths.setLen cell.col+1
widths[cell.col] = cell.width
else:
widths[cell.col] = max(widths[cell.col], cell.width)
cell.width = 0
for a in s:
case a
of '\n':
update()
cell.col = 0
cell.row.inc
elif a == delim:
update()
cell.col.inc
else:
# todo: consider multi-width chars when porting to non-ascii implementation
cell.width.inc
if s.len > 0 and s[^1] != '\n':
update()
cell.ncols = widths.len
cell.nrows = cell.row + 1
cell.row = 0
cell.col = 0
cell.width = 0
template update2() =
cell.width = widths[cell.col]
yield cell
cell.text = ""
cell.width = 0
cell.col.inc
template finishRow() =
for col in cell.col..<cell.ncols:
cell.col = col
update2()
cell.col = 0
for a in s:
case a
of '\n':
finishRow()
cell.row.inc
elif a == delim:
update2()
else:
cell.width.inc
cell.text.add a
if s.len > 0 and s[^1] != '\n':
finishRow()
proc alignTable*(s: string, delim = '\t', fill = ' ', sep = " "): string =
## formats a `delim`-delimited `s` representing a table; each cell is aligned
## to a width that's computed for each column; consecutive columns are
## delimted by `sep`, and alignment space is filled using `fill`.
## More customized formatting can be done by calling `parseTableCells` directly.
for cell in parseTableCells(s, delim):
result.add cell.text
for i in cell.text.len..<cell.width:
result.add fill
if cell.col < cell.ncols-1:
result.add sep
if cell.col == cell.ncols-1 and cell.row < cell.nrows - 1:
result.add '\n'

View File

@@ -1087,6 +1087,13 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
when debugIds:
registerId(result)
proc astdef*(s: PSym): PNode =
# get only the definition (initializer) portion of the ast
if s.ast != nil and s.ast.kind == nkIdentDefs:
s.ast[2]
else:
s.ast
proc isMetaType*(t: PType): bool =
return t.kind in tyMetaTypes or
(t.kind == tyStatic and t.n == nil) or

View File

@@ -1501,7 +1501,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), a.storage)
of tyOpenArray, tyVarargs:
var b: TLoc
case a.t.kind
case skipTypes(a.t, abstractVarRange).kind
of tyOpenArray, tyVarargs:
putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
of tyString, tySequence:

View File

@@ -31,6 +31,7 @@ const
cfsData: "NIM_merge_DATA",
cfsProcs: "NIM_merge_PROCS",
cfsInitProc: "NIM_merge_INIT_PROC",
cfsDatInitProc: "NIM_merge_DATINIT_PROC",
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
cfsTypeInit2: "NIM_merge_TYPE_INIT2",
cfsTypeInit3: "NIM_merge_TYPE_INIT3",

View File

@@ -436,30 +436,25 @@ proc mangleRecFieldName(m: BModule; field: PSym): Rope =
if result == nil: internalError(m.config, field.info, "mangleRecFieldName")
proc genRecordFieldsAux(m: BModule, n: PNode,
accessExpr: Rope, rectype: PType,
rectype: PType,
check: var IntSet): Rope =
result = nil
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1):
add(result, genRecordFieldsAux(m, n.sons[i], accessExpr, rectype, check))
add(result, genRecordFieldsAux(m, n.sons[i], rectype, check))
of nkRecCase:
if n.sons[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux")
add(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check))
add(result, genRecordFieldsAux(m, n.sons[0], rectype, check))
# prefix mangled name with "_U" to avoid clashes with other field names,
# since identifiers are not allowed to start with '_'
let uname = rope("_U" & mangle(n.sons[0].sym.name.s))
let ae = if accessExpr != nil: "$1.$2" % [accessExpr, uname]
else: uname
var unionBody: Rope = nil
for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind
of nkOfBranch, nkElse:
let k = lastSon(n.sons[i])
if k.kind != nkSym:
let sname = "S" & rope(i)
let a = genRecordFieldsAux(m, k, "$1.$2" % [ae, sname], rectype,
check)
let a = genRecordFieldsAux(m, k, rectype, check)
if a != nil:
if tfPacked notin rectype.flags:
add(unionBody, "struct {")
@@ -469,22 +464,20 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
else:
addf(unionBody, "#pragma pack(push, 1)$nstruct{", [])
add(unionBody, a)
addf(unionBody, "} $1;$n", [sname])
addf(unionBody, "};$n", [])
if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
addf(unionBody, "#pragma pack(pop)$n", [])
else:
add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check))
add(unionBody, genRecordFieldsAux(m, k, rectype, check))
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
if unionBody != nil:
addf(result, "union{$n$1} $2;$n", [unionBody, uname])
addf(result, "union{$n$1};$n", [unionBody])
of nkSym:
let field = n.sym
if field.typ.kind == tyVoid: return
#assert(field.ast == nil)
let sname = mangleRecFieldName(m, field)
let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname]
else: sname
fillLoc(field.loc, locField, n, ae, OnUnknown)
fillLoc(field.loc, locField, n, sname, OnUnknown)
# for importcpp'ed objects, we only need to set field.loc, but don't
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
# with heavily templatized C++ code:
@@ -505,7 +498,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
else: internalError(m.config, n.info, "genRecordFieldsAux()")
proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
result = genRecordFieldsAux(m, typ.n, nil, typ, check)
result = genRecordFieldsAux(m, typ.n, typ, check)
proc fillObjectFields*(m: BModule; typ: PType) =
# sometimes generic objects are not consistently merged. We patch over
@@ -544,12 +537,18 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
appcg(m, result, "virtual void raise() {throw *this;}$n") # required for polymorphic exceptions
if typ.sym.magic == mException:
# Add cleanup destructor to Exception base class
appcg(m, result, "~$1() {if(this->raiseId) popCurrentExceptionEx(this->raiseId);}$n", [name])
appcg(m, result, "~$1() {if(this->raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name])
# hack: forward declare popCurrentExceptionEx() on top of type description,
# proper request to generate popCurrentExceptionEx not possible for 2 reasons:
# generated function will be below declared Exception type and circular dependency
# between Exception and popCurrentExceptionEx function
result = genProcHeader(m, magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")) & ";" & result
let popExSym = magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")
if lfDynamicLib in popExSym.loc.flags and sfImportc in popExSym.flags:
# echo popExSym.flags, " ma flags ", popExSym.loc.flags
result = "extern " & getTypeDescAux(m, popExSym.typ, check) & " " & mangleName(m, popExSym) & ";\L" & result
else:
result = genProcHeader(m, popExSym) & ";\L" & result
hasField = true
else:
appcg(m, result, " {$n $1 Sup;$n",
@@ -971,9 +970,9 @@ proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope;
proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
# bugfix: we need to search the type that contains the discriminator:
var objtype = objtype
var objtype = objtype.skipTypes(abstractPtrs)
while lookupInRecord(objtype.n, d.name) == nil:
objtype = objtype.sons[0]
objtype = objtype.sons[0].skipTypes(abstractPtrs)
if objtype.sym == nil:
internalError(m.config, d.info, "anonymous obj with discriminator")
result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)]
@@ -1042,6 +1041,8 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
else: internalError(m.config, n.info, "genObjectFields(nkRecCase)")
of nkSym:
var field = n.sym
# Do not produce code for void types
if isEmptyType(field.typ): return
if field.bitsize == 0:
if field.loc.r == nil: fillObjectFields(m, typ)
if field.loc.t == nil:

View File

@@ -1087,30 +1087,24 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
result = getCopyright(conf, cfile)
addIntTypes(result, conf)
proc genFilenames(m: BModule): Rope =
discard cgsym(m, "dbgRegisterFilename")
result = nil
for i in 0..<m.config.m.fileInfos.len:
result.addf("dbgRegisterFilename($1);$N",
[m.config.m.fileInfos[i].projPath.string.makeCString])
proc genMainProc(m: BModule) =
## this function is called in cgenWriteModules after all modules are closed,
## it means raising dependency on the symbols is too late as it will not propogate
## into other modules, only simple rope manipulations are allowed
const
# The use of a volatile function pointer to call Pre/NimMainInner
# prevents inlining of the NimMainInner function and dependent
# functions, which might otherwise merge their stack frames.
PreMainBody =
"void PreMainInner(void) {$N" &
"\tsystemInit000();$N" &
"$1" &
"$2" &
"$3" &
"}$N$N" &
"void PreMain(void) {$N" &
"\tvoid (*volatile inner)(void);$N" &
"\tsystemDatInit000();$N" &
"\tinner = PreMainInner;$N" &
"$4$5" &
"$1" &
"\t(*inner)();$N" &
"}$N$N"
@@ -1217,21 +1211,17 @@ proc genMainProc(m: BModule) =
else:
nimMain = PosixNimMain
otherMain = PosixCMain
if m.g.breakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
if optEndb in m.config.options:
m.g.breakpoints.add(m.genFilenames)
for i in 0..<m.config.m.fileInfos.len:
m.g.breakpoints.addf("dbgRegisterFilename($1);$N",
[m.config.m.fileInfos[i].projPath.string.makeCString])
let initStackBottomCall =
if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
inc(m.labels)
appcg(m, m.s[cfsProcs], PreMainBody, [
m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit,
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
ropecg(m, "\t#initThreadVarsEmulation();$N")
else:
"".rope,
initStackBottomCall])
m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit])
appcg(m, m.s[cfsProcs], nimMain,
[m.g.mainModInit, initStackBottomCall, rope(m.labels)])
@@ -1260,22 +1250,61 @@ proc getInitName(m: PSym): Rope =
proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000")
proc registerModuleToMain(g: BModuleList; m: PSym) =
var
init = m.getInitName
datInit = m.getDatInitName
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
if sfSystemModule notin m.flags:
proc registerModuleToMain(g: BModuleList; m: BModule) =
if m.s[cfsDatInitProc].len > 0:
let datInit = m.module.getDatInitName
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
addf(g.mainDatInit, "\t$1();$N", [datInit])
# Initialization of TLS and GC should be done in between
# systemDatInit and systemInit calls if any
if sfSystemModule in m.module.flags:
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
add(g.mainDatInit, ropecg(m, "\t#initThreadVarsEmulation();$N"))
if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone:
add(g.mainDatInit, ropecg(m, "\t#initStackBottomWith((void *)&inner);$N"))
if m.s[cfsInitProc].len > 0:
let init = m.module.getInitName
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
let initCall = "\t$1();$N" % [init]
if sfMainModule in m.flags:
if sfMainModule in m.module.flags:
add(g.mainModInit, initCall)
elif sfSystemModule in m.module.flags:
add(g.mainDatInit, initCall) # systemInit must called right after systemDatInit if any
else:
add(g.otherModsInit, initCall)
proc genDatInitCode(m: BModule) =
## this function is called in cgenWriteModules after all modules are closed,
## it means raising dependency on the symbols is too late as it will not propogate
## into other modules, only simple rope manipulations are allowed
var moduleDatInitRequired = false
var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" %
[getDatInitName(m.module)]
for i in cfsTypeInit1..cfsDynLibInit:
if m.s[i].len != 0:
moduleDatInitRequired = true
add(prc, genSectionStart(i, m.config))
add(prc, m.s[i])
add(prc, genSectionEnd(i, m.config))
addf(prc, "}$N$N", [])
if moduleDatInitRequired:
add(m.s[cfsDatInitProc], prc)
proc genInitCode(m: BModule) =
var initname = getInitName(m.module)
## this function is called in cgenWriteModules after all modules are closed,
## it means raising dependency on the symbols is too late as it will not propogate
## into other modules, only simple rope manipulations are allowed
var moduleInitRequired = false
let initname = getInitName(m.module)
var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" % [initname]
if m.typeNodes > 0:
appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
@@ -1290,82 +1319,115 @@ proc genInitCode(m: BModule) =
# Keep a bogus frame in case the code needs one
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
if m.preInitProc.s(cpsLocals).len > 0:
moduleInitRequired = true
add(prc, genSectionStart(cpsLocals, m.config))
add(prc, m.preInitProc.s(cpsLocals))
add(prc, genSectionEnd(cpsLocals, m.config))
if m.preInitProc.s(cpsInit).len > 0:
moduleInitRequired = true
add(prc, genSectionStart(cpsInit, m.config))
add(prc, m.preInitProc.s(cpsInit))
add(prc, genSectionEnd(cpsInit, m.config))
if m.preInitProc.s(cpsStmts).len > 0:
moduleInitRequired = true
add(prc, genSectionStart(cpsStmts, m.config))
add(prc, m.preInitProc.s(cpsStmts))
add(prc, genSectionEnd(cpsStmts, m.config))
addf(prc, "}$N", [])
if m.initProc.gcFrameId > 0:
moduleInitRequired = true
add(prc, initGCFrame(m.initProc))
if m.initProc.s(cpsLocals).len > 0:
moduleInitRequired = true
add(prc, genSectionStart(cpsLocals, m.config))
add(prc, m.preInitProc.s(cpsLocals))
add(prc, m.initProc.s(cpsLocals))
add(prc, genSectionEnd(cpsLocals, m.config))
if m.initProc.s(cpsInit).len > 0 or m.initProc.s(cpsStmts).len > 0:
moduleInitRequired = true
if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
# BUT: the generated init code might depend on a current frame, so
# declare it nevertheless:
incl m.flags, frameDeclared
if preventStackTrace notin m.flags:
var procname = makeCString(m.module.name.s)
add(prc, initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info)))
else:
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
add(prc, genSectionStart(cpsInit, m.config))
add(prc, m.preInitProc.s(cpsInit))
add(prc, m.initProc.s(cpsInit))
add(prc, genSectionEnd(cpsInit, m.config))
add(prc, genSectionStart(cpsStmts, m.config))
add(prc, m.preInitProc.s(cpsStmts))
add(prc, m.initProc.s(cpsStmts))
add(prc, genSectionEnd(cpsStmts, m.config))
addf(prc, "}$N", [])
add(prc, initGCFrame(m.initProc))
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
add(prc, deinitFrame(m.initProc))
add(prc, genSectionStart(cpsLocals, m.config))
add(prc, m.initProc.s(cpsLocals))
add(prc, genSectionEnd(cpsLocals, m.config))
if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
# BUT: the generated init code might depend on a current frame, so
# declare it nevertheless:
incl m.flags, frameDeclared
if preventStackTrace notin m.flags:
var procname = makeCString(m.module.name.s)
add(prc, initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info)))
else:
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
add(prc, genSectionStart(cpsInit, m.config))
add(prc, m.initProc.s(cpsInit))
add(prc, genSectionEnd(cpsInit, m.config))
add(prc, genSectionStart(cpsStmts, m.config))
add(prc, m.initProc.s(cpsStmts))
add(prc, genSectionEnd(cpsStmts, m.config))
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
add(prc, deinitFrame(m.initProc))
add(prc, deinitGCFrame(m.initProc))
addf(prc, "}$N$N", [])
prc.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N",
[getDatInitName(m.module)])
for i in cfsTypeInit1..cfsDynLibInit:
add(prc, genSectionStart(i, m.config))
add(prc, m.s[i])
add(prc, genSectionEnd(i, m.config))
if m.initProc.gcFrameId > 0:
moduleInitRequired = true
add(prc, deinitGCFrame(m.initProc))
addf(prc, "}$N$N", [])
# we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
# that would lead to a *nesting* of merge sections which the merger does
# not support. So we add it to another special section: ``cfsInitProc``
add(m.s[cfsInitProc], prc)
for i, el in pairs(m.extensionLoaders):
if el != nil:
let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" %
[(i.ord - '0'.ord).rope, el]
add(m.s[cfsInitProc], ex)
moduleInitRequired = true
add(prc, ex)
if moduleInitRequired or sfMainModule in m.module.flags:
add(m.s[cfsInitProc], prc)
genDatInitCode(m)
registerModuleToMain(m.g, m)
proc genModule(m: BModule, cfile: Cfile): Rope =
var moduleIsEmpty = true
result = getFileHeader(m.config, cfile)
result.add(genMergeInfo(m))
if m.config.cppCustomNamespace.len > 0:
result.add openNamespaceNim(m.config.cppCustomNamespace)
generateThreadLocalStorage(m)
generateHeaders(m)
for i in countup(cfsHeaders, cfsProcs):
add(result, genSectionStart(i, m.config))
add(result, m.s[i])
add(result, genSectionEnd(i, m.config))
if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders:
result.add openNamespaceNim(m.config.cppCustomNamespace)
add(result, m.s[cfsInitProc])
if m.config.cppCustomNamespace.len > 0: result.add closeNamespaceNim()
add(result, genSectionStart(cfsHeaders, m.config))
add(result, m.s[cfsHeaders])
add(result, genSectionEnd(cfsHeaders, m.config))
for i in countup(cfsForwardTypes, cfsProcs):
if m.s[i].len > 0:
moduleIsEmpty = false
add(result, genSectionStart(i, m.config))
add(result, m.s[i])
add(result, genSectionEnd(i, m.config))
if m.s[cfsInitProc].len > 0:
moduleIsEmpty = false
add(result, m.s[cfsInitProc])
if m.s[cfsDatInitProc].len > 0:
moduleIsEmpty = false
add(result, m.s[cfsDatInitProc])
if m.config.cppCustomNamespace.len > 0:
result.add closeNamespaceNim()
if moduleIsEmpty:
result = nil
proc newPreInitProc(m: BModule): BProc =
result = newProc(nil, m)
@@ -1522,27 +1584,30 @@ proc writeModule(m: BModule, pending: bool) =
finishTypeDescriptions(m)
if sfMainModule in m.module.flags:
# generate main file:
genMainProc(m)
add(m.s[cfsProcHeaders], m.g.mainModProcs)
generateThreadVarsSize(m)
var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
var code = genModule(m, cf)
when hasTinyCBackend:
if conf.cmd == cmdRun:
tccgen.compileCCode($code)
return
if code != nil:
when hasTinyCBackend:
if conf.cmd == cmdRun:
tccgen.compileCCode($code)
return
if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf)
if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf)
elif pending and mergeRequired(m) and sfMainModule notin m.module.flags:
let cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
mergeFiles(cfile, m)
genInitCode(m)
finishTypeDescriptions(m)
var code = genModule(m, cf)
if not writeRope(code, cfile):
rawMessage(m.config, errCannotOpenFile, cfile.string)
addFileToCompile(m.config, cf)
if code != nil:
if not writeRope(code, cfile):
rawMessage(m.config, errCannotOpenFile, cfile.string)
addFileToCompile(m.config, cf)
else:
# Consider: first compilation compiles ``system.nim`` and produces
# ``system.c`` but then compilation fails due to an error. This means
@@ -1560,13 +1625,16 @@ proc updateCachedModule(m: BModule) =
mergeFiles(cfile, m)
genInitCode(m)
finishTypeDescriptions(m)
var code = genModule(m, cf)
if not writeRope(code, cfile):
rawMessage(m.config, errCannotOpenFile, cfile.string)
if code != nil:
if not writeRope(code, cfile):
rawMessage(m.config, errCannotOpenFile, cfile.string)
addFileToCompile(m.config, cf)
else:
if sfMainModule notin m.module.flags:
genMainProc(m)
cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf)
addFileToCompile(m.config, cf)
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
result = n
@@ -1579,15 +1647,25 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
if n != nil:
m.initProc.options = initProcOptions(m)
genStmts(m.initProc, n)
# cached modules need to registered too:
registerModuleToMain(m.g, m.module)
if sfMainModule in m.module.flags:
# raise dependencies on behalf of genMainProc
if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone:
discard cgsym(m, "initStackBottomWith")
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
discard cgsym(m, "initThreadVarsEmulation")
if m.g.breakpoints != nil:
discard cgsym(m, "dbgRegisterBreakpoint")
if optEndb in m.config.options:
discard cgsym(m, "dbgRegisterFilename")
if m.g.forwardedProcs.len == 0:
incl m.flags, objHasKidsValid
let disp = generateMethodDispatchers(graph)
for x in disp: genProcAux(m, x.sym)
genMainProc(m)
m.g.modules_closed.add m
proc genForwardedProcs(g: BModuleList) =
# Forward declared proc:s lack bodies when first encountered, so they're given

View File

@@ -32,6 +32,7 @@ type
cfsData, # section for C constant data
cfsProcs, # section for C procs that are not inline
cfsInitProc, # section for the C init proc
cfsDatInitProc, # section for the C datInit proc
cfsTypeInit1, # section 1 for declarations of type information
cfsTypeInit2, # section 2 for init of type information
cfsTypeInit3, # section 3 for init of type information
@@ -112,6 +113,7 @@ type
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
mapping*: Rope # the generated mapping file (if requested)
modules*: seq[BModule] # list of all compiled modules
modules_closed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
generatedHeader*: BModule
breakPointId*: int
@@ -191,8 +193,6 @@ proc newModuleList*(g: ModuleGraph): BModuleList =
graph: g, nimtvDeclared: initIntSet())
iterator cgenModules*(g: BModuleList): BModule =
for m in g.modules:
# ultimately, we are iterating over the file ids here.
# some "files" won't have an associated cgen module (like stdin)
# and we must skip over them.
if m != nil: yield m
for m in g.modules_closed:
# iterate modules in the order they were closed
yield m

View File

@@ -48,18 +48,10 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
if self.suggestMode:
conf.command = "nimsuggest"
# These defines/options should not be enabled while processing nimscript
# bug #4446, #9420, #8991, #9589, #9153
undefSymbol(conf.symbols, "profiler")
undefSymbol(conf.symbols, "memProfiler")
undefSymbol(conf.symbols, "nodejs")
# bug #9120
conf.globalOptions.excl(optTaintMode)
proc runNimScriptIfExists(path: AbsoluteFile)=
if fileExists(path):
runNimScript(cache, path, freshDefines = false, conf)
template runNimScriptIfExists(path: AbsoluteFile) =
let p = path # eval once
if fileExists(p):
runNimScript(cache, p, freshDefines = false, conf)
# Caution: make sure this stays in sync with `loadConfigs`
if optSkipSystemConfigFile notin conf.globalOptions:
@@ -88,9 +80,6 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
# 'nimsuggest foo.nims' means to just auto-complete the NimScript file
discard
# Reload configuration from .cfg file
loadConfigs(DefaultConfig, cache, conf)
# now process command line arguments again, because some options in the
# command line can overwite the config file's settings
extccomp.initVars(conf)

View File

@@ -142,7 +142,7 @@ proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TC
proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo) =
case arg.normalize
of "on": conf.options = conf.options + op
of "","on": conf.options = conf.options + op
of "off": conf.options = conf.options - op
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
@@ -158,7 +158,7 @@ proc processOnOffSwitchOrList(conf: ConfigRef; op: TOptions, arg: string, pass:
proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo) =
case arg.normalize
of "on": conf.globalOptions = conf.globalOptions + op
of "", "on": conf.globalOptions = conf.globalOptions + op
of "off": conf.globalOptions = conf.globalOptions - op
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
@@ -224,7 +224,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
case arg.normalize
of "boehm": result = conf.selectedGC == gcBoehm
of "refc": result = conf.selectedGC == gcRefc
of "v2": result = conf.selectedGC == gcV2
of "v2": result = false
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
of "generational": result = false
of "destructors": result = conf.selectedGC == gcDestructors
@@ -291,6 +291,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
of "patterns": result = contains(conf.options, optPatterns)
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
of "nilseqs": result = contains(conf.options, optNilSeqs)
of "oldast": result = contains(conf.options, optOldAst)
else: invalidCmdLineOption(conf, passCmd1, switch, info)
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
@@ -413,26 +414,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
if pass in {passCmd2, passPP}:
addExternalFileToLink(conf, AbsoluteFile arg)
of "debuginfo":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optCDebug)
processOnOffSwitchG(conf, {optCDebug}, arg, pass, info)
of "embedsrc":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optEmbedOrigSrc)
processOnOffSwitchG(conf, {optEmbedOrigSrc}, arg, pass, info)
of "compileonly", "c":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optCompileOnly)
processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
of "nolinking":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optNoLinking)
processOnOffSwitchG(conf, {optNoLinking}, arg, pass, info)
of "nomain":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optNoMain)
processOnOffSwitchG(conf, {optNoMain}, arg, pass, info)
of "forcebuild", "f":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optForceFullMake)
processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info)
of "project":
expectNoArg(conf, switch, arg, pass, info)
incl conf.globalOptions, optWholeProject
processOnOffSwitchG(conf, {optWholeProject}, arg, pass, info)
of "gc":
expectArg(conf, switch, arg, pass, info)
case arg.normalize
@@ -442,7 +436,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "refc":
conf.selectedGC = gcRefc
of "v2":
conf.selectedGC = gcV2
message(conf, info, warnDeprecated, "--gc:v2 is deprecated; using default gc")
of "markandsweep":
conf.selectedGC = gcMarkAndSweep
defineSymbol(conf.symbols, "gcmarkandsweep")
@@ -498,7 +492,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
else: undefSymbol(conf.symbols, "hotcodereloading")
of "oldnewlines":
case arg.normalize
of "on":
of "","on":
conf.oldNewlines = true
defineSymbol(conf.symbols, "nimOldNewlines")
of "off":
@@ -508,6 +502,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
localError(conf, info, errOnOrOffExpectedButXFound % arg)
of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info)
of "oldast": processOnOffSwitch(conf, {optOldAst}, arg, pass, info)
of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
of "floatchecks":
processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)
@@ -590,16 +585,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info)
of "import":
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}: conf.implicitImports.add arg
if pass in {passCmd2, passPP}:
conf.implicitImports.add findModule(conf, arg, toFullPath(conf, info)).string
of "include":
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}: conf.implicitIncludes.add arg
if pass in {passCmd2, passPP}:
conf.implicitIncludes.add findModule(conf, arg, toFullPath(conf, info)).string
of "listcmd":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optListCmd)
processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
of "genmapping":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optGenMapping)
processOnOffSwitchG(conf, {optGenMapping}, arg, pass, info)
of "os":
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd1, passPP}:
@@ -615,8 +610,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
elif cpu != conf.target.hostCPU:
setTarget(conf.target, conf.target.targetOS, cpu)
of "run", "r":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optRun)
processOnOffSwitchG(conf, {optRun}, arg, pass, info)
of "errormax":
expectArg(conf, switch, arg, pass, info)
# Note: `nim check` (etc) can overwrite this.
@@ -664,21 +658,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "v2": conf.symbolFiles = v2Sf
else: localError(conf, info, "invalid option for --incremental: " & arg)
of "skipcfg":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optSkipSystemConfigFile)
processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info)
of "skipprojcfg":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optSkipProjConfigFile)
processOnOffSwitchG(conf, {optSkipProjConfigFile}, arg, pass, info)
of "skipusercfg":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optSkipUserConfigFile)
processOnOffSwitchG(conf, {optSkipUserConfigFile}, arg, pass, info)
of "skipparentcfg":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optSkipParentConfigFiles)
processOnOffSwitchG(conf, {optSkipParentConfigFiles}, arg, pass, info)
of "genscript", "gendeps":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optGenScript)
incl(conf.globalOptions, optCompileOnly)
processOnOffSwitchG(conf, {optGenScript}, arg, pass, info)
processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
of "lib":
expectArg(conf, switch, arg, pass, info)
@@ -712,16 +701,13 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
expectNoArg(conf, switch, arg, pass, info)
conf.ideCmd = ideUse
of "stdout":
expectNoArg(conf, switch, arg, pass, info)
incl(conf.globalOptions, optStdout)
processOnOffSwitchG(conf, {optStdout}, arg, pass, info)
of "listfullpaths":
expectNoArg(conf, switch, arg, pass, info)
incl conf.globalOptions, optListFullPaths
processOnOffSwitchG(conf, {optListFullPaths}, arg, pass, info)
of "dynliboverride":
dynlibOverride(conf, switch, arg, pass, info)
of "dynliboverrideall":
expectNoArg(conf, switch, arg, pass, info)
incl conf.globalOptions, optDynlibOverrideAll
processOnOffSwitchG(conf, {optDynlibOverrideAll}, arg, pass, info)
of "cs":
# only supported for compatibility. Does nothing.
expectArg(conf, switch, arg, pass, info)

View File

@@ -296,7 +296,8 @@ proc makePtrType(c: Con, baseType: PType): PType =
template genOp(opr, opname, ri) =
let op = opr
if op == nil:
globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator not found for type " & typeToString(t))
globalError(c.graph.config, dest.info, "internal error: '" & opname &
"' operator not found for type " & typeToString(t))
elif op.ast[genericParamsPos].kind != nkEmpty:
globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic")
patchHead op
@@ -365,7 +366,7 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode =
result.add genWasMoved(n, c)
result.add tempAsNode
proc sinkParamIsLastReadCheck(c: var Con, s: PNode) =
proc sinkParamIsLastReadCheck(c: var Con, s: PNode) =
assert s.kind == nkSym and s.sym.kind == skParam
if not isLastRead(s, c):
localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s &
@@ -427,7 +428,7 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
result = copyNode(arg)
for i in 0..<arg.len:
var branch = copyNode(arg[i])
if arg[i].kind in {nkElifBranch, nkElifExpr}:
if arg[i].kind in {nkElifBranch, nkElifExpr}:
branch.add p(arg[i][0], c)
branch.add pArgIfTyped(arg[i][1])
else:
@@ -442,13 +443,13 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
branch = arg[i] # of branch conditions are constants
branch[^1] = pArgIfTyped(arg[i][^1])
elif arg[i].kind in {nkElifBranch, nkElifExpr}:
branch = copyNode(arg[i])
branch = copyNode(arg[i])
branch.add p(arg[i][0], c)
branch.add pArgIfTyped(arg[i][1])
else:
branch = copyNode(arg[i])
branch = copyNode(arg[i])
branch.add pArgIfTyped(arg[i][0])
result.add branch
result.add branch
else:
# an object that is not temporary but passed to a 'sink' parameter
# results in a copy.
@@ -476,7 +477,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
result.add ri2
of nkBracketExpr:
if ri[0].kind == nkSym and isUnpackedTuple(ri[0].sym):
# unpacking of tuple: move out the elements
# unpacking of tuple: move out the elements
result = genSink(c, dest.typ, dest, ri)
else:
result = genCopy(c, dest.typ, dest, ri)
@@ -509,11 +510,11 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
branch = ri[i] # of branch conditions are constants
branch[^1] = moveOrCopyIfTyped(ri[i][^1])
elif ri[i].kind in {nkElifBranch, nkElifExpr}:
branch = copyNode(ri[i])
branch = copyNode(ri[i])
branch.add p(ri[i][0], c)
branch.add moveOrCopyIfTyped(ri[i][1])
else:
branch = copyNode(ri[i])
branch = copyNode(ri[i])
branch.add moveOrCopyIfTyped(ri[i][0])
result.add branch
of nkBracket:
@@ -550,7 +551,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
sinkParamIsLastReadCheck(c, ri)
var snk = genSink(c, dest.typ, dest, ri)
snk.add ri
result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))
result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved))
elif ri.sym.kind != skParam and isLastRead(ri, c):
# Rule 3: `=sink`(x, z); wasMoved(z)
var snk = genSink(c, dest.typ, dest, ri)
@@ -647,7 +648,7 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
let params = owner.typ.n
for i in 1 ..< params.len:
let param = params[i].sym
if param.typ.kind == tySink and hasDestructor(param.typ):
if param.typ.kind == tySink and hasDestructor(param.typ):
c.destroys.add genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i])
let body = p(n, c)

View File

@@ -86,7 +86,7 @@ proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) =
result.add("\n")
inc i
if i in jumpTargets: result.add("L" & $i & ": End\n")
# consider calling `asciitables.alignTable`
proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} =
## echos the ControlFlowGraph for debugging purposes.

View File

@@ -40,6 +40,7 @@ type
# already. See bug #3655
destFile*: AbsoluteFile
thisDir*: AbsoluteDir
examples: string
PDoc* = ref TDocumentor ## Alias to type less.
@@ -118,7 +119,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
result.conf = conf
result.cache = cache
initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex),
conf.configVars, filename.string, {roSupportRawDirective},
conf.configVars, filename.string, {roSupportRawDirective, roSupportMarkdown},
docgenFindFile, compilerMsgHandler)
if conf.configVars.hasKey("doc.googleAnalytics"):
@@ -378,6 +379,14 @@ proc testExample(d: PDoc; ex: PNode) =
"_examples" & $d.exampleCounter & ".nim"))
#let nimcache = outp.changeFileExt"" & "_nimcache"
renderModule(ex, d.filename, outp.string, conf = d.conf)
d.examples.add "import r\"" & outp.string & "\"\n"
proc runAllExamples(d: PDoc) =
if d.examples.len == 0: return
let outputDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples"
let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" &
"_examples.nim"))
writeFile(outp, d.examples)
let backend = if isDefined(d.conf, "js"): "js"
elif isDefined(d.conf, "cpp"): "cpp"
elif isDefined(d.conf, "objc"): "objc"
@@ -400,11 +409,9 @@ proc extractImports(n: PNode; result: PNode) =
for i in 0..<n.safeLen: extractImports(n[i], result)
proc prepareExamples(d: PDoc; n: PNode) =
var docComment = newTree(nkCommentStmt)
let loc = d.conf.toFileLineCol(n.info)
docComment.comment = "autogenerated by docgen from " & loc
var runnableExamples = newTree(nkStmtList,
docComment,
newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
@@ -935,6 +942,7 @@ proc generateIndex*(d: PDoc) =
writeIndexFile(d[], dest.string)
proc writeOutput*(d: PDoc, useWarning = false) =
runAllExamples(d)
var content = genOutFile(d)
if optStdout in d.conf.globalOptions:
writeRope(stdout, content)
@@ -947,6 +955,7 @@ proc writeOutput*(d: PDoc, useWarning = false) =
outfile.string)
proc writeOutputJson*(d: PDoc, useWarning = false) =
runAllExamples(d)
var modDesc: string
for desc in d.modDesc:
modDesc &= desc
@@ -982,7 +991,7 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef;
d.isPureRst = true
var rst = parseRst(readFile(filen.string), filen.string, 0, 1, d.hasToc,
{roSupportRawDirective}, conf)
{roSupportRawDirective, roSupportMarkdown}, conf)
var modDesc = newStringOfCap(30_000)
renderRstToOut(d[], rst, modDesc)
d.modDesc = rope(modDesc)

View File

@@ -63,8 +63,8 @@ compiler gcc:
result = (
name: "gcc",
objExt: "o",
optSpeed: " -O3 -ffast-math ",
optSize: " -Os -ffast-math ",
optSpeed: " -O3 ",
optSize: " -Os ",
compilerExe: "gcc",
cppCompiler: "g++",
compileTmpl: "-c $options $include -o $objfile $file",
@@ -88,8 +88,8 @@ compiler nintendoSwitchGCC:
result = (
name: "switch_gcc",
objExt: "o",
optSpeed: " -O3 -ffast-math ",
optSize: " -Os -ffast-math ",
optSpeed: " -O3 ",
optSize: " -Os ",
compilerExe: "aarch64-none-elf-gcc",
cppCompiler: "aarch64-none-elf-g++",
compileTmpl: "-w -MMD -MP -MF $dfile -c $options $include -o $objfile $file",
@@ -153,6 +153,13 @@ compiler vcc:
structStmtFmt: "$3$n$1 $2",
props: {hasCpp, hasAssume, hasDeclspec})
compiler clangcl:
result = vcc()
result.name = "clang_cl"
result.compilerExe = "clang-cl"
result.cppCompiler = "clang-cl"
result.linkerExe = "clang-cl"
# Intel C/C++ Compiler
compiler icl:
result = vcc()
@@ -353,7 +360,8 @@ const
pcc(),
ucc(),
icl(),
icc()]
icc(),
clangcl()]
hExt* = ".h"
@@ -567,6 +575,8 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
includeCmd = ""
compilePattern = getCompilerExe(conf, c, cfile.cname)
includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)]))
var cf = if noAbsolutePaths(conf): AbsoluteFile extractFilename(cfile.cname.string)
else: cfile.cname

View File

@@ -257,9 +257,9 @@ proc canon*(n: PNode; o: Operators): PNode =
for i in 0 ..< n.len:
result.sons[i] = canon(n.sons[i], o)
elif n.kind == nkSym and n.sym.kind == skLet and
n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin +
n.sym.astdef.getMagic in (someEq + someAdd + someMul + someMin +
someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv):
result = n.sym.ast.copyTree
result = n.sym.astdef.copyTree
else:
result = n
case result.getMagic
@@ -395,8 +395,8 @@ proc usefulFact(n: PNode; o: Operators): PNode =
# if a:
# ...
# We make can easily replace 'a' by '2 < x' here:
if n.sym.ast != nil:
result = usefulFact(n.sym.ast, o)
if n.sym.astdef != nil:
result = usefulFact(n.sym.astdef, o)
elif n.kind == nkStmtListExpr:
result = usefulFact(n.lastSon, o)

View File

@@ -92,6 +92,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
rawImportSymbol(c, e)
e = nextIdentIter(it, fromMod.tab)
else: rawImportSymbol(c, s)
suggestSym(c.config, n.info, s, c.graph.usageSym, false)
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
var i: TTabIter
@@ -161,9 +162,9 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
localError(c.config, n.info, "A module cannot import itself")
if sfDeprecated in result.flags:
if result.constraint != nil:
message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s)
message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s & " is deprecated")
else:
message(c.config, n.info, warnDeprecated, result.name.s)
message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated")
suggestSym(c.config, n.info, result, c.graph.usageSym, false)
importStmtResult.add newSymNode(result, n.info)
#newStrNode(toFullPath(c.config, f), n.info)

View File

@@ -94,6 +94,7 @@ type
options: TOptions
module: BModule
g: PGlobals
generatedParamCopies: IntSet
beforeRetNeeded: bool
unique: int # for temp identifier generation
blocks: seq[TBlock]
@@ -918,35 +919,15 @@ proc countJsParams(typ: PType): int =
const
nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkStringToCString,
nkObjConstr, nkTupleConstr, nkBracket,
nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix,
nkCommand, nkHiddenCallConv, nkCallStrLit}
proc needsNoCopy(p: PProc; y: PNode): bool =
# if the node is a literal object constructor we have to recursively
# check the expressions passed into it
case y.kind
of nkObjConstr:
for arg in y.sons[1..^1]:
if not needsNoCopy(p, arg[1]):
return false
of nkTupleConstr:
for arg in y.sons:
var arg = arg
if arg.kind == nkExprColonExpr:
arg = arg[1]
if not needsNoCopy(p, arg):
return false
of nkBracket:
for arg in y.sons:
if not needsNoCopy(p, arg):
return false
of nodeKindsNeedNoCopy:
return true
else:
return (mapType(y.typ) != etyBaseIndex and
(skipTypes(y.typ, abstractInst).kind in
{tyRef, tyPtr, tyLent, tyVar, tyCString, tyProc} + IntegralTypes))
return true
return y.kind in nodeKindsNeedNoCopy or
((mapType(y.typ) != etyBaseIndex or (y.kind == nkSym and y.sym.kind == skParam)) and
(skipTypes(y.typ, abstractInst).kind in
{tyRef, tyPtr, tyLent, tyVar, tyCString, tyProc} + IntegralTypes))
proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
var a, b: TCompRes
@@ -969,7 +950,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
lineF(p, "$1 = nimCopy(null, $2, $3);$n",
[a.rdLoc, b.res, genTypeInfo(p, y.typ)])
of etyObject:
if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
if x.typ.kind == tyVar or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else:
useMagic(p, "nimCopy")
@@ -1252,12 +1233,29 @@ proc genProcForSymIfNeeded(p: PProc, s: PSym) =
if owner != nil: add(owner.locals, newp)
else: attachProc(p, newp, s)
proc genCopyForParamIfNeeded(p: PProc, n: PNode) =
let s = n.sym
if p.prc == s.owner or needsNoCopy(p, n):
return
var owner = p.up
while true:
if owner == nil:
internalError(p.config, n.info, "couldn't find the owner proc of the closed over param: " & s.name.s)
if owner.prc == s.owner:
if not owner.generatedParamCopies.containsOrIncl(s.id):
let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.r, genTypeInfo(p, s.typ)]
add(owner.locals, owner.indentLine(copy))
return
owner = owner.up
proc genSym(p: PProc, n: PNode, r: var TCompRes) =
var s = n.sym
case s.kind
of skVar, skLet, skParam, skTemp, skResult, skForVar:
if s.loc.r == nil:
internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
if s.kind == skParam:
genCopyForParamIfNeeded(p, n)
let k = mapType(p, s.typ)
if k == etyBaseIndex:
r.typ = etyBaseIndex
@@ -1504,6 +1502,8 @@ proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output:
for i in countup(1, sonsLen(rec) - 1):
createRecordVarAux(p, lastSon(rec.sons[i]), excludedFieldIDs, output)
of nkSym:
# Do not produce code for void types
if isEmptyType(rec.sym.typ): return
if rec.sym.id notin excludedFieldIDs:
if output.len > 0: output.add(", ")
output.addf("$#: ", [mangleName(p.module, rec.sym)])
@@ -1886,12 +1886,13 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
of mLtStr:
binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
of mIsNil:
# we want to accept undefined, so we ==
if mapType(n[1].typ) != etyBaseIndex:
unaryExpr(p, n, r, "", "($1 === null)")
unaryExpr(p, n, r, "", "($1 == null)")
else:
var x: TCompRes
gen(p, n[1], x)
r.res = "($# === null && $# === 0)" % [x.address, x.res]
r.res = "($# == null && $# === 0)" % [x.address, x.res]
of mEnumToStr: genRepr(p, n, r)
of mNew, mNewFinalize: genNew(p, n)
of mChr: gen(p, n.sons[1], r)
@@ -1924,7 +1925,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)")
of mSetLengthStr:
binaryExpr(p, n, r, "mnewString", "($1 === null ? $3 = mnewString($2) : $3.length = $2)")
binaryExpr(p, n, r, "mnewString", "($1 == null ? $3 = mnewString($2) : $3.length = $2)")
of mSetLengthSeq:
var x, y: TCompRes
gen(p, n.sons[1], x)
@@ -2005,6 +2006,10 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
if a.typ == etyBaseIndex:
addf(r.res, "[$1, $2]", [a.address, a.res])
else:
if not needsNoCopy(p, n[i]):
let typ = n[i].typ.skipTypes(abstractInst)
useMagic(p, "nimCopy")
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
add(r.res, a.res)
add(r.res, "]")
@@ -2017,9 +2022,13 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
var it = n.sons[i]
if it.kind == nkExprColonExpr: it = it.sons[1]
gen(p, it, a)
let typ = it.typ.skipTypes(abstractInst)
if a.typ == etyBaseIndex:
addf(r.res, "Field$#: [$#, $#]", [i.rope, a.address, a.res])
else:
if not needsNoCopy(p, it):
useMagic(p, "nimCopy")
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
addf(r.res, "Field$#: $#", [i.rope, a.res])
r.res.add("}")
@@ -2039,17 +2048,12 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
fieldIDs.incl(f.id)
let typ = val.typ.skipTypes(abstractInst)
if (typ.kind in IntegralTypes+{tyCstring, tyRef, tyPtr} and
mapType(p, typ) != etyBaseIndex) or
a.typ == etyBaseIndex or
needsNoCopy(p, it.sons[1]):
discard
else:
useMagic(p, "nimCopy")
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
if a.typ == etyBaseIndex:
addf(initList, "$#: [$#, $#]", [f.loc.r, a.address, a.res])
else:
if not needsNoCopy(p, val):
useMagic(p, "nimCopy")
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
addf(initList, "$#: $#", [f.loc.r, a.res])
let t = skipTypes(n.typ, abstractInst + skipPtrs)
createObjInitList(p, t, fieldIDs, initList)

View File

@@ -17,7 +17,7 @@
import
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
wordrecg, lineinfos, pathutils
wordrecg, lineinfos, pathutils, parseutils
const
MaxLineLength* = 80 # lines longer than this lead to a warning
@@ -307,20 +307,6 @@ template tokenEndPrevious(tok, pos) =
when defined(nimpretty):
tok.offsetB = L.offsetBase + pos
{.push overflowChecks: off.}
# We need to parse the largest uint literal without overflow checks
proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
var i = start
if i < s.len and s[i] in {'0'..'9'}:
b = 0
while i < s.len and s[i] in {'0'..'9'}:
b = b * 10 + (ord(s[i]) - ord('0'))
inc(i)
while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
result = i - start
{.pop.} # overflowChecks
template eatChar(L: var TLexer, t: var TToken, replacementChar: char) =
add(t.literal, replacementChar)
inc(L.bufpos)
@@ -586,33 +572,43 @@ proc getNumber(L: var TLexer, result: var TToken) =
of floatTypes:
result.fNumber = parseFloat(result.literal)
of tkUint64Lit:
xi = 0
let len = unsafeParseUInt(result.literal, xi)
if len != result.literal.len or len == 0:
raise newException(ValueError, "invalid integer: " & $xi)
result.iNumber = xi
var iNumber: uint64
var len: int
try:
len = parseBiggestUInt(result.literal, iNumber)
except ValueError:
raise newException(OverflowError, "number out of range: " & $result.literal)
if len != result.literal.len:
raise newException(ValueError, "invalid integer: " & $result.literal)
result.iNumber = cast[int64](iNumber)
else:
result.iNumber = parseBiggestInt(result.literal)
var iNumber: int64
var len: int
try:
len = parseBiggestInt(result.literal, iNumber)
except ValueError:
raise newException(OverflowError, "number out of range: " & $result.literal)
if len != result.literal.len:
raise newException(ValueError, "invalid integer: " & $result.literal)
result.iNumber = iNumber
# Explicit bounds checks
# Explicit bounds checks. Only T.high needs to be considered
# since result.iNumber can't be negative.
let outOfRange =
case result.tokType
of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high)
of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or
result.iNumber > BiggestInt(uint8.high))
of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high)
of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or
result.iNumber > BiggestInt(uint16.high))
of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high)
of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or
result.iNumber > BiggestInt(uint32.high))
of tkInt8Lit: result.iNumber > int8.high
of tkUInt8Lit: result.iNumber > BiggestInt(uint8.high)
of tkInt16Lit: result.iNumber > int16.high
of tkUInt16Lit: result.iNumber > BiggestInt(uint16.high)
of tkInt32Lit: result.iNumber > int32.high
of tkUInt32Lit: result.iNumber > BiggestInt(uint32.high)
else: false
if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos)
# Promote int literal to int64? Not always necessary, but more consistent
if result.tokType == tkIntLit:
if (result.iNumber < low(int32)) or (result.iNumber > high(int32)):
if result.iNumber > high(int32):
result.tokType = tkInt64Lit
except ValueError:

View File

@@ -65,7 +65,7 @@ const
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
warnXIsNeverRead: "'$1' is never read",
warnXmightNotBeenInit: "'$1' might not have been initialized",
warnDeprecated: "$1 is deprecated",
warnDeprecated: "$1",
warnConfigDeprecated: "config file '$1' is deprecated",
warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
warnUnknownMagic: "unknown magic '$1' might crash the compiler",
@@ -171,7 +171,7 @@ proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
hintSource, hintGlobalVar, hintGCStats}
result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
hintProcessing, hintPattern, hintExecuting, hintLinking}
hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC}
const
NotesVerbosity* = computeNotesVerbosity()

View File

@@ -1,28 +0,0 @@
#
#
# The Nim Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module is deprecated, don't use it.
# TODO Remove this
import os
static:
echo "WARNING: imported deprecated module compiler/lists.nim, use seq ore lists from the standard library"
proc appendStr*(list: var seq[string]; data: string) {.deprecated.} =
# just use system.add
list.add(data)
proc includeStr(list: var seq[string]; data: string): bool {.deprecated.} =
if list.contains(data):
result = true
else:
result = false
list.add data

View File

@@ -89,7 +89,7 @@ proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
prettybase.replaceDeprecated(conf, n.info, s, result)
else:
message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
s.name.s)
s.name.s & " is deprecated")
proc localSearchInScope*(c: PContext, s: PIdent): PSym =
result = strTableGet(c.currentScope.symbols, s)
@@ -150,7 +150,7 @@ type
proc getSymRepr*(conf: ConfigRef; s: PSym): string =
case s.kind
of skProc, skFunc, skMethod, skConverter, skIterator:
of routineKinds, skType:
result = getProcHeader(conf, s)
else:
result = s.name.s
@@ -174,7 +174,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
# maybe they can be made skGenericParam as well.
if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and
s.typ.kind != tyGenericParam:
message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(c.config, s))
message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s)
s = nextIter(it, scope.symbols)
proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string;
@@ -190,7 +190,7 @@ proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) =
wrongRedefinition(c, info, sym.name.s, conflict.info)
proc addDecl*(c: PContext, sym: PSym) =
let conflict = c.currentScope.addUniqueSym(sym)
let conflict = strTableInclReportConflict(c.currentScope.symbols, sym, true)
if conflict != nil:
wrongRedefinition(c, sym.info, sym.name.s, conflict.info)

View File

@@ -76,6 +76,8 @@ proc commandCompileToC(graph: ModuleGraph) =
registerPass(graph, cgenPass)
compileProject(graph)
if graph.config.errorCounter > 0:
return # issue #9933
cgenWriteModules(graph.backend, conf)
if conf.cmd != cmdRun:
let proj = changeFileExt(conf.projectFull, "")
@@ -90,6 +92,7 @@ proc commandJsonScript(graph: ModuleGraph) =
when not defined(leanCompiler):
proc commandCompileToJS(graph: ModuleGraph) =
let conf = graph.config
#incl(gGlobalOptions, optSafeCode)
setTarget(graph.config.target, osJS, cpuJS)
#initDefines()
@@ -98,6 +101,8 @@ when not defined(leanCompiler):
semanticPasses(graph)
registerPass(graph, JSgenPass)
compileProject(graph)
if optGenScript in graph.config.globalOptions:
writeDepsFile(graph, toGeneratedFile(conf, conf.projectFull, ""))
proc interactivePasses(graph: ModuleGraph) =
initDefines(graph.config.symbols)
@@ -165,6 +170,7 @@ proc mainCommand*(graph: ModuleGraph) =
of "c", "cc", "compile", "compiletoc":
# compile means compileToC currently
conf.cmd = cmdCompileToC
defineSymbol(graph.config.symbols, "c")
commandCompileToC(graph)
of "cpp", "compiletocpp":
conf.cmd = cmdCompileToCpp
@@ -281,6 +287,7 @@ proc mainCommand*(graph: ModuleGraph) =
(key: "defined_symbols", val: definedSymbols),
(key: "lib_paths", val: %libpaths),
(key: "out", val: %conf.outFile.string),
(key: "nimcache", val: %getNimcacheDir(conf).string),
(key: "hints", val: hints),
(key: "warnings", val: warnings),
]

View File

@@ -147,7 +147,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
# hacky way to implement 'x / y /../ z':
result = renderTree(n, {renderNoComments}).replace(" ")
of nkDotExpr:
localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths")
localError(conf, n.info, warnDeprecated, "using '.' instead of '/' in import paths is deprecated")
result = renderTree(n, {renderNoComments}).replace(".", "/")
of nkImportAs:
result = getModuleName(conf, n.sons[0])

View File

@@ -135,6 +135,10 @@ const
WarningColor = fgYellow
HintTitle = "Hint: "
HintColor = fgGreen
# NOTE: currently line info line numbers start with 1,
# but column numbers start with 0, however most editors expect
# first column to be 1, so we need to +1 here
ColOffset* = 1
proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len
proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L)
@@ -155,7 +159,10 @@ template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
if fileIdx.int32 < 0 or conf == nil:
"???"
else:
conf.m.fileInfos[fileIdx.int32].projPath.string
if optListFullPaths in conf.globalOptions:
conf.m.fileInfos[fileIdx.int32].fullPath.string
else:
conf.m.fileInfos[fileIdx.int32].projPath.string
proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string =
if fileIdx.int32 < 0 or conf == nil: result = "???"
@@ -192,10 +199,10 @@ proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
result = "???"
return
let absPath = conf.m.fileInfos[info.fileIndex.int32].fullPath.string
let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string
if optListFullPaths in conf.globalOptions:
result = absPath
else:
let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string
result = if absPath.len < relPath.len: absPath else: relPath
proc toLinenumber*(info: TLineInfo): int {.inline.} =
@@ -208,7 +215,9 @@ proc toFileLine*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
result = toFilename(conf, info) & ":" & $info.line
proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
result = toFilename(conf, info) & "(" & $info.line & ", " & $info.col & ")"
# consider calling `helpers.lineInfoToString` instead
result = toFilename(conf, info) & "(" & $info.line & ", " &
$(info.col + ColOffset) & ")"
proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
@@ -359,7 +368,7 @@ proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) =
styledMsgWriteln(styleBright,
PosFormat % [toMsgFilename(conf, context.info),
coordToStr(context.info.line.int),
coordToStr(context.info.col+1)],
coordToStr(context.info.col+ColOffset)],
resetStyle,
message)
info = context.info
@@ -446,7 +455,7 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s
of hintMin..hintMax: HintTitle
else: ErrorTitle
result = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int),
coordToStr(info.col+1)] &
coordToStr(info.col+ColOffset)] &
title &
getMessageStr(msg, arg)
@@ -483,11 +492,8 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
color = HintColor
if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
inc(conf.hintCounter)
# NOTE: currently line info line numbers start with 1,
# but column numbers start with 0, however most editors expect
# first column to be 1, so we need to +1 here
let x = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int),
coordToStr(info.col+1)]
coordToStr(info.col+ColOffset)]
let s = getMessageStr(msg, arg)
if not ignoreMsg:

View File

@@ -40,7 +40,8 @@ type # please make sure we have under 32 options
optMemTracker,
optHotCodeReloading,
optLaxStrings,
optNilSeqs
optNilSeqs,
optOldAst
TOptions* = set[TOption]
TGlobalOption* = enum # **keep binary compatible**
@@ -77,7 +78,7 @@ type # please make sure we have under 32 options
optShowAllMismatches # show all overloading resolution candidates
optWholeProject # for 'doc2': output any dependency
optMixedMode # true if some module triggered C++ codegen
optListFullPaths
optListFullPaths # use full paths in toMsgFilename, toFilename
optNoNimblePath
optDynlibOverrideAll
@@ -132,7 +133,7 @@ type
TSystemCC* = enum
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
ccTcc, ccPcc, ccUcc, ccIcl, ccIcc
ccTcc, ccPcc, ccUcc, ccIcl, ccIcc, ccClangCl
CfileFlag* {.pure.} = enum
Cached, ## no need to recompile this time
@@ -482,16 +483,7 @@ proc setDefaultLibpath*(conf: ConfigRef) =
conf.libpath = AbsoluteDir parentNimLibPath
proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
# on Windows, 'expandFilename' calls getFullPathName which doesn't do
# case corrections, so we have to use this convoluted way of retrieving
# the true filename (see tests/modules and Nimble uses 'import Uri' instead
# of 'import uri'):
when defined(windows):
result = AbsoluteFile path.string.expandFilename
for x in walkFiles(result.string):
return AbsoluteFile x
else:
result = AbsoluteFile path.string.expandFilename
result = AbsoluteFile path.string.expandFilename
proc shortenDir*(conf: ConfigRef; dir: string): string {.
deprecated: "use 'relativeTo' instead".} =

View File

@@ -115,20 +115,19 @@ proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) =
else:
patternError(p, conf)
proc semNodeKindConstraints*(p: PNode; conf: ConfigRef): PNode =
proc semNodeKindConstraints*(n: PNode; conf: ConfigRef; start: Natural): PNode =
## does semantic checking for a node kind pattern and compiles it into an
## efficient internal format.
assert p.kind == nkCurlyExpr
result = newNodeI(nkStrLit, p.info)
result = newNodeI(nkStrLit, n.info)
result.strVal = newStringOfCap(10)
result.strVal.add(chr(aqNone.ord))
if p.len >= 2:
for i in 1..<p.len:
compileConstraints(p.sons[i], result.strVal, conf)
if n.len >= 2:
for i in start..<n.len:
compileConstraints(n[i], result.strVal, conf)
if result.strVal.len > MaxStackSize-1:
internalError(conf, p.info, "parameter pattern too complex")
internalError(conf, n.info, "parameter pattern too complex")
else:
patternError(p, conf)
patternError(n, conf)
result.strVal.add(ppEof)
type

View File

@@ -332,12 +332,15 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
parMessage(p, errIdentifierExpected, p.tok)
break
of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi:
let lineinfo = parLineinfo(p)
var accm = ""
while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals,
tkParLe..tkParDotRi}:
accm.add(tokToStr(p.tok))
getTok(p)
result.add(newIdentNodeP(p.lex.cache.getIdent(accm), p))
let node = newNodeI(nkIdent, lineinfo)
node.ident = p.lex.cache.getIdent(accm)
result.add(node)
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit:
result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p))
getTok(p)
@@ -1764,23 +1767,8 @@ proc parseSection(p: var TParser, kind: TNodeKind,
else:
parMessage(p, errIdentifierExpected, p.tok)
proc parseConstant(p: var TParser): PNode =
#| constant = identWithPragma (colon typeDesc)? '=' optInd expr indAndComment
result = newNodeP(nkConstDef, p)
addSon(result, identWithPragma(p))
if p.tok.tokType == tkColon:
getTok(p)
optInd(p, result)
addSon(result, parseTypeDesc(p))
else:
addSon(result, p.emptyNode)
eat(p, tkEquals)
optInd(p, result)
addSon(result, parseExpr(p))
indAndComment(p, result)
proc parseEnum(p: var TParser): PNode =
#| enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+
#| enum = 'enum' optInd (symbol optPragmas optInd ('=' optInd expr COMMENT?)? comma?)+
result = newNodeP(nkEnumTy, p)
getTok(p)
addSon(result, p.emptyNode)
@@ -1790,25 +1778,35 @@ proc parseEnum(p: var TParser): PNode =
while true:
var a = parseSymbol(p)
if a.kind == nkEmpty: return
var symPragma = a
var pragma: PNode
if p.tok.tokType == tkCurlyDotLe:
pragma = optPragmas(p)
symPragma = newNodeP(nkPragmaExpr, p)
addSon(symPragma, a)
addSon(symPragma, pragma)
if p.tok.indent >= 0 and p.tok.indent <= p.currInd:
add(result, a)
add(result, symPragma)
break
if p.tok.tokType == tkEquals and p.tok.indent < 0:
getTok(p)
optInd(p, a)
var b = a
a = newNodeP(nkEnumFieldDef, p)
addSon(a, b)
addSon(a, parseExpr(p))
optInd(p, symPragma)
var b = symPragma
symPragma = newNodeP(nkEnumFieldDef, p)
addSon(symPragma, b)
addSon(symPragma, parseExpr(p))
if p.tok.indent < 0 or p.tok.indent >= p.currInd:
rawSkipComment(p, a)
rawSkipComment(p, symPragma)
if p.tok.tokType == tkComma and p.tok.indent < 0:
getTok(p)
rawSkipComment(p, a)
rawSkipComment(p, symPragma)
else:
if p.tok.indent < 0 or p.tok.indent >= p.currInd:
rawSkipComment(p, a)
addSon(result, a)
rawSkipComment(p, symPragma)
addSon(result, symPragma)
if p.tok.indent >= 0 and p.tok.indent <= p.currInd or
p.tok.tokType == tkEof:
break
@@ -1920,6 +1918,8 @@ proc parseObject(p: var TParser): PNode =
result = newNodeP(nkObjectTy, p)
getTok(p)
if p.tok.tokType == tkCurlyDotLe and p.validInd:
# Deprecated since v0.20.0
parMessage(p, warnDeprecated, "type pragmas follow the type name; this form of writing pragmas is deprecated")
addSon(result, parsePragma(p))
else:
addSon(result, p.emptyNode)
@@ -1992,13 +1992,42 @@ proc parseTypeClass(p: var TParser): PNode =
proc parseTypeDef(p: var TParser): PNode =
#|
#| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux
#| indAndComment? / identVisDot genericParamList? pragma '=' optInd typeDefAux
#| indAndComment?
result = newNodeP(nkTypeDef, p)
addSon(result, identWithPragma(p, allowDot=true))
var identifier = identVis(p, allowDot=true)
var identPragma = identifier
var pragma: PNode
var genericParam: PNode
var noPragmaYet = true
if p.tok.tokType == tkCurlyDotLe:
pragma = optPragmas(p)
identPragma = newNodeP(nkPragmaExpr, p)
addSon(identPragma, identifier)
addSon(identPragma, pragma)
noPragmaYet = false
if p.tok.tokType == tkBracketLe and p.validInd:
addSon(result, parseGenericParamList(p))
if not noPragmaYet:
# Deprecated since v0.20.0
parMessage(p, warnDeprecated, "pragma before generic parameter list is deprecated")
genericParam = parseGenericParamList(p)
else:
addSon(result, p.emptyNode)
genericParam = p.emptyNode
if noPragmaYet:
pragma = optPragmas(p)
if pragma.kind != nkEmpty:
identPragma = newNodeP(nkPragmaExpr, p)
addSon(identPragma, identifier)
addSon(identPragma, pragma)
elif p.tok.tokType == tkCurlyDotLe:
parMessage(p, errGenerated, "pragma already present")
addSon(result, identPragma)
addSon(result, genericParam)
if p.tok.tokType == tkEquals:
result.info = parLineInfo(p)
getTok(p)
@@ -2035,6 +2064,23 @@ proc parseVariable(p: var TParser): PNode =
result[^1] = postExprBlocks(p, result[^1])
indAndComment(p, result)
proc parseConstant(p: var TParser): PNode =
#| constant = (parseVarTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment
if p.tok.tokType == tkParLe: result = parseVarTuple(p)
else:
result = newNodeP(nkConstDef, p)
addSon(result, identWithPragma(p))
if p.tok.tokType == tkColon:
getTok(p)
optInd(p, result)
addSon(result, parseTypeDesc(p))
else:
addSon(result, p.emptyNode)
eat(p, tkEquals)
optInd(p, result)
addSon(result, parseExpr(p))
indAndComment(p, result)
proc parseBind(p: var TParser, k: TNodeKind): PNode =
#| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
#| mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma

View File

@@ -102,9 +102,9 @@ proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNod
for module in items(implicits):
# implicit imports should not lead to a module importing itself
if m.position != resolveMod(graph.config, module, relativeTo).int32:
var importStmt = newNodeI(nodeKind, gCmdLineInfo)
var importStmt = newNodeI(nodeKind, m.info)
var str = newStrNode(nkStrLit, module)
str.info = gCmdLineInfo
str.info = m.info
importStmt.addSon str
if not processTopLevelStmt(graph, importStmt, a): break
@@ -155,7 +155,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {
while true:
openParsers(p, fileIdx, s, graph.cache, graph.config)
if sfSystemModule notin module.flags:
if module.owner == nil or module.owner.name.s != "stdlib" or module.name.s == "distros":
# XXX what about caching? no processing then? what if I change the
# modules to include between compilation runs? we'd need to track that
# in ROD files. I think we should enable this feature only

View File

@@ -17,11 +17,9 @@ type
AbsoluteDir* = distinct string
RelativeFile* = distinct string
RelativeDir* = distinct string
AnyPath* = AbsoluteFile|AbsoluteDir|RelativeFile|RelativeDir
proc isEmpty*(x: AbsoluteFile): bool {.inline.} = x.string.len == 0
proc isEmpty*(x: AbsoluteDir): bool {.inline.} = x.string.len == 0
proc isEmpty*(x: RelativeFile): bool {.inline.} = x.string.len == 0
proc isEmpty*(x: RelativeDir): bool {.inline.} = x.string.len == 0
proc isEmpty*(x: AnyPath): bool {.inline.} = x.string.len == 0
proc copyFile*(source, dest: AbsoluteFile) =
os.copyFile(source.string, dest.string)
@@ -44,14 +42,13 @@ proc cmpPaths*(x, y: AbsoluteDir): int {.borrow.}
proc createDir*(x: AbsoluteDir) {.borrow.}
proc `$`*(x: AnyPath): string = x.string
when true:
proc eqImpl(x, y: string): bool {.inline.} =
result = cmpPaths(x, y) == 0
proc `==`*(x, y: AbsoluteFile): bool = eqImpl(x.string, y.string)
proc `==`*(x, y: AbsoluteDir): bool = eqImpl(x.string, y.string)
proc `==`*(x, y: RelativeFile): bool = eqImpl(x.string, y.string)
proc `==`*(x, y: RelativeDir): bool = eqImpl(x.string, y.string)
proc `==`*[T: AnyPath](x, y: T): bool = eqImpl(x.string, y.string)
proc `/`*(base: AbsoluteDir; f: RelativeFile): AbsoluteFile =
#assert isAbsolute(base.string)
@@ -88,6 +85,12 @@ when true:
when isMainModule:
doAssert AbsoluteDir"/Users/me///" / RelativeFile"z.nim" == AbsoluteFile"/Users/me/z.nim"
doAssert relativePath("/foo/bar.nim", "/foo/", '/') == "bar.nim"
doAssert $RelativeDir"foo/bar" == "foo/bar"
doAssert RelativeDir"foo/bar" == RelativeDir"foo/bar"
doAssert RelativeFile"foo/bar".changeFileExt(".txt") == RelativeFile"foo/bar.txt"
doAssert RelativeFile"foo/bar".addFileExt(".txt") == RelativeFile"foo/bar.txt"
doAssert not RelativeDir"foo/bar".isEmpty
doAssert RelativeDir"".isEmpty
when isMainModule and defined(windows):
let nasty = string(AbsoluteDir(r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\linkedPkgs\pkgB-#head\../../simplePkgs/pkgB-#head/") / RelativeFile"pkgA/module.nim")

View File

@@ -73,6 +73,7 @@ const
wThread, wRaises, wLocks, wTags, wGcSafe}
forVarPragmas* = {wInject, wGensym}
allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
enumFieldPragmas* = {wDeprecated}
proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
let p = procAst[pragmasPos]
@@ -241,7 +242,7 @@ proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
# deprecated as of 0.18.1
message(c.config, n.info, warnDeprecated,
"use {.experimental: \"codeReordering.\".} instead; " &
(if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}"))
(if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}") & " is deprecated")
proc processCallConv(c: PContext, n: PNode) =
if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent:
@@ -446,14 +447,14 @@ proc processPop(c: PContext, n: PNode) =
proc processDefine(c: PContext, n: PNode) =
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
defineSymbol(c.config.symbols, n[1].ident.s)
message(c.config, n.info, warnDeprecated, "define")
message(c.config, n.info, warnDeprecated, "define is deprecated")
else:
invalidPragma(c, n)
proc processUndef(c: PContext, n: PNode) =
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
undefSymbol(c.config.symbols, n[1].ident.s)
message(c.config, n.info, warnDeprecated, "undef")
message(c.config, n.info, warnDeprecated, "undef is deprecated")
else:
invalidPragma(c, n)
@@ -740,7 +741,7 @@ proc semCustomPragma(c: PContext, n: PNode): PNode =
result.kind = n.kind # pragma(arg) -> pragma: arg
proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
validPragmas: TSpecialWords): bool =
validPragmas: TSpecialWords, comesFromPush: bool) : bool =
var it = n.sons[i]
var key = if it.kind in nkPragmaCallKinds and it.len > 1: it.sons[0] else: it
if key.kind == nkBracketExpr:
@@ -783,7 +784,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
if sym.kind in {skTemplate, skMacro}:
incl(sym.flags, sfImmediate)
incl(sym.flags, sfAllUntyped)
message(c.config, n.info, warnDeprecated, "use 'untyped' parameters instead; immediate")
message(c.config, n.info, warnDeprecated, "use 'untyped' parameters instead; immediate is deprecated")
else: invalidPragma(c, it)
of wDirty:
if sym.kind == skTemplate: incl(sym.flags, sfDirty)
@@ -880,7 +881,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
of wExplain:
sym.flags.incl sfExplain
of wDeprecated:
if sym != nil and sym.kind in routineKinds + {skType}:
if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet}:
if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
incl(sym.flags, sfDeprecated)
elif sym != nil and sym.kind != skModule:
@@ -1090,10 +1091,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
of wThis:
if it.kind in nkPragmaCallKinds and it.len == 2:
c.selfName = considerQuotedIdent(c, it[1])
message(c.config, n.info, warnDeprecated, "the '.this' pragma")
message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated")
elif it.kind == nkIdent or it.len == 1:
c.selfName = getIdent(c.cache, "self")
message(c.config, n.info, warnDeprecated, "the '.this' pragma")
message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated")
else:
localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
of wNoRewrite:
@@ -1111,15 +1112,25 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
else: sym.flags.incl sfUsed
of wLiftLocals: discard
else: invalidPragma(c, it)
elif sym == nil or (sym != nil and sym.kind in {skVar, skLet, skParam,
skField, skProc, skFunc, skConverter, skMethod, skType}):
n.sons[i] = semCustomPragma(c, it)
elif sym != nil:
illegalCustomPragma(c, it, sym)
elif comesFromPush and whichKeyword(ident) in {wTags, wRaises}:
discard "ignore the .push pragma; it doesn't apply"
else:
invalidPragma(c, it)
if sym == nil or (sym != nil and sym.kind in {skVar, skLet, skParam,
skField, skProc, skFunc, skConverter, skMethod, skType}):
n.sons[i] = semCustomPragma(c, it)
elif sym != nil:
illegalCustomPragma(c, it, sym)
else:
invalidPragma(c, it)
proc overwriteLineInfo(n: PNode; info: TLineInfo) =
n.info = info
for i in 0..<safeLen(n):
overwriteLineInfo(n[i], info)
proc mergePragmas(n, pragmas: PNode) =
var pragmas = copyTree(pragmas)
overwriteLineInfo pragmas, n.info
if n[pragmasPos].kind == nkEmpty:
n[pragmasPos] = pragmas
else:
@@ -1134,7 +1145,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
pushInfoContext(c.config, n.info)
var i = 0
while i < o.len:
if singlePragma(c, sym, o, i, validPragmas):
if singlePragma(c, sym, o, i, validPragmas, true):
internalError(c.config, n.info, "implicitPragmas")
inc i
popInfoContext(c.config)
@@ -1163,7 +1174,7 @@ proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
if n == nil: return
var i = 0
while i < n.len:
if singlePragma(c, sym, n, i, validPragmas): break
if singlePragma(c, sym, n, i, validPragmas, false): break
inc i
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =

View File

@@ -42,15 +42,18 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
proc (a: VmArgs) =
body
template cbos(name, body) {.dirty.} =
template cbexc(name, exc, body) {.dirty.} =
result.registerCallback "stdlib.system." & astToStr(name),
proc (a: VmArgs) =
errorMsg = ""
try:
body
except OSError:
except exc:
errorMsg = getCurrentExceptionMsg()
template cbos(name, body) {.dirty.} =
cbexc(name, OSError, body)
# Idea: Treat link to file as a file, but ignore link to directory to prevent
# endless recursions out of the box.
cbos listFiles:
@@ -63,8 +66,10 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
os.removeFile getString(a, 0)
cbos createDir:
os.createDir getString(a, 0)
cbos getOsError:
setResult(a, errorMsg)
result.registerCallback "stdlib.system.getError",
proc (a: VmArgs) = setResult(a, errorMsg)
cbos setCurrentDir:
os.setCurrentDir getString(a, 0)
cbos getCurrentDir:
@@ -155,6 +160,12 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
setResult(a, os.getAppFilename())
cbconf cppDefine:
options.cppDefine(conf, a.getString(0))
cbexc stdinReadLine, EOFError:
setResult(a, "")
setResult(a, stdin.readLine())
cbexc stdinReadAll, EOFError:
setResult(a, "")
setResult(a, stdin.readAll())
proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
freshDefines=true; conf: ConfigRef) =

View File

@@ -556,8 +556,6 @@ proc isEmptyTree(n: PNode): bool =
else: result = false
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
if n.kind == nkDefer:
localError(c.config, n.info, "defer statement not supported at top level")
if c.topStmts == 0 and not isImportSystemStmt(c.graph, n):
if sfSystemModule notin c.module.flags and not isEmptyTree(n):
c.importTable.addSym c.graph.systemModule # import the "System" identifier

View File

@@ -14,7 +14,7 @@
type
TLiftCtx = object
c: PContext
graph: ModuleGraph
info: TLineInfo # for construction
kind: TTypeAttachedOp
fn: PSym
@@ -22,7 +22,7 @@ type
recurse: bool
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode)
proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym {.discardable.}
proc at(a, i: PNode, elemType: PType): PNode =
@@ -33,7 +33,7 @@ proc at(a, i: PNode, elemType: PType): PNode =
proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
for i in 0 ..< t.len:
let lit = lowerings.newIntLit(c.c.graph, x.info, i)
let lit = lowerings.newIntLit(c.graph, x.info, i)
liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
proc dotField(x: PNode, f: PSym): PNode =
@@ -49,6 +49,14 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
liftBodyAux(c, f.typ, body, x.dotField(f), y.dotField(f))
of nkNilLit: discard
of nkRecCase:
if c.kind in {attachedSink, attachedAsgn, attachedDeepCopy}:
## the value needs to be destroyed before we assign the selector
## or the value is lost
let prevKind = c.kind
c.kind = attachedDestructor
liftBodyObj(c, n, body, x, y)
c.kind = prevKind
# copy the selector:
liftBodyObj(c, n[0], body, x, y)
# we need to generate a case statement:
@@ -66,26 +74,25 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
caseStmt.add(branch)
body.add(caseStmt)
localError(c.c.config, c.info, "cannot lift assignment operator to 'case' object")
of nkRecList:
for t in items(n): liftBodyObj(c, t, body, x, y)
else:
illFormedAstLocal(n, c.c.config)
illFormedAstLocal(n, c.graph.config)
proc genAddr(c: PContext; x: PNode): PNode =
proc genAddr(g: ModuleGraph; x: PNode): PNode =
if x.kind == nkHiddenDeref:
checkSonsLen(x, 1, c.config)
checkSonsLen(x, 1, g.config)
result = x.sons[0]
else:
result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ))
result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ))
addSon(result, x)
proc newAsgnCall(c: PContext; op: PSym; x, y: PNode): PNode =
proc newAsgnCall(g: ModuleGraph; op: PSym; x, y: PNode): PNode =
#if sfError in op.flags:
# localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s)
result = newNodeI(nkCall, x.info)
result.add newSymNode(op)
result.add genAddr(c, x)
result.add genAddr(g, x)
result.add y
proc newAsgnStmt(le, ri: PNode): PNode =
@@ -98,10 +105,10 @@ proc newOpCall(op: PSym; x: PNode): PNode =
result.add(newSymNode(op))
result.add x
proc destructorCall(c: PContext; op: PSym; x: PNode): PNode =
proc destructorCall(g: ModuleGraph; op: PSym; x: PNode): PNode =
result = newNodeIT(nkCall, x.info, op.typ.sons[0])
result.add(newSymNode(op))
result.add genAddr(c, x)
result.add genAddr(g, x)
proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
result = newAsgnStmt(x, newOpCall(op, y))
@@ -120,13 +127,13 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
else:
op = field
if op == nil:
op = liftBody(c.c, t, c.kind, c.info)
op = liftBody(c.graph, t, c.kind, c.info)
if sfError in op.flags:
incl c.fn.flags, sfError
else:
markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
onUse(c.info, op)
body.add newAsgnCall(c.c, op, x, y)
body.add newAsgnCall(c.graph, op, x, y)
result = true
proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
@@ -134,9 +141,9 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
of attachedDestructor:
let op = t.destructor
if op != nil:
markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
onUse(c.info, op)
body.add destructorCall(c.c, op, x)
body.add destructorCall(c.graph, op, x)
result = true
of attachedAsgn:
result = considerAsgnOrSink(c, t, body, x, y, t.assignment)
@@ -145,7 +152,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
of attachedDeepCopy:
let op = t.deepCopy
if op != nil:
markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
onUse(c.info, op)
body.add newDeepCopyCall(op, x, y)
result = true
@@ -162,13 +169,13 @@ proc addVar(father, v, value: PNode) =
addSon(father, vpart)
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
var temp = newSym(skTemp, getIdent(c.c.cache, lowerings.genPrefix), c.fn, c.info)
temp.typ = getSysType(c.c.graph, body.info, tyInt)
var temp = newSym(skTemp, getIdent(c.graph.cache, lowerings.genPrefix), c.fn, c.info)
temp.typ = getSysType(c.graph, body.info, tyInt)
incl(temp.flags, sfFromGeneric)
var v = newNodeI(nkVarSection, c.info)
result = newSymNode(temp)
v.addVar(result, lowerings.newIntLit(c.c.graph, body.info, first))
v.addVar(result, lowerings.newIntLit(c.graph, body.info, first))
body.add v
proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
@@ -178,22 +185,22 @@ proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
result = newNodeI(nkWhileStmt, c.info, 2)
let cmp = genBuiltin(c.c.graph, mLeI, "<=", i)
cmp.add genHigh(c.c.graph, dest)
cmp.typ = getSysType(c.c.graph, c.info, tyBool)
let cmp = genBuiltin(c.graph, mLeI, "<=", i)
cmp.add genHigh(c.graph, dest)
cmp.typ = getSysType(c.graph, c.info, tyBool)
result.sons[0] = cmp
result.sons[1] = newNodeI(nkStmtList, c.info)
proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
let incCall = genBuiltin(c.c.graph, mInc, "inc", i)
incCall.add lowerings.newIntLit(c.c.graph, c.info, 1)
let incCall = genBuiltin(c.graph, mInc, "inc", i)
incCall.add lowerings.newIntLit(c.graph, c.info, 1)
body.add incCall
proc newSeqCall(c: PContext; x, y: PNode): PNode =
proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode =
# don't call genAddr(c, x) here:
result = genBuiltin(c.graph, mNewSeq, "newSeq", x)
let lenCall = genBuiltin(c.graph, mLengthSeq, "len", y)
lenCall.typ = getSysType(c.graph, x.info, tyInt)
result = genBuiltin(g, mNewSeq, "newSeq", x)
let lenCall = genBuiltin(g, mLengthSeq, "len", y)
lenCall.typ = getSysType(g, x.info, tyInt)
result.add lenCall
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
@@ -204,7 +211,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
defaultOp(c, t, body, x, y)
of tyArray:
if tfHasAsgn in t.flags:
let i = declareCounter(c, body, firstOrd(c.c.config, t))
let i = declareCounter(c, body, firstOrd(c.graph.config, t))
let whileLoop = genWhileLoop(c, i, x)
let elemType = t.lastSon
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
@@ -216,12 +223,12 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of tySequence:
# note that tfHasAsgn is propagated so we need the check on
# 'selectedGC' here to determine if we have the new runtime.
if c.c.config.selectedGC == gcDestructors:
if c.graph.config.selectedGC == gcDestructors:
discard considerOverloadedOp(c, t, body, x, y)
elif tfHasAsgn in t.flags:
if c.kind != attachedDestructor:
body.add newSeqCall(c.c, x, y)
let i = declareCounter(c, body, firstOrd(c.c.config, t))
body.add newSeqCall(c.graph, x, y)
let i = declareCounter(c, body, firstOrd(c.graph.config, t))
let whileLoop = genWhileLoop(c, i, x)
let elemType = t.lastSon
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
@@ -235,11 +242,12 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
discard considerOverloadedOp(c, t, body, x, y)
else:
defaultOp(c, t, body, x, y)
of tyObject, tyDistinct:
of tyObject:
if not considerOverloadedOp(c, t, body, x, y):
if t.sons[0] != nil:
liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
if t.kind == tyObject: liftBodyObj(c, t.n, body, x, y)
liftBodyObj(c, t.n, body, x, y)
of tyDistinct:
if not considerOverloadedOp(c, t, body, x, y):
liftBodyAux(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
of tyTuple:
liftBodyTup(c, t, body, x, y)
of tyProc:
@@ -250,20 +258,20 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
# have to go through some indirection; we delegate this to the codegen:
let call = newNodeI(nkCall, c.info, 2)
call.typ = t
call.sons[0] = newSymNode(createMagic(c.c.graph, "deepCopy", mDeepCopy))
call.sons[0] = newSymNode(createMagic(c.graph, "deepCopy", mDeepCopy))
call.sons[1] = y
body.add newAsgnStmt(x, call)
of tyVarargs, tyOpenArray:
localError(c.c.config, c.info, "cannot copy openArray")
localError(c.graph.config, c.info, "cannot copy openArray")
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
tyTypeDesc, tyGenericInvocation, tyForward:
internalError(c.c.config, c.info, "assignment requested for type: " & typeToString(t))
internalError(c.graph.config, c.info, "assignment requested for type: " & typeToString(t))
of tyOrdinal, tyRange, tyInferred,
tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink:
liftBodyAux(c, lastSon(t), body, x, y)
of tyOptAsRef: internalError(c.c.config, "liftBodyAux")
of tyOptAsRef: internalError(c.graph.config, "liftBodyAux")
proc newProcType(info: TLineInfo; owner: PSym): PType =
result = newType(tyProc, owner)
@@ -279,26 +287,54 @@ proc addParam(procType: PType; param: PSym) =
addSon(procType.n, newSymNode(param))
rawAddSon(procType, param.typ)
proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
proc liftBodyDistinctType(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
assert typ.kind == tyDistinct
let baseType = typ[0]
case kind
of attachedAsgn:
if baseType.assignment == nil:
discard liftBody(g, baseType, kind, info)
typ.assignment = baseType.assignment
result = typ.assignment
of attachedSink:
if baseType.sink == nil:
discard liftBody(g, baseType, kind, info)
typ.sink = baseType.sink
result = typ.sink
of attachedDeepCopy:
if baseType.deepCopy == nil:
discard liftBody(g, baseType, kind, info)
typ.deepCopy = baseType.deepCopy
result = typ.deepCopy
of attachedDestructor:
if baseType.destructor == nil:
discard liftBody(g, baseType, kind, info)
typ.destructor = baseType.destructor
result = typ.destructor
proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo): PSym =
if typ.kind == tyDistinct:
return liftBodyDistinctType(g, typ, kind, info)
var a: TLiftCtx
a.info = info
a.c = c
a.graph = g
a.kind = kind
let body = newNodeI(nkStmtList, info)
let procname = case kind
of attachedAsgn: getIdent(c.cache, "=")
of attachedSink: getIdent(c.cache, "=sink")
of attachedDeepCopy: getIdent(c.cache, "=deepcopy")
of attachedDestructor: getIdent(c.cache, "=destroy")
of attachedAsgn: getIdent(g.cache, "=")
of attachedSink: getIdent(g.cache, "=sink")
of attachedDeepCopy: getIdent(g.cache, "=deepcopy")
of attachedDestructor: getIdent(g.cache, "=destroy")
result = newSym(skProc, procname, typ.owner, info)
a.fn = result
a.asgnForType = typ
let dest = newSym(skParam, getIdent(c.cache, "dest"), result, info)
let src = newSym(skParam, getIdent(c.cache, "src"), result, info)
dest.typ = makeVarType(c, typ)
let dest = newSym(skParam, getIdent(g.cache, "dest"), result, info)
let src = newSym(skParam, getIdent(g.cache, "src"), result, info)
dest.typ = makeVarType(typ.owner, typ)
src.typ = typ
result.typ = newProcType(info, typ.owner)
@@ -309,7 +345,7 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
liftBodyAux(a, typ, body, newSymNode(dest).newDeref, newSymNode(src))
# recursion is handled explicitly, do not register the type based operation
# before 'liftBodyAux':
if c.config.selectedGC == gcDestructors and
if g.config.selectedGC == gcDestructors and
typ.kind in {tySequence, tyString} and body.len == 0:
discard "do not cache it yet"
else:
@@ -328,17 +364,17 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
incl result.flags, sfFromGeneric
proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
proc getAsgnOrLiftBody(g: ModuleGraph; typ: PType; info: TLineInfo): PSym =
let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
result = t.assignment
if result.isNil:
result = liftBody(c, t, attachedAsgn, info)
result = liftBody(g, t, attachedAsgn, info)
proc overloadedAsgn(c: PContext; dest, src: PNode): PNode =
let a = getAsgnOrLiftBody(c, dest.typ, dest.info)
result = newAsgnCall(c, a, dest, src)
proc overloadedAsgn(g: ModuleGraph; dest, src: PNode): PNode =
let a = getAsgnOrLiftBody(g, dest.typ, dest.info)
result = newAsgnCall(g, a, dest, src)
proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
proc liftTypeBoundOps*(g: ModuleGraph; typ: PType; info: TLineInfo) =
## In the semantic pass this is called in strategic places
## to ensure we lift assignment, destructors and moves properly.
## The later 'destroyer' pass depends on it.
@@ -350,11 +386,11 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
let typ = typ.skipTypes({tyGenericInst, tyAlias})
# we generate the destructor first so that other operators can depend on it:
if typ.destructor == nil:
liftBody(c, typ, attachedDestructor, info)
liftBody(g, typ, attachedDestructor, info)
if typ.assignment == nil:
liftBody(c, typ, attachedAsgn, info)
liftBody(g, typ, attachedAsgn, info)
if typ.sink == nil:
liftBody(c, typ, attachedSink, info)
liftBody(g, typ, attachedSink, info)
#proc patchResolvedTypeBoundOp*(c: PContext; n: PNode): PNode =
#proc patchResolvedTypeBoundOp*(g: ModuleGraph; n: PNode): PNode =
# if n.kind == nkCall and

View File

@@ -287,7 +287,18 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
let ident = considerQuotedIdent(c, f, n).s
if nfDotField in n.flags and nfExplicitCall notin n.flags:
result = errUndeclaredField % ident & result
let sym = n.sons[1].typ.sym
var typeHint = ""
if sym == nil:
# Perhaps we're in a `compiles(foo.bar)` expression, or
# in a concept, eg:
# ExplainedConcept {.explain.} = concept x
# x.foo is int
# We coudl use: `(c.config $ n.sons[1].info)` to get more context.
discard
else:
typeHint = " for type " & getProcHeader(c.config, sym)
result = errUndeclaredField % ident & typeHint & " " & result
else:
if result.len == 0: result = errUndeclaredRoutine % ident
else: result = errBadRoutine % [ident, result]

View File

@@ -286,6 +286,13 @@ proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType =
result = newTypeS(kind, c)
addSonSkipIntLit(result, baseType)
proc makeVarType*(owner: PSym, baseType: PType; kind = tyVar): PType =
if baseType.kind == kind:
result = baseType
else:
result = newType(kind, owner)
addSonSkipIntLit(result, baseType)
proc makeTypeDesc*(c: PContext, typ: PType): PType =
if typ.kind == tyTypeDesc:
result = typ

View File

@@ -108,6 +108,8 @@ const
proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
result = convOK
# We're interested in the inner type and not in the static tag
var src = src.skipTypes({tyStatic})
if sameType(castDest, src) and castDest.sym == src.sym:
# don't annoy conversions that may be needed on another processor:
if castDest.kind notin IntegralTypes+{tyRange}:
@@ -230,7 +232,7 @@ proc semConv(c: PContext, n: PNode): PNode =
# special case to make MyObject(x = 3) produce a nicer error message:
if n[1].kind == nkExprEqExpr and
targetType.skipTypes(abstractPtrs).kind == tyObject:
localError(c.config, n.info, "object contruction uses ':', not '='")
localError(c.config, n.info, "object construction uses ':', not '='")
var op = semExprWithType(c, n.sons[1])
if targetType.isMetaType:
let final = inferWithMetatype(c, targetType, op, true)
@@ -403,8 +405,8 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
n[1] = makeTypeSymNode(c, lhsType, n[1].info)
lhsType = n[1].typ
else:
internalAssert c.config, lhsType.base.kind != tyNone
if c.inGenericContext > 0 and lhsType.base.containsGenericType:
if lhsType.base.kind == tyNone or
(c.inGenericContext > 0 and lhsType.base.containsGenericType):
# BUGFIX: don't evaluate this too early: ``T is void``
return
@@ -710,16 +712,20 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
let a = getConstExpr(c.module, n.sons[i], c.graph)
if a == nil: return n
call.add(a)
#echo "NOW evaluating at compile time: ", call.renderTree
if sfCompileTime in callee.flags:
result = evalStaticExpr(c.module, c.graph, call, c.p.owner)
if result.isNil:
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
else: result = fixupTypeAfterEval(c, result, n)
if c.inStaticContext == 0 or sfNoSideEffect in callee.flags:
if sfCompileTime in callee.flags:
result = evalStaticExpr(c.module, c.graph, call, c.p.owner)
if result.isNil:
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(call))
else: result = fixupTypeAfterEval(c, result, n)
else:
result = evalConstExpr(c.module, c.graph, call)
if result.isNil: result = n
else: result = fixupTypeAfterEval(c, result, n)
else:
result = evalConstExpr(c.module, c.graph, call)
if result.isNil: result = n
else: result = fixupTypeAfterEval(c, result, n)
result = n
#if result != n:
# echo "SUCCESS evaluated at compile time: ", call.renderTree
@@ -798,7 +804,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
result = magicsAfterOverloadResolution(c, result, flags)
if result.typ != nil and
not (result.typ.kind == tySequence and result.typ.sons[0].kind == tyEmpty):
liftTypeBoundOps(c, result.typ, n.info)
liftTypeBoundOps(c.graph, result.typ, n.info)
#result = patchResolvedTypeBoundOp(c, result)
if c.matchedConcept == nil:
result = evalAtCompileTime(c, result)
@@ -913,7 +919,7 @@ proc semExprNoType(c: PContext, n: PNode): PNode =
let isPush = hintExtendedContext in c.config.notes
if isPush: pushInfoContext(c.config, n.info)
result = semExpr(c, n, {efWantStmt})
discardCheck(c, result, {})
result = discardCheck(c, result, {})
if isPush: popInfoContext(c.config)
proc isTypeExpr(n: PNode): bool =
@@ -1039,7 +1045,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
of skConst:
markUsed(c.config, n.info, s, c.graph.usageSym)
onUse(n.info, s)
case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind
let typ = skipTypes(s.typ, abstractInst-{tyTypeDesc})
case typ.kind
of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
tyTuple, tySet, tyUInt..tyUInt64:
if s.magic == mNone: result = inlineConst(c, n, s)
@@ -1057,6 +1064,12 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
# deal with two different ``[]``.
if s.ast.len == 0: result = inlineConst(c, n, s)
else: result = newSymNode(s, n.info)
of tyStatic:
if typ.n != nil:
result = typ.n
result.typ = typ.base
else:
result = newSymNode(s, n.info)
else:
result = newSymNode(s, n.info)
of skMacro:
@@ -1581,7 +1594,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
typeMismatch(c.config, n.info, lhs.typ, rhsTyp)
n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1]))
liftTypeBoundOps(c, lhs.typ, lhs.info)
liftTypeBoundOps(c.graph, lhs.typ, lhs.info)
#liftTypeBoundOps(c, n.sons[0].typ, n.sons[0].info)
fixAbstractType(c, n)
@@ -1628,7 +1641,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
a.sons[1] = result
result = semAsgn(c, a)
else:
discardCheck(c, result, {})
result = discardCheck(c, result, {})
if c.p.owner.kind notin {skMacro, skTemplate} and
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
@@ -2324,7 +2337,6 @@ proc semExportExcept(c: PContext, n: PNode): PNode =
proc semExport(c: PContext, n: PNode): PNode =
result = newNodeI(nkExportStmt, n.info)
for i in 0..<n.len:
let a = n.sons[i]
var o: TOverloadIter
@@ -2343,6 +2355,9 @@ proc semExport(c: PContext, n: PNode): PNode =
it = nextIter(ti, s.tab)
else:
while s != nil:
if s.kind == skEnumField:
localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a) &
"; enum field cannot be exported individually")
if s.kind in ExportableSymKinds+{skModule}:
result.add(newSymNode(s, a.info))
strTableAdd(c.module.tab, s)
@@ -2430,7 +2445,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result.kind = nkCall
result = semExpr(c, result, flags)
of nkBind:
message(c.config, n.info, warnDeprecated, "bind")
message(c.config, n.info, warnDeprecated, "bind is deprecated")
result = semExpr(c, n.sons[0], flags)
of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy:
if c.matchedConcept != nil and n.len == 1:
@@ -2620,6 +2635,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkStaticStmt:
result = semStaticStmt(c, n)
of nkDefer:
if c.currentScope == c.topLevelScope:
localError(c.config, n.info, "defer statement not supported at top level")
n.sons[0] = semExpr(c, n.sons[0])
if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]):
localError(c.config, n.info, "'defer' takes a 'void' expression")

View File

@@ -19,6 +19,9 @@ type
c: PContext
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
if c.field != nil and isEmptyType(c.field.typ):
result = newNode(nkEmpty)
return
case n.kind
of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
of nkIdent, nkSym:

View File

@@ -132,7 +132,9 @@ proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
return field.name.s
else:
return field.ast.strVal
internalError(g.config, a.info, "no symbol for ordinal value: " & $x)
localError(g.config, a.info,
"Cannot convert int literal to $1. The value is invalid." %
[typeToString(t)])
else:
result = $x

View File

@@ -129,14 +129,15 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
template typeWithSonsResult(kind, sons): PNode =
newTypeWithSons(context, kind, sons).toNode(traitCall.info)
case trait.sym.name.s
let s = trait.sym.name.s
case s
of "or", "|":
return typeWithSonsResult(tyOr, @[operand, operand2])
of "and":
return typeWithSonsResult(tyAnd, @[operand, operand2])
of "not":
return typeWithSonsResult(tyNot, @[operand])
of "name":
of "name", "$":
result = newStrNode(nkStrLit, operand.typeToString(preferTypeName))
result.typ = newType(tyString, context)
result.info = traitCall.info
@@ -160,7 +161,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
hasDestructor(t)
result = newIntNodeT(ord(not complexObj), traitCall, c.graph)
else:
localError(c.config, traitCall.info, "unknown trait")
localError(c.config, traitCall.info, "unknown trait: " & s)
result = newNodeI(nkEmpty, traitCall.info)
proc semTypeTraits(c: PContext, n: PNode): PNode =

View File

@@ -95,13 +95,6 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode =
elif efInTypeof in flags:
result.typ = n[1].typ
proc toCover(c: PContext, t: PType): BiggestInt =
let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
if t2.kind == tyEnum and enumHasHoles(t2):
result = sonsLen(t2.n)
else:
result = lengthOrd(c.config, skipTypes(t, abstractVar-{tyTypeDesc}))
proc semProc(c: PContext, n: PNode): PNode
proc semExprBranch(c: PContext, n: PNode; flags: TExprFlags = {}): PNode =
@@ -137,13 +130,13 @@ proc fixNilType(c: PContext; n: PNode) =
for it in n: fixNilType(c, it)
n.typ = nil
proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
proc discardCheck(c: PContext, expr: PNode, flags: TExprFlags): PNode =
result = expr
if c.matchedConcept != nil or efInTypeof in flags: return
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
if implicitlyDiscardable(result):
var n = newNodeI(nkDiscardStmt, result.info, 1)
n[0] = result
result = newNode(nkDiscardStmt, result.info, @[result])
elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
var n = result
while n.kind in skipForDiscardable: n = n.lastSon
@@ -175,7 +168,8 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode =
else: illFormedAst(it, c.config)
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
(not hasElse and efInTypeof notin flags):
for it in n: discardCheck(c, it.lastSon, flags)
for it in n:
it.sons[^1] = discardCheck(c, it.sons[^1], flags)
result.kind = nkIfStmt
# propagate any enforced VoidContext:
if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext
@@ -273,12 +267,14 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
dec c.p.inTryStmt
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
discardCheck(c, n.sons[0], flags)
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
n.sons[0] = discardCheck(c, n.sons[0], flags)
for i in 1..n.len-1:
n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags)
if typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
else:
if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags)
if n.lastSon.kind == nkFinally:
n.sons[^1].sons[^1] = discardCheck(c, n.sons[^1].sons[^1], flags)
n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info)
for i in 1..last:
var it = n.sons[i]
@@ -322,14 +318,24 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
result = semIdentWithPragma(c, kind, n, {})
if result.owner.kind == skModule:
incl(result.flags, sfGlobal)
suggestSym(c.config, n.info, result, c.graph.usageSym)
proc getLineInfo(n: PNode): TLineInfo =
case n.kind
of nkPostfix:
getLineInfo(n.sons[1])
of nkAccQuoted, nkPragmaExpr:
getLineInfo(n.sons[0])
else:
n.info
let info = getLineInfo(n)
suggestSym(c.config, info, result, c.graph.usageSym)
proc checkNilable(c: PContext; v: PSym) =
if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and
{tfNotNil, tfNeedsInit} * v.typ.flags != {}:
if v.ast.isNil:
if v.astdef.isNil:
message(c.config, v.info, warnProveInit, v.name.s)
elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
elif tfNotNil in v.typ.flags and tfNotNil notin v.astdef.typ.flags:
message(c.config, v.info, warnProveInit, v.name.s)
include semasgn
@@ -470,7 +476,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
# this can only happen for errornous var statements:
if typ == nil: continue
typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {})
liftTypeBoundOps(c, typ, a.info)
liftTypeBoundOps(c.graph, typ, a.info)
var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
if a.kind == nkVarTuple:
if tup.kind != tyTuple:
@@ -515,8 +521,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
message(c.config, a.info, warnShadowIdent, v.name.s)
if a.kind != nkVarTuple:
if def.kind != nkEmpty:
# this is needed for the evaluation pass and for the guard checking:
v.ast = def
if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit)
setVarType(c, v, typ)
b = newNodeI(nkIdentDefs, a.info)
@@ -528,6 +532,23 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
addSon(b, a.sons[length-2])
addSon(b, copyTree(def))
addToVarSection(c, result, n, b)
if optOldAst in c.config.options:
if def.kind != nkEmpty:
v.ast = def
else:
# this is needed for the evaluation pass, guard checking
# and custom pragmas:
var ast = newNodeI(nkIdentDefs, a.info)
if a[j].kind == nkPragmaExpr:
var p = newNodeI(nkPragmaExpr, a.info)
p.add newSymNode(v)
p.add a[j][1].copyTree
ast.add p
else:
ast.add newSymNode(v)
ast.add a.sons[length-2].copyTree
ast.add def
v.ast = ast
else:
if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
# bug #7663, for 'nim check' this can be a non-tuple:
@@ -545,21 +566,22 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
proc semConst(c: PContext, n: PNode): PNode =
result = copyNode(n)
inc c.inStaticContext
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
if a.kind == nkCommentStmt: continue
if a.kind != nkConstDef: illFormedAst(a, c.config)
checkSonsLen(a, 3, c.config)
var v = semIdentDef(c, a.sons[0], skConst)
styleCheckDef(c.config, v)
onDef(a[0].info, v)
var typ: PType = nil
if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil)
if a.kind notin {nkConstDef, nkVarTuple}: illFormedAst(a, c.config)
checkMinSonsLen(a, 3, c.config)
var length = sonsLen(a)
var def = semConstExpr(c, a.sons[2])
var typ: PType = nil
if a.sons[length-2].kind != nkEmpty:
typ = semTypeNode(c, a.sons[length-2], nil)
var def = semConstExpr(c, a.sons[length-1])
if def == nil:
localError(c.config, a.sons[2].info, errConstExprExpected)
localError(c.config, a.sons[length-1].info, errConstExprExpected)
continue
# check type compatibility between def.typ and typ:
if typ != nil:
@@ -567,21 +589,44 @@ proc semConst(c: PContext, n: PNode): PNode =
else:
typ = def.typ
if typ == nil:
localError(c.config, a.sons[2].info, errConstExprExpected)
localError(c.config, a.sons[length-1].info, errConstExprExpected)
continue
if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
localError(c.config, a.info, "invalid type for const: " & typeToString(typ))
continue
setVarType(c, v, typ)
v.ast = def # no need to copy
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
elif v.owner == nil: v.owner = getCurrOwner(c)
var b = newNodeI(nkConstDef, a.info)
if importantComments(c.config): b.comment = a.comment
addSon(b, newSymNode(v))
addSon(b, a.sons[1])
addSon(b, copyTree(def))
addSon(result, b)
var b: PNode
if a.kind == nkVarTuple:
if typ.kind != tyTuple:
localError(c.config, a.info, errXExpected, "tuple")
elif length-2 != sonsLen(typ):
localError(c.config, a.info, errWrongNumberOfVariables)
b = newNodeI(nkVarTuple, a.info)
newSons(b, length)
b.sons[length-2] = a.sons[length-2]
b.sons[length-1] = def
for j in countup(0, length-3):
var v = semIdentDef(c, a.sons[j], skConst)
if sfGenSym notin v.flags: addInterfaceDecl(c, v)
elif v.owner == nil: v.owner = getCurrOwner(c)
styleCheckDef(c.config, v)
onDef(a[j].info, v)
if a.kind != nkVarTuple:
setVarType(c, v, typ)
v.ast = def # no need to copy
b = newNodeI(nkConstDef, a.info)
if importantComments(c.config): b.comment = a.comment
addSon(b, newSymNode(v))
addSon(b, a.sons[1])
addSon(b, copyTree(def))
else:
setVarType(c, v, typ.sons[j])
v.ast = def[j]
b.sons[j] = newSymNode(v)
addSon(result,b)
dec c.inStaticContext
include semfields
@@ -637,7 +682,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
openScope(c)
n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
if efInTypeof notin flags:
discardCheck(c, n.sons[length-1], flags)
n.sons[^1] = discardCheck(c, n.sons[^1], flags)
closeScope(c)
dec(c.p.nestedLoopCounter)
@@ -824,7 +869,8 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
closeScope(c)
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
(not hasElse and efInTypeof notin flags):
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon, flags)
for i in 1..n.len-1:
n.sons[i].sons[^1] = discardCheck(c, n.sons[i].sons[^1], flags)
# propagate any enforced VoidContext:
if typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
@@ -1074,6 +1120,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
#debug s.typ
s.ast = a
popOwner(c)
if sfExportc in s.flags and s.typ.kind == tyAlias:
localError(c.config, name.info, "{.exportc.} not allowed for type aliases")
let aa = a.sons[2]
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
aa.sons[0].kind == nkObjectTy:
@@ -1090,9 +1138,13 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
proc checkForMetaFields(c: PContext; n: PNode) =
template checkMeta(t) =
proc checkMeta(c: PContext; n: PNode; t: PType) =
if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
if t.kind == tyBuiltInTypeClass and t.len == 1 and t.sons[0].kind == tyProc:
localError(c.config, n.info, ("'$1' is not a concrete type; " &
"for a callback without parameters use 'proc()'") % t.typeToString)
else:
localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
if n.isNil: return
case n.kind
@@ -1107,9 +1159,9 @@ proc checkForMetaFields(c: PContext; n: PNode) =
tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink:
let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
for i in start ..< t.len:
checkMeta(t.sons[i])
checkMeta(c, n, t.sons[i])
else:
checkMeta(t)
checkMeta(c, n, t)
else:
internalAssert c.config, false
@@ -1205,9 +1257,6 @@ proc semTypeSection(c: PContext, n: PNode): PNode =
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
s.typ = semProcTypeNode(c, n, genericParams, nil, s.kind)
if s.kind notin {skMacro, skTemplate}:
if s.typ.sons[0] != nil and s.typ.sons[0].kind == tyStmt:
localError(c.config, n.info, "invalid return type: 'stmt'")
proc addParams(c: PContext, n: PNode, kind: TSymKind) =
for i in countup(1, sonsLen(n)-1):
@@ -1423,8 +1472,15 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
addResult(c, s.typ.sons[0], n.info, s.kind)
addResultNode(c, n)
proc canonType(c: PContext, t: PType): PType =
if t.kind == tySequence:
result = c.graph.sysTypes[tySequence]
else:
result = t
proc semOverride(c: PContext, s: PSym, n: PNode) =
case s.name.s.normalize
let name = s.name.s.normalize
case name
of "=destroy":
let t = s.typ
var noError = false
@@ -1436,12 +1492,16 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
else: break
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
obj = canonType(c, obj)
if obj.destructor.isNil:
obj.destructor = s
else:
localError(c.config, n.info, errGenerated,
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
noError = true
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
"type bound operation `=destroy` can be defined only in the same module with its type (" & obj.typeToString() & ")")
if not noError and sfSystemModule notin s.owner.flags:
localError(c.config, n.info, errGenerated,
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
@@ -1465,6 +1525,11 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
else:
localError(c.config, n.info, errGenerated,
"cannot bind 'deepCopy' to: " & typeToString(t))
if t.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
"type bound operation `" & name & "` can be defined only in the same module with its type (" & t.typeToString() & ")")
else:
localError(c.config, n.info, errGenerated,
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
@@ -1487,12 +1552,18 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
objB = objB.sons[0]
else: break
if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB):
# attach these ops to the canonical tySequence
obj = canonType(c, obj)
let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink)
if opr[].isNil:
opr[] = s
else:
localError(c.config, n.info, errGenerated,
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
return
if sfSystemModule notin s.owner.flags:
localError(c.config, n.info, errGenerated,
@@ -1542,7 +1613,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
foundObj = true
x.methods.add((col,s))
if not foundObj:
message(c.config, n.info, warnDeprecated, "generic method not attachable to object type")
message(c.config, n.info, warnDeprecated, "generic method not attachable to object type is deprecated")
else:
# why check for the body? bug #2400 has none. Checking for sfForward makes
# no sense either.
@@ -1975,7 +2046,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
elif i != last or voidContext:
discardCheck(c, n.sons[i], flags)
n.sons[i] = discardCheck(c, n.sons[i], flags)
else:
n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr

View File

@@ -80,9 +80,14 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
if isPure: initStrTable(symbols)
var hasNull = false
for i in countup(1, sonsLen(n) - 1):
if n.sons[i].kind == nkEmpty: continue
case n.sons[i].kind
of nkEnumFieldDef:
e = newSymS(skEnumField, n.sons[i].sons[0], c)
if n.sons[i].sons[0].kind == nkPragmaExpr:
e = newSymS(skEnumField, n.sons[i].sons[0].sons[0], c)
pragma(c, e, n.sons[i].sons[0].sons[1], enumFieldPragmas)
else:
e = newSymS(skEnumField, n.sons[i].sons[0], c)
var v = semConstExpr(c, n.sons[i].sons[1])
var strVal: PNode = nil
case skipTypes(v.typ, abstractInst-{tyTypeDesc}).kind
@@ -91,7 +96,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
strVal = v.sons[1] # second tuple part is the string value
if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}:
if not isOrdinalType(v.sons[0].typ, allowEnumWithHoles=true):
localError(c.config, v.sons[0].info, errOrdinalTypeExpected)
localError(c.config, v.sons[0].info, errOrdinalTypeExpected & "; given: " & typeToString(v.sons[0].typ, preferDesc))
x = getOrdValue(v.sons[0]) # first tuple part is the ordinal
else:
localError(c.config, strVal.info, errStringLiteralExpected)
@@ -102,7 +107,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
x = counter
else:
if not isOrdinalType(v.typ, allowEnumWithHoles=true):
localError(c.config, v.info, errOrdinalTypeExpected)
localError(c.config, v.info, errOrdinalTypeExpected & "; given: " & typeToString(v.typ, preferDesc))
x = getOrdValue(v)
if i != 1:
if x != counter: incl(result.flags, tfEnumHasHoles)
@@ -115,6 +120,9 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
e = n.sons[i].sym
of nkIdent, nkAccQuoted:
e = newSymS(skEnumField, n.sons[i], c)
of nkPragmaExpr:
e = newSymS(skEnumField, n.sons[i].sons[0], c)
pragma(c, e, n.sons[i].sons[1], enumFieldPragmas)
else:
illFormedAst(n[i], c.config)
e.typ = result
@@ -133,7 +141,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
wrongRedefinition(c, e.info, e.name.s, conflict.info)
inc(counter)
if not hasNull: incl(result.flags, tfNeedsInit)
if tfNotNil in e.typ.flags and not hasNull: incl(result.flags, tfNeedsInit)
proc semSet(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tySet, prev, c)
@@ -202,7 +210,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
tyError, tyObject}:
message c.config, n[i].info, errGenerated, "region needs to be an object type"
else:
message(c.config, n.info, warnDeprecated, "region for pointer types")
message(c.config, n.info, warnDeprecated, "region for pointer types is deprecated")
addSonSkipIntLit(result, region)
addSonSkipIntLit(result, t)
if tfPartial in result.flags:
@@ -591,6 +599,13 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
for i in lastIndex.succ..(sonsLen(branch) - 2):
checkForOverlap(c, t, i, branchIndex)
proc toCover(c: PContext, t: PType): BiggestInt =
let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
if t2.kind == tyEnum and enumHasHoles(t2):
result = sonsLen(t2.n)
else:
result = lengthOrd(c.config, skipTypes(t, abstractVar-{tyTypeDesc}))
proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
father: PNode, rectype: PType, hasCaseFields = false)
proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
@@ -603,15 +618,16 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
return
incl(a.sons[0].sym.flags, sfDiscriminant)
var covered: BiggestInt = 0
var chckCovered = false
var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc})
if not isOrdinalType(typ):
localError(c.config, n.info, "selector must be of an ordinal type")
elif firstOrd(c.config, typ) != 0:
localError(c.config, n.info, "low(" & $a.sons[0].sym.name.s &
") must be 0 for discriminant")
elif lengthOrd(c.config, typ) > 0x00007FFF:
localError(c.config, n.info, "len($1) must be less than 32768" % a.sons[0].sym.name.s)
var chckCovered = true
case typ.kind
of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool, tyRange:
chckCovered = true
of tyFloat..tyFloat128, tyString, tyError:
discard
else:
if not isOrdinalType(typ):
localError(c.config, n.info, "selector must be of an ordinal type, float or string")
for i in countup(1, sonsLen(n) - 1):
var b = copyTree(n.sons[i])
addSon(a, b)
@@ -620,12 +636,14 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
checkMinSonsLen(b, 2, c.config)
semCaseBranch(c, a, b, i, covered)
of nkElse:
chckCovered = false
checkSonsLen(b, 1, c.config)
if chckCovered and covered == toCover(c, a.sons[0].typ):
localError(c.config, b.info, "invalid else, all cases are already covered")
chckCovered = false
else: illFormedAst(n, c.config)
delSon(b, sonsLen(b) - 1)
semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype, hasCaseFields = true)
if chckCovered and covered != lengthOrd(c.config, a.sons[0].typ):
if chckCovered and covered != toCover(c, a.sons[0].typ):
localError(c.config, a.info, "not all cases are covered")
addSon(father, a)
@@ -1010,7 +1028,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
if n.kind == nkCurlyExpr:
result = semTypeNode(c, n.sons[0], nil)
constraint = semNodeKindConstraints(n, c.config)
constraint = semNodeKindConstraints(n, c.config, 1)
elif n.kind == nkCall and
n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice} and
considerQuotedIdent(c, n[0]).s == "{}":
result = semTypeNode(c, n[1], nil)
constraint = semNodeKindConstraints(n, c.config, 2)
else:
result = semTypeNode(c, n, nil)
@@ -1054,6 +1077,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if hasType:
typ = semParamType(c, a.sons[length-2], constraint)
var owner = getCurrOwner(c).owner
# TODO: Disallow typed/untyped in procs in the compiler/stdlib
if (owner.kind != skModule or owner.owner.name.s != "stdlib") and
kind == skProc and (typ.kind == tyStmt or typ.kind == tyExpr):
localError(c.config, a.sons[length-2].info, "'" & typ.sym.name.s & "' is only allowed in templates and macros")
if hasDefault:
def = a[^1]
@@ -1130,12 +1158,15 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
# turn explicit 'void' return type into 'nil' because the rest of the
# compiler only checks for 'nil':
if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid:
if kind notin {skMacro, skTemplate} and r.kind in {tyStmt, tyExpr}:
localError(c.config, n.sons[0].info, "return type '" & typeToString(r) &
"' is only valid for macros and templates")
# 'auto' as a return type does not imply a generic:
if r.kind == tyAnything:
elif r.kind == tyAnything:
# 'p(): auto' and 'p(): expr' are equivalent, but the rest of the
# compiler is hardly aware of 'auto':
r = newTypeS(tyExpr, c)
elif r.kind != tyExpr:
else:
if r.sym == nil or sfAnon notin r.sym.flags:
let lifted = liftParamType(c, kind, genericParams, r, "result",
n.sons[0].info)
@@ -1287,7 +1318,19 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
if tx != result and tx.kind == tyObject and tx.sons[0] != nil:
semObjectTypeForInheritedGenericInst(c, n, tx)
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward} and prev != nil:
result = newTypeS(tyAlias, c)
result.rawAddSon typeExpr
result.sym = prev.sym
assignType(prev, result)
proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
if prev != nil:
let result = newTypeS(tyAlias, c)
result.rawAddSon typExpr.typ
result.sym = prev.sym
assignType(prev, result)
proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
var n = semExprWithType(c, n, {efDetermineType})
@@ -1391,20 +1434,6 @@ proc semProcTypeWithScope(c: PContext, n: PNode,
when useEffectSystem: setEffectsForProcType(c.graph, result, n.sons[1])
closeScope(c)
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
if typeExpr.kind in {tyObject, tyEnum, tyDistinct} and prev != nil:
result = newTypeS(tyAlias, c)
result.rawAddSon typeExpr
result.sym = prev.sym
assignType(prev, result)
proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
if prev != nil:
let result = newTypeS(tyAlias, c)
result.rawAddSon typExpr.typ
result.sym = prev.sym
assignType(prev, result)
proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
if n.kind == nkType:
result = symFromType(c, n.typ, n.info)
@@ -1551,10 +1580,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
# XXX figure out why this has children already...
result.sons.setLen 0
result.n = nil
if c.config.selectedGc == gcDestructors:
result.flags = {tfHasAsgn}
else:
result.flags = {}
result.flags = {tfHasAsgn}
semContainerArg(c, n, "seq", result)
else:
result = semContainer(c, n, tySequence, "seq", prev)

View File

@@ -2071,6 +2071,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
# constructor in a call:
if result == nil and f.kind == tyVarargs:
if f.n != nil:
# Forward to the varargs converter
result = localConvMatch(c, m, f, a, arg)
else:
r = typeRel(m, base(f), a)
@@ -2083,10 +2084,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
# bug #4799, varargs accepting subtype relation object
elif r == isSubtype:
inc(m.subtypeMatches)
if f.kind == tyTypeDesc:
if base(f).kind == tyTypeDesc:
result = arg
else:
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
result = implicitConv(nkHiddenSubConv, base(f), arg, m, c)
m.baseTypeMatch = true
else:
result = userConvMatch(c, m, base(f), a, arg)
@@ -2231,14 +2232,14 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
else:
m.state = csNoMatch
return
if formal.typ.kind == tyVar:
let arg_converter = if arg.kind == nkHiddenDeref: arg[0] else: arg
if arg_converter.kind == nkHiddenCallConv:
if arg_converter.typ.kind != tyVar:
let argConverter = if arg.kind == nkHiddenDeref: arg[0] else: arg
if argConverter.kind == nkHiddenCallConv:
if argConverter.typ.kind != tyVar:
m.state = csNoMatch
m.mutabilityProblem = uint8(f-1)
return
return
elif not n.isLValue:
m.state = csNoMatch
m.mutabilityProblem = uint8(f-1)
@@ -2267,8 +2268,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
if n.sons[a].kind == nkHiddenStdConv:
doAssert n.sons[a].sons[0].kind == nkEmpty and
n.sons[a].sons[1].kind == nkArgList and
n.sons[a].sons[1].len == 0
n.sons[a].sons[1].kind in {nkBracket, nkArgList}
# Steal the container and pass it along
setSon(m.call, formal.position + 1, n.sons[a].sons[1])
else:

View File

@@ -18,7 +18,7 @@ const
szIllegalRecursion* = -2
szUncomputedSize* = -1
proc computeSizeAlign(conf: ConfigRef; typ: PType): void
proc computeSizeAlign(conf: ConfigRef; typ: PType)
proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
## returns object alignment
@@ -49,13 +49,14 @@ proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
else:
result = 1
proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt): tuple[offset, align: BiggestInt] =
proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode,
initialOffset: BiggestInt): tuple[offset, align: BiggestInt] =
## ``offset`` is the offset within the object, after the node has been written, no padding bytes added
## ``align`` maximum alignment from all sub nodes
assert n != nil
if n.typ != nil and n.typ.size == szIllegalRecursion:
result.offset = szIllegalRecursion
result.align = szIllegalRecursion
result.align = szIllegalRecursion
return
result.align = 1
@@ -71,66 +72,52 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset:
of nkOfBranch, nkElse:
# offset parameter cannot be known yet, it needs to know the alignment first
let align = computeSubObjectAlign(conf, n.sons[i].lastSon)
if align == szIllegalRecursion:
result.offset = szIllegalRecursion
result.offset = szIllegalRecursion
result.align = szIllegalRecursion
return
if align == szUnknownSize or maxChildAlign == szUnknownSize:
maxChildAlign = szUnknownSize
else:
maxChildAlign = max(maxChildAlign, align)
else:
internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
if maxChildAlign == szUnknownSize:
result.align = szUnknownSize
result.offset = szUnknownSize
else:
# the union neds to be aligned first, before the offsets can be assigned
let kindUnionOffset = align(kindOffset, maxChildAlign)
var maxChildOffset: BiggestInt = 0
for i in 1 ..< sonsLen(n):
let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset)
maxChildOffset = max(maxChildOffset, offset)
result.align = max(kindAlign, maxChildAlign)
result.offset = maxChildOffset
of nkRecList:
result.align = 1 # maximum of all member alignments
var offset = initialOffset
for i, child in n.sons:
let (new_offset, align) = computeObjectOffsetsFoldFunction(conf, child, offset)
if new_offset == szIllegalRecursion:
result.offset = szIllegalRecursion
result.align = szIllegalRecursion
return
elif new_offset == szUnknownSize or offset == szUnknownSize:
# if anything is unknown, the rest becomes unknown as well
offset = szUnknownSize
result.align = szUnknownSize
else:
offset = new_offset
result.align = max(result.align, align)
# final alignment
if offset == szUnknownSize:
result.offset = szUnknownSize
else:
result.offset = align(offset, result.align)
of nkSym:
var size = szUnknownSize
var align = szUnknownSize
if n.sym.bitsize == 0: # 0 represents bitsize not set
computeSizeAlign(conf, n.sym.typ)
size = n.sym.typ.size.int
@@ -155,7 +142,6 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf
let kindOffset = computePackedObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset, debug)
# the union neds to be aligned first, before the offsets can be assigned
let kindUnionOffset = kindOffset
var maxChildOffset: BiggestInt = kindUnionOffset
for i in 1 ..< sonsLen(n):
let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug)
@@ -168,9 +154,17 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf
if result == szIllegalRecursion:
break
of nkSym:
computeSizeAlign(conf, n.sym.typ)
n.sym.offset = initialOffset.int
result = n.sym.offset + n.sym.typ.size
var size = szUnknownSize
if n.sym.bitsize == 0:
computeSizeAlign(conf, n.sym.typ)
size = n.sym.typ.size.int
if initialOffset == szUnknownSize or size == szUnknownSize:
n.sym.offset = szUnknownSize
result = szUnknownSize
else:
n.sym.offset = int(initialOffset)
result = initialOffset + n.sym.typ.size
else:
result = szUnknownSize
@@ -324,7 +318,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
var headerAlign: int16
if typ.sons[0] != nil:
# compute header size
if conf.cmd == cmdCompileToCpp:
# if the target is C++ the members of this type are written
# into the padding byets at the end of the parent type. At the
@@ -364,7 +357,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
typ.size = szUnknownSize
typ.align = szUnknownSize
return
# header size is already in size from computeObjectOffsetsFoldFunction
# maxAlign is probably not changed at all from headerAlign
if tfPacked in typ.flags:
@@ -373,7 +365,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
else:
typ.align = int16(max(align, headerAlign))
typ.size = align(offset, typ.align)
of tyInferred:
if typ.len > 1:
computeSizeAlign(conf, typ.lastSon)

View File

@@ -456,20 +456,27 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym;
proc extractPragma(s: PSym): PNode =
if s.kind in routineKinds:
result = s.ast[pragmasPos]
elif s.kind in {skType}:
elif s.kind in {skType, skVar, skLet}:
# s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
result = s.ast[0][1]
doAssert result == nil or result.kind == nkPragma
proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) =
let pragmaNode = extractPragma(s)
var pragmaNode: PNode
if optOldAst in conf.options and s.kind in {skVar, skLet}:
pragmaNode = nil
else:
pragmaNode = if s.kind == skEnumField: extractPragma(s.owner) else: extractPragma(s)
let name =
if s.kind == skEnumField and sfDeprecated notin s.flags: "enum '" & s.owner.name.s & "' which contains field '" & s.name.s & "'"
else: s.name.s
if pragmaNode != nil:
for it in pragmaNode:
if whichPragma(it) == wDeprecated and it.safeLen == 2 and
it[1].kind in {nkStrLit..nkTripleStrLit}:
message(conf, info, warnDeprecated, it[1].strVal & "; " & s.name.s)
message(conf, info, warnDeprecated, it[1].strVal & "; " & name & " is deprecated")
return
message(conf, info, warnDeprecated, s.name.s)
message(conf, info, warnDeprecated, name & " is deprecated")
proc userError(conf: ConfigRef; info: TLineInfo; s: PSym) =
let pragmaNode = extractPragma(s)
@@ -486,6 +493,8 @@ proc markUsed(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
incl(s.flags, sfUsed)
if s.kind == skEnumField and s.owner != nil:
incl(s.owner.flags, sfUsed)
if sfDeprecated in s.owner.flags:
warnAboutDeprecated(conf, info, s)
if {sfDeprecated, sfError} * s.flags != {}:
if sfDeprecated in s.flags: warnAboutDeprecated(conf, info, s)
if sfError in s.flags: userError(conf, info, s)

View File

@@ -107,20 +107,23 @@ proc isFloatLit*(t: PType): bool {.inline.} =
result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit
proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName): string =
result = sym.owner.name.s & '.' & sym.name.s & '('
var n = sym.typ.n
for i in countup(1, sonsLen(n) - 1):
let p = n.sons[i]
if p.kind == nkSym:
add(result, p.sym.name.s)
add(result, ": ")
add(result, typeToString(p.sym.typ, prefer))
if i != sonsLen(n)-1: add(result, ", ")
else:
result.add renderTree(p)
add(result, ')')
if n.sons[0].typ != nil:
result.add(": " & typeToString(n.sons[0].typ, prefer))
assert sym != nil
result = sym.owner.name.s & '.' & sym.name.s
if sym.kind in routineKinds:
result.add '('
var n = sym.typ.n
for i in countup(1, sonsLen(n) - 1):
let p = n.sons[i]
if p.kind == nkSym:
add(result, p.sym.name.s)
add(result, ": ")
add(result, typeToString(p.sym.typ, prefer))
if i != sonsLen(n)-1: add(result, ", ")
else:
result.add renderTree(p)
add(result, ')')
if n.sons[0].typ != nil:
result.add(": " & typeToString(n.sons[0].typ, prefer))
result.add "[declared in "
result.add(conf$sym.info)
result.add "]"
@@ -429,7 +432,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
sfAnon notin t.sym.flags:
if t.kind == tyInt and isIntLit(t):
result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
elif t.kind == tyAlias:
elif t.kind == tyAlias and t.sons[0].kind != tyAlias:
result = typeToString(t.sons[0])
elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
result = t.sym.name.s
@@ -756,7 +759,7 @@ type
TTypeCmpFlags* = set[TTypeCmpFlag]
TSameTypeClosure = object {.pure.}
TSameTypeClosure = object
cmp: TDistinctCompare
recCheck: int
flags: TTypeCmpFlags

View File

@@ -0,0 +1,38 @@
# note: consider merging tests/assert/testhelper.nim here.
proc mismatch*[T](lhs: T, rhs: T): string =
## Simplified version of `unittest.require` that satisfies a common use case,
## while avoiding pulling too many dependencies. On failure, diagnostic
## information is provided that in particular makes it easy to spot
## whitespace mismatches and where the mismatch is.
proc replaceInvisible(s: string): string =
for a in s:
case a
of '\n': result.add "\\n\n"
else: result.add a
proc quoted(s: string): string = result.addQuoted s
result.add "\n"
result.add "lhs:{" & replaceInvisible(
$lhs) & "}\nrhs:{" & replaceInvisible($rhs) & "}\n"
when compiles(lhs.len):
if lhs.len != rhs.len:
result.add "lhs.len: " & $lhs.len & " rhs.len: " & $rhs.len & "\n"
when compiles(lhs[0]):
var i = 0
while i < lhs.len and i < rhs.len:
if lhs[i] != rhs[i]: break
i.inc
result.add "first mismatch index: " & $i & "\n"
if i < lhs.len and i < rhs.len:
result.add "lhs[i]: {" & quoted($lhs[i]) & "}\nrhs[i]: {" & quoted(
$rhs[i]) & "}\n"
result.add "lhs[0..<i]:{" & replaceInvisible($lhs[
0..<i]) & "}"
proc assertEquals*[T](lhs: T, rhs: T) =
when false: # can be useful for debugging to see all that's fed to this.
echo "----" & $lhs
if lhs!=rhs:
doAssert false, mismatch(lhs, rhs)

View File

@@ -10,10 +10,6 @@
## This file implements the new evaluation engine for Nim code.
## An instruction is 1-3 int32s in memory, it is a register based VM.
const
debugEchoCode = false
traceCode = debugEchoCode
import ast except getstr
import
@@ -26,6 +22,9 @@ from evaltempl import evalTemplate
from modulegraphs import ModuleGraph, PPassContext
const
traceCode = debugEchoCode
when hasFFI:
import evalffi
@@ -65,22 +64,27 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) =
return
stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1)
var info = c.debug[pc]
# we now use the same format as in system/except.nim
var s = substr(toFilename(c.config, info), 0)
# this 'substr' prevents a strange corruption. XXX This needs to be
# investigated eventually but first attempts to fix it broke everything
# see the araq-wip-fixed-writebarrier branch.
# we now use a format similar to the one in lib/system/excpt.nim
var s = ""
# todo: factor with quotedFilename
if optExcessiveStackTrace in c.config.globalOptions:
s = toFullPath(c.config, info)
else:
s = toFilename(c.config, info)
var line = toLinenumber(info)
var col = toColumn(info)
if line > 0:
add(s, '(')
add(s, $line)
add(s, ", ")
add(s, $(col + ColOffset))
add(s, ')')
if x.prc != nil:
for k in 1..max(1, 25-s.len): add(s, ' ')
add(s, x.prc.name.s)
msgWriteln(c.config, s)
proc stackTrace(c: PCtx, tos: PStackFrame, pc: int,
proc stackTraceImpl(c: PCtx, tos: PStackFrame, pc: int,
msg: string, lineInfo: TLineInfo) =
msgWriteln(c.config, "stack trace: (most recent call last)")
stackTraceAux(c, tos, pc)
@@ -88,8 +92,14 @@ proc stackTrace(c: PCtx, tos: PStackFrame, pc: int,
if c.mode == emRepl: globalError(c.config, lineInfo, msg)
else: localError(c.config, lineInfo, msg)
proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) =
stackTrace(c, tos, pc, msg, c.debug[pc])
template stackTrace(c: PCtx, tos: PStackFrame, pc: int,
msg: string, lineInfo: TLineInfo) =
stackTraceImpl(c, tos, pc, msg, lineInfo)
return
template stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) =
stackTraceImpl(c, tos, pc, msg, c.debug[pc])
return
proc bailOut(c: PCtx; tos: PStackFrame) =
stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " &
@@ -950,13 +960,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
decodeBC(rkInt)
let a = regs[rb].node
let b = regs[rc].node
if a.kind == nkSym and a.sym.kind in skProcKinds and
if a.kind == nkSym and a.sym.kind in skProcKinds and
b.kind == nkSym and b.sym.kind in skProcKinds:
regs[ra].intVal =
if sfFromGeneric in a.sym.flags and a.sym.owner == b.sym: 1
else: 0
else:
stackTrace(c, tos, pc, "node is not a proc symbol")
else:
stackTrace(c, tos, pc, "node is not a proc symbol")
of opcEcho:
let rb = instr.regB
if rb == 1:
@@ -1239,8 +1249,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let newLen = regs[rb].intVal.int
if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc])
of opcReset:
internalError(c.config, c.debug[pc], "too implement")
of opcNarrowS:
decodeB(rkInt)
let min = -(1.BiggestInt shl (rb-1))

View File

@@ -73,7 +73,7 @@ type
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
opcIsNil, opcOf, opcIs,
opcSubStr, opcParseFloat, opcConv, opcCast,
opcQuit, opcReset,
opcQuit,
opcNarrowS, opcNarrowU,
opcAddStrCh,

View File

@@ -33,6 +33,11 @@ import
import platform
from os import splitFile
const
debugEchoCode* = defined(nimVMDebug)
when debugEchoCode:
import asciitables
when hasFFI:
import evalffi
@@ -43,9 +48,10 @@ type
TGenFlags = set[TGenFlag]
proc debugInfo(c: PCtx; info: TLineInfo): string =
result = toFilename(c.config, info).splitFile.name & ":" & $info.line
result = toFileLineCol(c.config, info)
proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
## for debugging purposes
# first iteration: compute all necessary labels:
var jumpTargets = initIntSet()
let last = if last < 0: c.code.len-1 else: min(last, c.code.len-1)
@@ -54,7 +60,9 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
if x.opcode in relativeJumps:
jumpTargets.incl(i+x.regBx-wordExcess)
# for debugging purposes
template toStr(opc: TOpcode): string = ($opc).substr(3)
result.add "code listing:\n"
var i = start
while i <= last:
if i in jumpTargets: result.addf("L$1:\n", i)
@@ -62,34 +70,39 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
result.add($i)
let opc = opcode(x)
if opc in {opcConv, opcCast}:
if opc in {opcIndCall, opcIndCallAsgn}:
result.addf("\t$#\tr$#, r$#, nargs:$#", opc.toStr, x.regA,
x.regB, x.regC)
elif opc in {opcConv, opcCast}:
let y = c.code[i+1]
let z = c.code[i+2]
result.addf("\t$#\tr$#, r$#, $#, $#", ($opc).substr(3), x.regA, x.regB,
result.addf("\t$#\tr$#, r$#, $#, $#", opc.toStr, x.regA, x.regB,
c.types[y.regBx-wordExcess].typeToString,
c.types[z.regBx-wordExcess].typeToString)
inc i, 2
elif opc < firstABxInstr:
result.addf("\t$#\tr$#, r$#, r$#", ($opc).substr(3), x.regA,
result.addf("\t$#\tr$#, r$#, r$#", opc.toStr, x.regA,
x.regB, x.regC)
elif opc in relativeJumps:
result.addf("\t$#\tr$#, L$#", ($opc).substr(3), x.regA,
result.addf("\t$#\tr$#, L$#", opc.toStr, x.regA,
i+x.regBx-wordExcess)
elif opc in {opcLdConst, opcAsgnConst}:
let idx = x.regBx-wordExcess
result.addf("\t$#\tr$#, $# ($#)", ($opc).substr(3), x.regA,
result.addf("\t$#\tr$#, $# ($#)", opc.toStr, x.regA,
c.constants[idx].renderTree, $idx)
elif opc in {opcMarshalLoad, opcMarshalStore}:
let y = c.code[i+1]
result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB,
result.addf("\t$#\tr$#, r$#, $#", opc.toStr, x.regA, x.regB,
c.types[y.regBx-wordExcess].typeToString)
inc i
else:
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
result.addf("\t$#\tr$#, $#", opc.toStr, x.regA, x.regBx-wordExcess)
result.add("\t#")
result.add(debugInfo(c, c.debug[i]))
result.add("\n")
inc i
when debugEchoCode:
result = result.alignTable
proc echoCode*(c: PCtx; start=0; last = -1) {.deprecated.} =
var buf = ""
@@ -1092,7 +1105,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
of mReset:
unused(c, n, dest)
var d = c.genx(n.sons[1])
c.gABC(n, opcReset, d)
c.gABx(n, opcLdNull, d, c.genType(n.sons[1].typ))
c.gABx(n, opcNodeToReg, d, d)
c.genAsgnPatch(n.sons[1], d)
of mOf, mIs:
if dest < 0: dest = c.getTemp(n.typ)
var tmp = c.genx(n.sons[1])
@@ -1736,8 +1751,9 @@ proc genVarSection(c: PCtx; n: PNode) =
#assert(a.sons[0].kind == nkSym) can happen for transformed vars
if a.kind == nkVarTuple:
for i in 0 .. a.len-3:
if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
checkCanEval(c, a[i])
if a[i].kind == nkSym:
if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
checkCanEval(c, a[i])
c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner))
elif a.sons[0].kind == nkSym:
let s = a.sons[0].sym

View File

@@ -13,7 +13,7 @@ from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
floor, ceil, `mod`
from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir
from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, getAppFilename
template mathop(op) {.dirty.} =
registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
@@ -120,3 +120,6 @@ proc registerAdditionalOps*(c: PCtx) =
setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
systemop gorgeEx
macrosop getProjectPath
registerCallback c, "stdlib.os.getCurrentCompilerExe", proc (a: VmArgs) {.nimcall.} =
setResult(a, getAppFilename())

1
config/config.nims Normal file
View File

@@ -0,0 +1 @@
# empty config.nims to prevent future regressions, see #9990

View File

@@ -102,9 +102,6 @@ path="$lib/pure"
tlsEmulation:on
@end
@if haiku:
# Haiku currently have problems with TLS
# https://dev.haiku-os.org/ticket/14342
tlsEmulation:on
gcc.options.linker = "-Wl,--as-needed -lnetwork"
gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork"
clang.options.linker = "-Wl,--as-needed -lnetwork"
@@ -220,10 +217,25 @@ llvm_gcc.options.size = "-Os"
# Configuration for the LLVM CLang compiler:
clang.options.debug = "-g"
clang.cpp.options.debug = "-g"
clang.options.always = "-w"
clang.options.speed = "-O3"
clang.options.size = "-Os"
@if windows:
clang_cl.cpp.options.always %= "${clang_cl.options.always} /EHsc"
@if not release:
clang_cl.options.linker = "/Z7"
clang_cl.cpp.options.linker = "/Z7"
@end
clang.options.debug = "-g -gcodeview"
clang.cpp.options.debug = "-g -gcodeview"
@if not release:
clang.options.linker = "-g"
clang.cpp.options.linker = "-g"
@end
@end
# Configuration for the Visual C/C++ compiler:
# VCCEXE is a tool that invokes the Visual Studio Developer Command Prompt
# before calling the compiler.

View File

@@ -18,9 +18,9 @@ Advanced commands:
Advanced options:
-o:FILE, --out:FILE set the output filename
--stdout output to stdout
--stdout:on|off output to stdout
--colors:on|off turn compiler messages coloring on|off
--listFullPaths list full paths in messages
--listFullPaths:on|off list full paths in messages
-w:on|off|list, --warnings:on|off|list
turn all warnings on|off or list all available
--warning[X]:on|off turn specific warning X on|off
@@ -39,29 +39,27 @@ Advanced options:
--nimcache:PATH set the path used for generated files
--header:FILE the compiler should produce a .h file (FILE
is optional)
-c, --compileOnly compile Nim files only; do not assemble or link
--noLinking compile Nim and generated files but do not link
--noMain do not generate a main procedure
--genScript generate a compile script (in the 'nimcache'
-c, --compileOnly:on|off compile Nim files only; do not assemble or link
--noLinking:on|off compile Nim and generated files but do not link
--noMain:on|off do not generate a main procedure
--genScript:on|off generate a compile script (in the 'nimcache'
subdirectory named 'compile_$$project$$scriptext'),
implies --compileOnly
--genDeps generate a '.deps' file containing the dependencies
--genDeps:on|off generate a '.deps' file containing the dependencies
--os:SYMBOL set the target operating system (cross-compilation)
--cpu:SYMBOL set the target processor (cross-compilation)
--debuginfo enables debug information
--debuginfo:on|off enables debug information
-t, --passC:OPTION pass an option to the C compiler
-l, --passL:OPTION pass an option to the linker
--cincludes:DIR modify the C compiler header search path
--clibdir:DIR modify the linker library search path
--clib:LIBNAME link an additional C library
(you should omit platform-specific extensions)
--genMapping generate a mapping file containing
(Nim, mangled) identifier pairs
--project document the whole project (doc2)
--docSeeSrcUrl:url activate 'see source' for doc and doc2 commands
(see doc.item.seesrc in config/nimdoc.cfg)
--lineDir:on|off generation of #line directive on|off
--embedsrc embeds the original source code as comments
--embedsrc:on|off embeds the original source code as comments
in the generated output
--threadanalysis:on|off turn thread analysis on|off
--tlsEmulation:on|off turn thread local storage emulation on|off
@@ -78,11 +76,12 @@ Advanced options:
strings is allowed; only for backwards compatibility
--nilseqs:on|off allow 'nil' for strings/seqs for
backwards compatibility
--skipCfg do not read the general configuration file
--skipUserCfg do not read the user's configuration file
--skipParentCfg do not read the parent dirs' configuration files
--skipProjCfg do not read the project's configuration file
--gc:refc|v2|markAndSweep|boehm|go|none|regions
--oldast:on|off use old AST for backwards compatibility
--skipCfg:on|off do not read the nim installation's configuration file
--skipUserCfg:on|off do not read the user's configuration file
--skipParentCfg:on|off do not read the parent dirs' configuration files
--skipProjCfg:on|off do not read the project's configuration file
--gc:refc|markAndSweep|boehm|go|none|regions
select the GC to use; default is 'refc'
--index:on|off turn index file generation on|off
--putenv:key=value set an environment variable
@@ -98,8 +97,8 @@ Advanced options:
symbol matching is fuzzy so
that --dynlibOverride:lua matches
dynlib: "liblua.so.3"
--dynlibOverrideAll makes the dynlib pragma have no effect
--listCmd list the commands used to execute external programs
--dynlibOverrideAll:on|off makes the dynlib pragma have no effect
--listCmd:on|off list the commands used to execute external programs
--parallelBuild:0|1|... perform a parallel build
value = number of processors (0 for auto-detect)
--incremental:on|off only recompile the changed modules (experimental!)
@@ -108,3 +107,6 @@ Advanced options:
--experimental:$1
enable experimental language feature
-v, --version show detailed version information
--profiler:on|off Enable profiling; requires `import nimprof`, and
works better with `--stackTrace:on`
see also https://nim-lang.github.io/Nim/estp.html

View File

@@ -15,7 +15,7 @@ Options:
(Optionally: Define the value for that symbol,
see: "compile time define pragmas")
-u, --undef:SYMBOL undefine a conditional symbol
-f, --forceBuild force rebuilding of all modules
-f, --forceBuild:on|off force rebuilding of all modules
--stackTrace:on|off turn stack tracing on|off
--lineTrace:on|off turn line tracing on|off
--threads:on|off turn support for multi-threading on|off
@@ -35,7 +35,7 @@ Options:
--debugger:native|endb use native debugger (gdb) | ENDB (experimental)
--app:console|gui|lib|staticlib
generate a console app|GUI app|DLL|static library
-r, --run run the compiled program with given arguments
-r, --run:on|off run the compiled program with given arguments
--fullhelp show all command line switches
-h, --help show this help

View File

@@ -6,6 +6,9 @@ The documentation consists of several documents:
- | `Tutorial (part II) <tut2.html>`_
| The Nim tutorial part two deals with the advanced language constructs.
- | `Tutorial (part III) <tut3.html>`_
| The Nim tutorial part three about Nim's macro system.
- | `Language Manual <manual.html>`_
| The Nim manual is a draft that will evolve into a proper specification.

View File

@@ -107,8 +107,8 @@ String handling
substrings, replacing substrings.
* `strformat <strformat.html>`_
Macro based standard string interpolation / formatting. Inpired by
Python's ```f``-strings.
Macro based standard string interpolation / formatting. Inspired by
Python's ``f``-strings.
* `strmisc <strmisc.html>`_
This module contains uncommon string handling operations that do not
@@ -141,12 +141,6 @@ String handling
Ropes can represent very long strings efficiently; especially concatenation
is done in O(1) instead of O(n).
* `matchers <matchers.html>`_
This module contains various string matchers for email addresses, etc.
* `subexes <subexes.html>`_
This module implements advanced string substitution operations.
* `std/editdistance <editdistance.html>`_
This module contains an algorithm to compute the edit distance between two
Unicode strings.
@@ -236,9 +230,6 @@ Internet Protocols and Support
* `cgi <cgi.html>`_
This module implements helpers for CGI applications.
* `scgi <scgi.html>`_
This module implements helpers for SCGI applications.
* `browsers <browsers.html>`_
This module implements procs for opening URLs with the user's default
browser.
@@ -275,8 +266,8 @@ Internet Protocols and Support
module.
* `net <net.html>`_
This module implements a high-level sockets API. It will replace the
``sockets`` module in the future.
This module implements a high-level sockets API. It replaces the
``sockets`` module.
* `nativesockets <nativesockets.html>`_
This module implements a low-level sockets API.
@@ -456,13 +447,6 @@ Database support
for other databases too.
Other
-----
* `ssl <ssl.html>`_
This module provides an easy to use sockets-style
Nim interface to the OpenSSL library.
Wrappers
========

View File

@@ -480,7 +480,6 @@ The type suffixes are:
``'d`` float64
``'f32`` float32
``'f64`` float64
``'f128`` float128
================= =========================
Floating point literals may also be in binary, octal or hexadecimal
@@ -1587,7 +1586,7 @@ details like this when mixing garbage collected data with unmanaged memory.
Not nil annotation
------------------
All types for that ``nil`` is a valid value can be annotated to
All types for which ``nil`` is a valid value can be annotated to
exclude ``nil`` as a valid value with the ``not nil`` annotation:
.. code-block:: nim
@@ -1664,12 +1663,12 @@ Nim supports these `calling conventions`:idx:\:
and another one for the pointer to implicitly passed environment.
`stdcall`:idx:
This the stdcall convention as specified by Microsoft. The generated C
This is the stdcall convention as specified by Microsoft. The generated C
procedure is declared with the ``__stdcall`` keyword.
`cdecl`:idx:
The cdecl convention means that a procedure shall use the same convention
as the C compiler. Under windows the generated C procedure is declared with
as the C compiler. Under Windows the generated C procedure is declared with
the ``__cdecl`` keyword.
`safecall`:idx:
@@ -7365,6 +7364,17 @@ Example:
embedsC()
``nimbase.h`` defines ``NIM_EXTERNC`` C macro that can be used for
``extern "C"`` code to work with both ``nim c`` and ``nim cpp``, eg:
.. code-block:: Nim
proc foobar() {.importc:"$1".}
{.emit: """
#include <stdio.h>
NIM_EXTERNC
void fun(){}
""".}
For backwards compatibility, if the argument to the ``emit`` statement
is a single string literal, Nim symbols can be referred to via backticks.
This usage is however deprecated.

View File

@@ -347,13 +347,16 @@ complete list.
Define Effect
====================== =========================================================
``release`` Turns off runtime checks and turns on the optimizer.
More aggressive optimizations are possible, eg:
``--passC:-ffast-math`` (but see issue #10305)
``--stacktrace:off``
``useWinAnsi`` Modules like ``os`` and ``osproc`` use the Ansi versions
of the Windows API. The default build uses the Unicode
version.
``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``.
``useNimRtl`` Compile and link against ``nimrtl.dll``.
``useMalloc`` Makes Nim use C's `malloc`:idx: instead of Nim's
own memory manager, ableit prefixing each allocation with
own memory manager, albeit prefixing each allocation with
its size to support clearing memory on reallocation.
This only works with ``gc:none``.
``useRealtimeGC`` Enables support of Nim's GC for *soft* realtime

View File

@@ -26,9 +26,28 @@ previous settings):
``$project.nim``. This file can be skipped with the same
``--skipProjCfg`` command line option.
The VM cannot deal with ``importc`` because the FFI is not
available. So the stdlib modules using ``importc`` cannot be used with
Nim's VM. However, at least the following modules are available:
Limitations
=================================
NimScript is subject to some limitations caused by the implementation of the VM
(virtual machine):
* Nim's FFI (foreign function interface) is not available in NimScript. This
means that any stdlib module which relies on ``importc`` can not be used in
the VM.
* ``ptr`` operations are are hard to emulate with the symbolic representation
the VM uses. They are available and tested extensively but there are bugs left.
* ``var T`` function arguments rely on ``ptr`` operations internally and might
also be problematic in some cases.
* More than one level of `ref` is generally not supported (for example, the type
`ref ref int`).
* multimethods are not available.
Given the above restrictions, at least the following modules are available:
* `macros <macros.html>`_
* `os <os.html>`_
@@ -98,17 +117,6 @@ Task Description
========= ===================================================
If the task runs an external command via ``exec`` it should afterwards call
``setCommand "nop"`` to tell the Nim compiler that nothing else needs to be
done:
.. code-block:: nim
task tests, "test regular expressions":
exec "nim c -r tests"
setCommand "nop"
Look at the module `distros <distros.html>`_ for some support of the
OS's native package managers.

104
koch.nim
View File

@@ -46,6 +46,8 @@ Possible Commands:
boot [options] bootstraps with given command line options
distrohelper [bindir] helper for distro packagers
tools builds Nim related tools
toolsNoNimble builds Nim related tools (except nimble)
doesn't require network connectivity
nimble builds the Nimble tool
Boot options:
-d:release produce a release version of the compiler
@@ -56,6 +58,7 @@ Boot options:
for bootstrapping
Commands for core developers:
runCI runs continuous integration (CI), eg from travis
docs [options] generates the full documentation
csource -d:release builds the C sources for installation
pdf builds the PDF documentation
@@ -71,6 +74,12 @@ Web options:
build the official docs, use UA-48159761-1
"""
let kochExe* = when isMainModule: os.getAppFilename() # always correct when koch is main program, even if `koch` exe renamed eg: `nim c -o:koch_debug koch.nim`
else: getAppDir() / "koch".exe # works for winrelease
proc kochExec*(cmd: string) =
exec kochExe.quoteShell & " " & cmd
template withDir(dir, body) =
let old = getCurrentDir()
try:
@@ -121,9 +130,6 @@ proc bundleNimbleExe(latest: bool) =
# installer.ini expects it under $nim/bin
nimCompile("dist/nimble/src/nimble.nim", options = "-d:release --nilseqs:on")
proc buildNimfind() =
nimCompile("tools/nimfind.nim", options = "-d:release")
proc buildNimble(latest: bool) =
# old installations created nim/nimblepkg/*.nim files. We remove these
# here so that it cannot cause problems (nimble bug #306):
@@ -197,13 +203,12 @@ proc buildTool(toolname, args: string) =
nimexec("cc $# $#" % [args, toolname])
copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe)
proc buildTools(latest: bool) =
proc buildTools() =
bundleNimsuggest()
nimCompile("tools/nimgrep.nim", options = "-d:release")
when defined(windows): buildVccTool()
nimCompile("nimpretty/nimpretty.nim", options = "-d:release")
buildNimble(latest)
buildNimfind()
nimCompile("tools/nimfind.nim", options = "-d:release")
proc nsis(latest: bool; args: string) =
bundleNimbleExe(latest)
@@ -272,16 +277,30 @@ proc boot(args: string) =
var output = "compiler" / "nim".exe
var finalDest = "bin" / "nim".exe
# default to use the 'c' command:
let defaultCommand = if getEnv("NIM_COMPILE_TO_CPP", "false") == "true": "cpp" else: "c"
let bootOptions = if args.len == 0 or args.startsWith("-"): defaultCommand else: ""
let useCpp = getEnv("NIM_COMPILE_TO_CPP", "false") == "true"
let smartNimcache = (if "release" in args: "nimcache/r_" else: "nimcache/d_") &
hostOs & "_" & hostCpu
copyExe(findStartNim(), 0.thVersion)
let nimStart = findStartNim()
copyExe(nimStart, 0.thVersion)
for i in 0..2:
let defaultCommand = if useCpp: "cpp" else: "c"
let bootOptions = if args.len == 0 or args.startsWith("-"): defaultCommand else: ""
echo "iteration: ", i+1
exec i.thVersion & " $# $# --nimcache:$# compiler" / "nim.nim" % [bootOptions, args,
smartNimcache]
var extraOption = ""
if i == 0:
extraOption.add " --skipUserCfg --skipParentCfg"
# Note(D20190115T162028:here): the configs are skipped for bootstrap
# (1st iteration) to prevent newer flags from breaking bootstrap phase.
# fixes #10030.
let ret = execCmdEx(nimStart & " --version")
doAssert ret.exitCode == 0
let version = ret.output.splitLines[0]
if version.startsWith "Nim Compiler Version 0.19.0":
extraOption.add " -d:nimBoostrapCsources0_19_0"
# remove this when csources get updated
exec i.thVersion & " $# $# $# --nimcache:$# compiler" / "nim.nim" %
[bootOptions, extraOption, args, smartNimcache]
if sameFileContent(output, i.thVersion):
copyExe(output, finalDest)
echo "executables are equal: SUCCESS!"
@@ -352,8 +371,8 @@ proc winReleaseArch(arch: string) =
# Rebuilding koch is necessary because it uses its pointer size to
# determine which mingw link to put in the NSIS installer.
nimexec "c --cpu:$# koch" % cpu
exec "koch boot -d:release --cpu:$#" % cpu
exec "koch --latest zip -d:release"
kochExec "boot -d:release --cpu:$#" % cpu
kochExec "--latest zip -d:release"
overwriteFile r"build\nim-$#.zip" % VersionAsString,
r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch]
@@ -428,6 +447,47 @@ proc xtemp(cmd: string) =
finally:
copyExe(d / "bin" / "nim_backup".exe, d / "bin" / "nim".exe)
proc runCI(cmd: string) =
doAssert cmd.len == 0, cmd # avoid silently ignoring
echo "runCI:", cmd
# note(@araq): Do not replace these commands with direct calls (eg boot())
# as that would weaken our testing efforts.
when defined(posix): # appveyor (on windows) didn't run this
kochExec "boot"
kochExec "boot -d:release"
## build nimble early on to enable remainder to depend on it if needed
kochExec "nimble"
when false:
for pkg in "zip opengl sdl1 jester@#head niminst".split:
exec "nimble install -y" & pkg
buildTools() # altenatively, kochExec "tools --toolsNoNimble"
## run tests
exec "nim e tests/test_nimscript.nims"
when defined(windows):
# note: will be over-written below
exec "nim c -d:nimCoroutines --os:genode -d:posix --compileOnly testament/tester"
# main bottleneck here
exec "nim c -r -d:nimCoroutines testament/tester --pedantic all -d:nimCoroutines"
exec "nim c -r nimdoc/tester"
exec "nim c -r nimpretty/tester.nim"
when defined(posix):
exec "nim c -r nimsuggest/tester"
## remaining actions
when defined(posix):
kochExec "docs --git.commit:devel"
kochExec "csource"
elif defined(windows):
when false:
kochExec "csource"
kochExec "zip"
proc pushCsources() =
if not dirExists("../csources/.git"):
quit "[Error] no csources git repository found"
@@ -475,8 +535,7 @@ proc testUnixInstall(cmdLineRest: string) =
execCleanPath("./koch --latest tools")
# check the tests work:
putEnv("NIM_EXE_NOT_IN_PATH", "NOT_IN_PATH")
execCleanPath("./koch tests", destDir / "bin")
#execCleanPath("./koch tests cat newconfig", destDir / "bin")
execCleanPath("./koch tests cat megatest", destDir / "bin")
else:
echo "Version check: failure"
finally:
@@ -512,6 +571,10 @@ when isMainModule:
var op = initOptParser()
var latest = false
var stable = false
template isLatest(): bool =
if stable: false
else:
existsDir(".git") or latest
while true:
op.next()
case op.kind
@@ -537,17 +600,18 @@ when isMainModule:
of "distrohelper": geninstall()
of "install": install(op.cmdLineRest)
of "testinstall": testUnixInstall(op.cmdLineRest)
of "runci": runCI(op.cmdLineRest)
of "test", "tests": tests(op.cmdLineRest)
of "temp": temp(op.cmdLineRest)
of "xtemp": xtemp(op.cmdLineRest)
of "wintools": bundleWinTools()
of "nimble":
if stable: buildNimble(false)
else: buildNimble(existsDir(".git") or latest)
of "nimble": buildNimble(isLatest())
of "nimsuggest": bundleNimsuggest()
of "toolsnonimble":
buildTools()
of "tools":
if stable: buildTools(false)
else: buildTools(existsDir(".git") or latest)
buildTools()
buildNimble(isLatest())
of "pushcsource", "pushcsources": pushCsources()
of "valgrind": valgrind(op.cmdLineRest)
else: showHelp()

View File

@@ -151,7 +151,7 @@ proc `==`*(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect.}
proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated.}
## compares two Nim symbols
## **Deprecated since version 0.18.1**; Use ```==`(NimNode,NimNode)`` instead.
## **Deprecated since version 0.18.1**; Use ``==(NimNode, NimNode)`` instead.
proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} =
@@ -277,9 +277,9 @@ when defined(nimHasSymOwnerInMacro):
when defined(nimHasInstantiationOfInMacro):
proc isInstantiationOf*(instanceProcSym, genProcSym: NimNode): bool {.magic: "SymIsInstantiationOf", noSideEffect.}
## check if proc symbol is instance of the generic proc symbol
## useful to check proc symbols against generic symbols
## useful to check proc symbols against generic symbols
## returned by `bindSym`
proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
## with 'getType' you can access the node's `type`:idx:. A Nim type is
## mapped to a Nim AST too, so it's slightly confusing but it means the same
@@ -377,7 +377,9 @@ proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.}
proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.}
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.}
## writes an error message at compile time
## writes an error message at compile time. The optional ``n: NimNode``
## parameter is used as the source for file and line number information in
## the compilation error message.
proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.}
## writes a warning message at compile time
@@ -1402,8 +1404,14 @@ proc customPragmaNode(n: NimNode): NimNode =
let impl = n.getImpl()
if impl.kind in RoutineNodes:
return impl.pragma
elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr:
return impl[0][1]
else:
return typ.getImpl()[0][1]
let timpl = typ.getImpl()
if timpl.len>0 and timpl[0].len>1:
return timpl[0][1]
else:
return timpl
if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}:
let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1])
@@ -1492,9 +1500,18 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
let pragmaNode = customPragmaNode(n)
for p in pragmaNode:
if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp:
return p[1]
error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error,
if p.len == 2:
result = p[1]
else:
let def = p[0].getImpl[3]
result = newTree(nnkPar)
for i in 1..<p.len:
let key = def[i][0]
let val = p[i]
result.add newTree(nnkExprColonExpr, key, val)
break
if result.kind == nnkEmpty:
error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error,
when not defined(booting):

View File

@@ -70,12 +70,6 @@ proc `=sink`[T](x: var seq[T]; y: seq[T]) =
a.len = b.len
a.p = b.p
when false:
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
compilerRtl.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
type
PayloadBase = object

View File

@@ -1,18 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## **Warning:** Since version 0.11.4 this module is deprecated.
##
## This module implemented basic arithmetic operators for unsigned integers.
## These operators are now available in the ``system`` module directly.
{.deprecated.}
export `shr`, `shl`, `and`, `or`, `xor`, `==`, `+`, `-`, `*`, `div`, `mod`,
`<=`, `<`

View File

@@ -1,239 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## `Actor`:idx: support for Nim. An actor is implemented as a thread with
## a channel as its inbox. This module requires the ``--threads:on``
## command line switch.
##
## Example:
##
## .. code-block:: nim
##
## var
## a: ActorPool[int, void]
## createActorPool(a)
## for i in 0 ..< 300:
## a.spawn(i, proc (x: int) {.thread.} = echo x)
## a.join()
##
## **Note**: This whole module is deprecated. Use `threadpool` and ``spawn``
## instead.
{.deprecated.}
from os import sleep
type
Task*[In, Out] = object{.pure, final.} ## a task
when Out isnot void:
receiver*: ptr Channel[Out] ## the receiver channel of the response
action*: proc (x: In): Out {.thread.} ## action to execute;
## sometimes useful
shutDown*: bool ## set to tell an actor to shut-down
data*: In ## the data to process
Actor[In, Out] = object{.pure, final.}
i: Channel[Task[In, Out]]
t: Thread[ptr Actor[In, Out]]
PActor*[In, Out] = ptr Actor[In, Out] ## an actor
proc spawn*[In, Out](action: proc(
self: PActor[In, Out]){.thread.}): PActor[In, Out] =
## creates an actor; that is a thread with an inbox. The caller MUST call
## ``join`` because that also frees the actor's associated resources.
result = cast[PActor[In, Out]](allocShared0(sizeof(result[])))
open(result.i)
createThread(result.t, action, result)
proc inbox*[In, Out](self: PActor[In, Out]): ptr Channel[In] =
## gets a pointer to the associated inbox of the actor `self`.
result = addr(self.i)
proc running*[In, Out](a: PActor[In, Out]): bool =
## returns true if the actor `a` is running.
result = running(a.t)
proc ready*[In, Out](a: PActor[In, Out]): bool =
## returns true if the actor `a` is ready to process new messages.
result = ready(a.i)
proc join*[In, Out](a: PActor[In, Out]) =
## joins an actor.
joinThread(a.t)
close(a.i)
deallocShared(a)
proc recv*[In, Out](a: PActor[In, Out]): Task[In, Out] =
## receives a task from `a`'s inbox.
result = recv(a.i)
proc send*[In, Out, X, Y](receiver: PActor[In, Out], msg: In,
sender: PActor[X, Y]) =
## sends a message to `a`'s inbox.
var t: Task[In, Out]
t.receiver = addr(sender.i)
shallowCopy(t.data, msg)
send(receiver.i, t)
proc send*[In, Out](receiver: PActor[In, Out], msg: In,
sender: ptr Channel[Out] = nil) =
## sends a message to `receiver`'s inbox.
var t: Task[In, Out]
t.receiver = sender
shallowCopy(t.data, msg)
send(receiver.i, t)
proc sendShutdown*[In, Out](receiver: PActor[In, Out]) =
## send a shutdown message to `receiver`.
var t: Task[In, Out]
t.shutdown = true
send(receiver.i, t)
proc reply*[In, Out](t: Task[In, Out], m: Out) =
## sends a message to io's output message box.
when Out is void:
{.error: "you cannot reply to a void outbox".}
assert t.receiver != nil
send(t.receiver[], m)
# ----------------- actor pools ----------------------------------------------
type
ActorPool*[In, Out] = object{.pure, final.} ## an actor pool
actors: seq[PActor[In, Out]]
when Out isnot void:
outputs: Channel[Out]
proc `^`*[T](f: ptr Channel[T]): T =
## alias for 'recv'.
result = recv(f[])
proc poolWorker[In, Out](self: PActor[In, Out]) {.thread.} =
while true:
var m = self.recv
if m.shutDown: break
when Out is void:
m.action(m.data)
else:
send(m.receiver[], m.action(m.data))
#self.reply()
proc createActorPool*[In, Out](a: var ActorPool[In, Out], poolSize = 4) =
## creates an actor pool.
newSeq(a.actors, poolSize)
when Out isnot void:
open(a.outputs)
for i in 0 ..< a.actors.len:
a.actors[i] = spawn(poolWorker[In, Out])
proc sync*[In, Out](a: var ActorPool[In, Out], polling=50) =
## waits for every actor of `a` to finish with its work. Currently this is
## implemented as polling every `polling` ms and has a slight chance
## of failing since we check for every actor to be in `ready` state and not
## for messages still in ether. This will change in a later
## version, however.
var allReadyCount = 0
while true:
var wait = false
for i in 0..high(a.actors):
if not a.actors[i].i.ready:
wait = true
allReadyCount = 0
break
if not wait:
# it's possible that some actor sent a message to some other actor but
# both appeared to be non-working as the message takes some time to
# arrive. We assume that this won't take longer than `polling` and
# simply attempt a second time and declare victory then. ;-)
inc allReadyCount
if allReadyCount > 1: break
sleep(polling)
proc terminate*[In, Out](a: var ActorPool[In, Out]) =
## terminates each actor in the actor pool `a` and frees the
## resources attached to `a`.
var t: Task[In, Out]
t.shutdown = true
for i in 0..<a.actors.len: send(a.actors[i].i, t)
for i in 0..<a.actors.len: join(a.actors[i])
when Out isnot void:
close(a.outputs)
a.actors = @[]
proc join*[In, Out](a: var ActorPool[In, Out]) =
## short-cut for `sync` and then `terminate`.
sync(a)
terminate(a)
template setupTask =
t.action = action
shallowCopy(t.data, input)
template schedule =
# extremely simple scheduler: We always try the first thread first, so that
# it remains 'hot' ;-). Round-robin hurts for keeping threads hot.
for i in 0..high(p.actors):
if p.actors[i].i.ready:
p.actors[i].i.send(t)
return
# no thread ready :-( --> send message to the thread which has the least
# messages pending:
var minIdx = -1
var minVal = high(int)
for i in 0..high(p.actors):
var curr = p.actors[i].i.peek
if curr == 0:
# ok, is ready now:
p.actors[i].i.send(t)
return
if curr < minVal and curr >= 0:
minVal = curr
minIdx = i
if minIdx >= 0:
p.actors[minIdx].i.send(t)
else:
raise newException(DeadThreadError, "cannot send message; thread died")
proc spawn*[In, Out](p: var ActorPool[In, Out], input: In,
action: proc (input: In): Out {.thread.}
): ptr Channel[Out] =
## uses the actor pool to run ``action(input)`` concurrently.
## `spawn` is guaranteed to not block.
var t: Task[In, Out]
setupTask()
result = addr(p.outputs)
t.receiver = result
schedule()
proc spawn*[In](p: var ActorPool[In, void], input: In,
action: proc (input: In) {.thread.}) =
## uses the actor pool to run ``action(input)`` concurrently.
## `spawn` is guaranteed to not block.
var t: Task[In, void]
setupTask()
schedule()
when not defined(testing) and isMainModule:
var
a: ActorPool[int, void]
createActorPool(a)
for i in 0 ..< 300:
a.spawn(i, proc (x: int) {.thread.} = echo x)
when false:
proc treeDepth(n: PNode): int {.thread.} =
var x = a.spawn(treeDepth, n.le)
var y = a.spawn(treeDepth, n.ri)
result = max(^x, ^y) + 1
a.join()

View File

@@ -1,3 +0,0 @@
# to shut up the tester:
--threads:on

View File

@@ -81,7 +81,6 @@ type
oneSecond: BiggestInt # Bytes transferred in one second.
lastProgressReport: float # Time
toStore: string # Data left to upload (Only used with async)
else: nil
FtpClientObj* = FtpBaseObj[Socket]
FtpClient* = ref FtpClientObj

View File

@@ -1,112 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## **Warnings:** This module is deprecated since version 0.10.2.
## Use the `uri <uri.html>`_ module instead.
##
## Parses & constructs URLs.
{.deprecated.}
import strutils
type
Url* = tuple[ ## represents a *Uniform Resource Locator* (URL)
## any optional component is "" if it does not exist
scheme, username, password,
hostname, port, path, query, anchor: string]
proc parseUrl*(url: string): Url {.deprecated.} =
var i = 0
var scheme, username, password: string = ""
var hostname, port, path, query, anchor: string = ""
var temp = ""
if url[i] != '/': # url isn't a relative path
while true:
# Scheme
if url[i] == ':':
if url[i+1] == '/' and url[i+2] == '/':
scheme = temp
temp.setLen(0)
inc(i, 3) # Skip the //
# Authority(username, password)
if url[i] == '@':
username = temp
let colon = username.find(':')
if colon >= 0:
password = username.substr(colon+1)
username = username.substr(0, colon-1)
temp.setLen(0)
inc(i) #Skip the @
# hostname(subdomain, domain, port)
if url[i] == '/' or url[i] == '\0':
hostname = temp
let colon = hostname.find(':')
if colon >= 0:
port = hostname.substr(colon+1)
hostname = hostname.substr(0, colon-1)
temp.setLen(0)
break
temp.add(url[i])
inc(i)
if url[i] == '/': inc(i) # Skip the '/'
# Path
while true:
if url[i] == '?':
path = temp
temp.setLen(0)
if url[i] == '#':
if temp[0] == '?':
query = temp
else:
path = temp
temp.setLen(0)
if url[i] == '\0':
if temp[0] == '?':
query = temp
elif temp[0] == '#':
anchor = temp
else:
path = temp
break
temp.add(url[i])
inc(i)
return (scheme, username, password, hostname, port, path, query, anchor)
proc `$`*(u: Url): string {.deprecated.} =
## turns the URL `u` into its string representation.
result = ""
if u.scheme.len > 0:
result.add(u.scheme)
result.add("://")
if u.username.len > 0:
result.add(u.username)
if u.password.len > 0:
result.add(":")
result.add(u.password)
result.add("@")
result.add(u.hostname)
if u.port.len > 0:
result.add(":")
result.add(u.port)
if u.path.len > 0:
result.add("/")
result.add(u.path)
result.add(u.query)
result.add(u.anchor)

View File

@@ -1,14 +0,0 @@
import nativesockets
export nativesockets
{.warning: "rawsockets module is deprecated, use nativesockets instead".}
template newRawSocket*(domain, sockType, protocol: cint): untyped =
{.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
newNativeSocket(domain, sockType, protocol)
template newRawSocket*(domain: Domain = AF_INET,
sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): untyped =
{.warning: "newRawSocket is deprecated, use newNativeSocket instead".}
newNativeSocket(domain, sockType, protocol)

View File

@@ -252,8 +252,11 @@ proc createDiffs(dataA, dataB: DiffData): seq[Item] =
proc diffInt*(arrayA, arrayB: openArray[int]): seq[Item] =
## Find the difference in 2 arrays of integers.
##
## ``arrayA`` A-version of the numbers (usualy the old one)
##
## ``arrayB`` B-version of the numbers (usualy the new one)
##
## Returns a array of Items that describe the differences.
# The A-Version of the data (original data) to be compared.
@@ -273,15 +276,16 @@ proc diffInt*(arrayA, arrayB: openArray[int]): seq[Item] =
proc diffText*(textA, textB: string): seq[Item] =
## Find the difference in 2 text documents, comparing by textlines.
##
## The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents
## each line is converted into a (hash) number. This hash-value is computed by storing all
## textlines into a common hashtable so i can find dublicates in there, and generating a
## new number each time a new textline is inserted.
## ``TextA`` A-version of the text (usualy the old one)
## ``TextB`` B-version of the text (usualy the new one)
## ``trimSpace`` When set to true, all leading and trailing whitespace characters are stripped out before the comparation is done.
## ``ignoreSpace`` When set to true, all whitespace characters are converted to a single space character before the comparation is done.
## ``ignoreCase`` When set to true, all characters are converted to their lowercase equivivalence before the comparation is done.
##
## ``textA`` A-version of the text (usually the old one)
##
## ``textB`` B-version of the text (usually the new one)
##
## Returns a seq of Items that describe the differences.
# prepare the input-text and convert to comparable numbers.

View File

@@ -131,7 +131,7 @@ proc getErrInfo(db: var DbConn): tuple[res: int, ss, ne, msg: string] {.
511.TSqlSmallInt, retSz.addr.PSQLSMALLINT)
except:
discard
return (res.int, $sqlState, $nativeErr, $errMsg)
return (res.int, $(addr sqlState), $(addr nativeErr), $(addr errMsg))
proc dbError*(db: var DbConn) {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =

View File

@@ -166,10 +166,12 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
var stmt = setupQuery(db, query, args)
var L = (column_count(stmt))
var result = newRow(L)
while step(stmt) == SQLITE_ROW:
setRow(stmt, result, L)
yield result
if finalize(stmt) != SQLITE_OK: dbError(db)
try:
while step(stmt) == SQLITE_ROW:
setRow(stmt, result, L)
yield result
finally:
if finalize(stmt) != SQLITE_OK: dbError(db)
iterator instantRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): InstantRow
@@ -177,9 +179,11 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
## same as fastRows but returns a handle that can be used to get column text
## on demand using []. Returned handle is valid only within the iterator body.
var stmt = setupQuery(db, query, args)
while step(stmt) == SQLITE_ROW:
yield stmt
if finalize(stmt) != SQLITE_OK: dbError(db)
try:
while step(stmt) == SQLITE_ROW:
yield stmt
finally:
if finalize(stmt) != SQLITE_OK: dbError(db)
proc toTypeKind(t: var DbType; x: int32) =
case x
@@ -210,9 +214,11 @@ iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery,
## on demand using []. Returned handle is valid only within the iterator body.
var stmt = setupQuery(db, query, args)
setColumns(columns, stmt)
while step(stmt) == SQLITE_ROW:
yield stmt
if finalize(stmt) != SQLITE_OK: dbError(db)
try:
while step(stmt) == SQLITE_ROW:
yield stmt
finally:
if finalize(stmt) != SQLITE_OK: dbError(db)
proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
## returns text for given column of the row

View File

@@ -540,7 +540,7 @@ proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Opt
raise RegexInternalError(msg : "Unknown internal error: " & $execRet)
proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
## Like ```find(...)`` <#proc-find>`_, but anchored to the start of the
## Like ` ``find(...)`` <#proc-find>`_, but anchored to the start of the
## string.
##
runnableExamples:
@@ -550,11 +550,11 @@ proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[R
return str.matchImpl(pattern, start, endpos, pcre.ANCHORED)
iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): RegexMatch =
## Works the same as ```find(...)`` <#proc-find>`_, but finds every
## Works the same as ` ``find(...)`` <#proc-find>`_, but finds every
## non-overlapping match. ``"2222".find(re"22")`` is ``"22", "22"``, not
## ``"22", "22", "22"``.
##
## Arguments are the same as ```find(...)`` <#proc-find>`_
## Arguments are the same as ` ``find(...)`` <#proc-find>`_
##
## Variants:
##
@@ -633,7 +633,7 @@ 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(...)`` <#proc-find>`_.
## ``start`` behaves the same as in ` ``find(...)`` <#proc-find>`_.
##
runnableExamples:
# - If the match is zero-width, then the string is still split:

View File

@@ -561,31 +561,6 @@ proc escapeRe*(s: string): string =
result.add("\\x")
result.add(toHex(ord(c), 2))
const ## common regular expressions
reIdentifier* {.deprecated.} = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b"
## describes an identifier
reNatural* {.deprecated.} = r"\b\d+\b"
## describes a natural number
reInteger* {.deprecated.} = r"\b[-+]?\d+\b"
## describes an integer
reHex* {.deprecated.} = r"\b0[xX][0-9a-fA-F]+\b"
## describes a hexadecimal number
reBinary* {.deprecated.} = r"\b0[bB][01]+\b"
## describes a binary number (example: 0b11101)
reOctal* {.deprecated.} = r"\b0[oO][0-7]+\b"
## describes an octal number (example: 0o777)
reFloat* {.deprecated.} = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
## describes a floating point number
reEmail* {.deprecated.} = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\. &" &
r"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@" &
r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+" &
r"(?:[a-zA-Z]{2}|com|org|net|gov|mil|biz|" &
r"info|mobi|name|aero|jobs|museum)\b"
## describes a common email address
reURL* {.deprecated.} = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms-help)" &
r":((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
## describes an URL
when isMainModule:
doAssert match("(a b c)", rex"\( .* \)")
doAssert match("WHiLe", re("while", {reIgnoreCase}))
@@ -595,7 +570,7 @@ when isMainModule:
doAssert "ABC".match(rex"\d+ | \w+")
{.push warnings:off.}
doAssert matchLen("key", re(reIdentifier)) == 3
doAssert matchLen("key", re"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b") == 3
{.pop.}
var pattern = re"[a-z0-9]+\s*=\s*[a-z0-9]+"

View File

@@ -1,99 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2012 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module provides an easy to use sockets-style
## nim interface to the OpenSSL library.
##
## **Warning:** This module is deprecated, use the SSL procedures defined in
## the ``net`` module instead.
{.deprecated.}
import openssl, strutils, os
type
SecureSocket* = object
ssl: SslPtr
bio: BIO
proc connect*(sock: var SecureSocket, address: string,
port: int): int =
## Connects to the specified `address` on the specified `port`.
## Returns the result of the certificate validation.
SslLoadErrorStrings()
ERR_load_BIO_strings()
if SSL_library_init() != 1:
raiseOSError(osLastError())
var ctx = SSL_CTX_new(SSLv23_client_method())
if ctx == nil:
ERR_print_errors_fp(stderr)
raiseOSError(osLastError())
#if SSL_CTX_load_verify_locations(ctx,
# "/tmp/openssl-0.9.8e/certs/vsign1.pem", NIL) == 0:
# echo("Failed load verify locations")
# ERR_print_errors_fp(stderr)
sock.bio = BIO_new_ssl_connect(ctx)
if BIO_get_ssl(sock.bio, addr(sock.ssl)) == 0:
raiseOSError(osLastError())
if BIO_set_conn_hostname(sock.bio, address & ":" & $port) != 1:
raiseOSError(osLastError())
if BIO_do_connect(sock.bio) <= 0:
ERR_print_errors_fp(stderr)
raiseOSError(osLastError())
result = SSL_get_verify_result(sock.ssl)
proc recvLine*(sock: SecureSocket, line: var TaintedString): bool =
## Acts in a similar fashion to the `recvLine` in the sockets module.
## Returns false when no data is available to be read.
## `Line` must be initialized and not nil!
setLen(line.string, 0)
while true:
var c: array[0..0, char]
var n = BIO_read(sock.bio, addr c, c.len.cint)
if n <= 0: return false
if c[0] == '\r':
n = BIO_read(sock.bio, addr c, c.len.cint)
if n > 0 and c[0] == '\L':
return true
elif n <= 0:
return false
elif c[0] == '\L': return true
add(line.string, c[0])
proc send*(sock: SecureSocket, data: string) =
## Writes `data` to the socket.
if BIO_write(sock.bio, data, data.len.cint) <= 0:
raiseOSError(osLastError())
proc close*(sock: SecureSocket) =
## Closes the socket
if BIO_free(sock.bio) <= 0:
ERR_print_errors_fp(stderr)
raiseOSError(osLastError())
when not defined(testing) and isMainModule:
var s: SecureSocket
echo connect(s, "smtp.gmail.com", 465)
#var buffer: array[0..255, char]
#echo BIO_read(bio, buffer, buffer.len)
var buffer: string = ""
echo s.recvLine(buffer)
echo buffer
echo buffer.len

View File

@@ -416,12 +416,12 @@ type
BoundingRect* {.importc.} = ref object
top*, bottom*, left*, right*, x*, y*, width*, height*: float
PerformanceMemory* {.importc.} = ref object
PerformanceMemory* {.importc.} = ref object
jsHeapSizeLimit*: float
totalJSHeapSize*: float
usedJSHeapSize*: float
PerformanceTiming* {.importc.} = ref object
PerformanceTiming* {.importc.} = ref object
connectStart*: float
domComplete*: float
domContentLoadedEventEnd*: float
@@ -459,7 +459,6 @@ proc dispatchEvent*(et: EventTarget, ev: Event)
proc alert*(w: Window, msg: cstring)
proc back*(w: Window)
proc blur*(w: Window)
proc captureEvents*(w: Window, eventMask: int) {.deprecated.}
proc clearInterval*(w: Window, interval: ref TInterval)
proc clearTimeout*(w: Window, timeout: ref TTimeOut)
proc close*(w: Window)
@@ -478,7 +477,6 @@ proc open*(w: Window, uri, windowname: cstring,
properties: cstring = nil): Window
proc print*(w: Window)
proc prompt*(w: Window, text, default: cstring): cstring
proc releaseEvents*(w: Window, eventMask: int) {.deprecated.}
proc resizeBy*(w: Window, x, y: int)
proc resizeTo*(w: Window, x, y: int)
proc routeEvent*(w: Window, event: Event)
@@ -513,7 +511,6 @@ proc setAttribute*(n: Node, name, value: cstring)
proc setAttributeNode*(n: Node, attr: Node)
# Document "methods"
proc captureEvents*(d: Document, eventMask: int) {.deprecated.}
proc createAttribute*(d: Document, identifier: cstring): Node
proc createElement*(d: Document, identifier: cstring): Element
proc createTextNode*(d: Document, identifier: cstring): Node
@@ -524,7 +521,6 @@ proc getElementsByClassName*(d: Document, name: cstring): seq[Element]
proc getSelection*(d: Document): cstring
proc handleEvent*(d: Document, event: Event)
proc open*(d: Document)
proc releaseEvents*(d: Document, eventMask: int) {.deprecated.}
proc routeEvent*(d: Document, event: Event)
proc write*(d: Document, text: cstring)
proc writeln*(d: Document, text: cstring)
@@ -605,25 +601,3 @@ proc parseInt*(s: cstring): int {.importc, nodecl.}
proc parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
proc newEvent*(name: cstring): Event {.importcpp: "new Event(@)", constructor.}
type
TEventHandlers* {.deprecated.} = EventTargetObj
TWindow* {.deprecated.} = WindowObj
TFrame* {.deprecated.} = FrameObj
TNode* {.deprecated.} = NodeObj
TDocument* {.deprecated.} = DocumentObj
TElement* {.deprecated.} = ElementObj
TLink* {.deprecated.} = LinkObj
TEmbed* {.deprecated.} = EmbedObj
TAnchor* {.deprecated.} = AnchorObj
TOption* {.deprecated.} = OptionObj
TForm* {.deprecated.} = FormObj
TImage* {.deprecated.} = ImageObj
TNodeType* {.deprecated.} = NodeType
TEvent* {.deprecated.} = EventObj
TLocation* {.deprecated.} = LocationObj
THistory* {.deprecated.} = HistoryObj
TNavigator* {.deprecated.} = NavigatorObj
TStyle* {.deprecated.} = StyleObj
TScreen* {.deprecated.} = ScreenObj
TApplet* {.importc, deprecated.} = object of RootObj

View File

@@ -73,7 +73,7 @@ proc parse*(d: DateLib, s: cstring): int {.importcpp.}
proc newDate*(): DateTime {.
importcpp: "new Date()".}
proc newDate*(date: int|string): DateTime {.
proc newDate*(date: int|int64|string): DateTime {.
importcpp: "new Date(#)".}
proc newDate*(year, month, day, hours, minutes,
@@ -90,6 +90,16 @@ proc getSeconds*(d: DateTime): int {.importcpp.}
proc getYear*(d: DateTime): int {.importcpp.}
proc getTime*(d: DateTime): int {.importcpp.}
proc toString*(d: DateTime): cstring {.importcpp.}
proc getUTCDate*(d: DateTime): int {.importcpp.}
proc getUTCFullYear*(d: DateTime): int {.importcpp.}
proc getUTCHours*(d: DateTime): int {.importcpp.}
proc getUTCMilliseconds*(d: DateTime): int {.importcpp.}
proc getUTCMinutes*(d: DateTime): int {.importcpp.}
proc getUTCMonth*(d: DateTime): int {.importcpp.}
proc getUTCSeconds*(d: DateTime): int {.importcpp.}
proc getUTCDay*(d: DateTime): int {.importcpp.}
proc getTimezoneOffset*(d: DateTime): int {.importcpp.}
proc setFullYear*(d: DateTime, year: int) {.importcpp.}
#JSON library
proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.}

View File

@@ -104,6 +104,12 @@ var
jsFilename* {.importc: "__filename", nodecl.}: cstring
## JavaScript's __filename pseudo-variable
proc isNull*[T](x: T): bool {.noSideEffect, importcpp: "(# === null)".}
## check if a value is exactly null
proc isUndefined*[T](x: T): bool {.noSideEffect, importcpp: "(# === undefined)".}
## check if a value is exactly undefined
# Exceptions
type
JsError* {.importc: "Error".} = object of JsRoot

View File

@@ -166,13 +166,13 @@ __clang__
# define N_STDCALL(rettype, name) rettype __stdcall name
# define N_SYSCALL(rettype, name) rettype __syscall name
# define N_FASTCALL(rettype, name) rettype __fastcall name
# define N_SAFECALL(rettype, name) rettype __safecall name
# define N_SAFECALL(rettype, name) rettype __stdcall name
/* function pointers with calling convention: */
# define N_CDECL_PTR(rettype, name) rettype (__cdecl *name)
# define N_STDCALL_PTR(rettype, name) rettype (__stdcall *name)
# define N_SYSCALL_PTR(rettype, name) rettype (__syscall *name)
# define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name)
# define N_SAFECALL_PTR(rettype, name) rettype (__safecall *name)
# define N_SAFECALL_PTR(rettype, name) rettype (__stdcall *name)
# ifdef __cplusplus
# define N_LIB_EXPORT extern "C" __declspec(dllexport)

View File

@@ -560,9 +560,9 @@ proc match(p: RstParser, start: int, expr: string): bool =
result = (p.tok[j].kind == tkWord) or (p.tok[j].symbol == "#")
if result:
case p.tok[j].symbol[0]
of 'a'..'z', 'A'..'Z': result = len(p.tok[j].symbol) == 1
of 'a'..'z', 'A'..'Z', '#': result = len(p.tok[j].symbol) == 1
of '0'..'9': result = allCharsInSet(p.tok[j].symbol, {'0'..'9'})
else: discard
else: result = false
else:
var c = expr[i]
var length = 0
@@ -780,6 +780,31 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
add(result, nil)
add(result, lb)
proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool =
result = true
var desc, link = ""
var i = p.idx
template parse(endToken, dest) =
inc i # skip begin token
while true:
if p.tok[i].kind in {tkEof, tkIndent}: return false
if p.tok[i].symbol == endToken: break
dest.add p.tok[i].symbol
inc i
inc i # skip end token
parse("]", desc)
if p.tok[i].symbol != "(": return false
parse(")", link)
let child = newRstNode(rnHyperlink)
child.add desc
child.add link
# only commit if we detected no syntax error:
father.add child
p.idx = i
result = true
proc parseInline(p: var RstParser, father: PRstNode) =
case p.tok[p.idx].kind
of tkPunct:
@@ -811,6 +836,9 @@ proc parseInline(p: var RstParser, father: PRstNode) =
var n = newRstNode(rnSubstitutionReferences)
parseUntil(p, n, "|", false)
add(father, n)
elif roSupportMarkdown in p.s.options and p.tok[p.idx].symbol == "[" and
parseMarkdownLink(p, father):
discard "parseMarkdownLink already processed it"
else:
if roSupportSmilies in p.s.options:
let n = parseSmiley(p)
@@ -1037,15 +1065,32 @@ proc isOptionList(p: RstParser): bool =
result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or
match(p, p.idx, "/w") or match(p, p.idx, "//w")
proc isMarkdownHeadlinePattern(s: string): bool =
if s.len >= 1 and s.len <= 6:
for c in s:
if c != '#': return false
result = true
proc isMarkdownHeadline(p: RstParser): bool =
if roSupportMarkdown in p.s.options:
if isMarkdownHeadlinePattern(p.tok[p.idx].symbol) and p.tok[p.idx+1].kind == tkWhite:
if p.tok[p.idx+2].kind in {tkWord, tkOther, tkPunct}:
result = true
proc whichSection(p: RstParser): RstNodeKind =
case p.tok[p.idx].kind
of tkAdornment:
if match(p, p.idx + 1, "ii"): result = rnTransition
elif match(p, p.idx + 1, " a"): result = rnTable
elif match(p, p.idx + 1, "i"): result = rnOverline
else: result = rnLeaf
elif isMarkdownHeadline(p):
result = rnHeadline
else:
result = rnLeaf
of tkPunct:
if match(p, tokenAfterNewline(p), "ai"):
if isMarkdownHeadline(p):
result = rnHeadline
elif match(p, tokenAfterNewline(p), "ai"):
result = rnHeadline
elif p.tok[p.idx].symbol == "::":
result = rnLiteralBlock
@@ -1060,7 +1105,7 @@ proc whichSection(p: RstParser): RstNodeKind =
elif match(p, p.idx, ":w:") and predNL(p):
# (p.tok[p.idx].symbol == ":")
result = rnFieldList
elif match(p, p.idx, "(e) "):
elif match(p, p.idx, "(e) ") or match(p, p.idx, "e. "):
result = rnEnumList
elif match(p, p.idx, "+a+"):
result = rnGridTable
@@ -1130,12 +1175,18 @@ proc parseParagraph(p: var RstParser, result: PRstNode) =
proc parseHeadline(p: var RstParser): PRstNode =
result = newRstNode(rnHeadline)
parseUntilNewline(p, result)
assert(p.tok[p.idx].kind == tkIndent)
assert(p.tok[p.idx + 1].kind == tkAdornment)
var c = p.tok[p.idx + 1].symbol[0]
inc(p.idx, 2)
result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c)
if isMarkdownHeadline(p):
result.level = p.tok[p.idx].symbol.len
assert(p.tok[p.idx+1].kind == tkWhite)
inc p.idx, 2
parseUntilNewline(p, result)
else:
parseUntilNewline(p, result)
assert(p.tok[p.idx].kind == tkIndent)
assert(p.tok[p.idx + 1].kind == tkAdornment)
var c = p.tok[p.idx + 1].symbol[0]
inc(p.idx, 2)
result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c)
type
IntSeq = seq[int]

View File

@@ -89,6 +89,9 @@ proc lastSon*(n: PRstNode): PRstNode =
proc add*(father, son: PRstNode) =
add(father.sons, son)
proc add*(father: PRstNode; s: string) =
add(father.sons, newRstNode(rnLeaf, s))
proc addIfNotNil*(father, son: PRstNode) =
if son != nil: add(father, son)

View File

@@ -1134,11 +1134,9 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
of rnTripleEmphasis:
renderAux(d, n, "<strong><em>$1</em></strong>",
"\\textbf{emph{$1}}", result)
of rnInterpretedText:
renderAux(d, n, "<cite>$1</cite>", "\\emph{$1}", result)
of rnIdx:
renderIndexTerm(d, n, result)
of rnInlineLiteral:
of rnInlineLiteral, rnInterpretedText:
renderAux(d, n,
"<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>",
"\\texttt{$1}", result)

View File

@@ -295,6 +295,7 @@ const IF_NAMESIZE* = cint(16)
const IPPROTO_IP* = cint(0)
const IPPROTO_IPV6* = cint(41)
const IPPROTO_ICMP* = cint(1)
const IPPROTO_ICMPV6* = cint(58)
const IPPROTO_RAW* = cint(255)
const IPPROTO_TCP* = cint(6)
const IPPROTO_UDP* = cint(17)

View File

@@ -237,6 +237,7 @@ const IF_NAMESIZE* = cint(16)
const IPPROTO_IP* = cint(0)
const IPPROTO_IPV6* = cint(41)
const IPPROTO_ICMP* = cint(1)
const IPPROTO_ICMPV6* = cint(58)
const IPPROTO_RAW* = cint(255)
const IPPROTO_TCP* = cint(6)
const IPPROTO_UDP* = cint(17)

View File

@@ -302,6 +302,7 @@ var IF_NAMESIZE* {.importc: "IF_NAMESIZE", header: "<net/if.h>".}: cint
var IPPROTO_IP* {.importc: "IPPROTO_IP", header: "<netinet/in.h>".}: cint
var IPPROTO_IPV6* {.importc: "IPPROTO_IPV6", header: "<netinet/in.h>".}: cint
var IPPROTO_ICMP* {.importc: "IPPROTO_ICMP", header: "<netinet/in.h>".}: cint
var IPPROTO_ICMPV6* {.importc: "IPPROTO_ICMPV6", header: "<netinet/in.h>".}: cint
var IPPROTO_RAW* {.importc: "IPPROTO_RAW", header: "<netinet/in.h>".}: cint
var IPPROTO_TCP* {.importc: "IPPROTO_TCP", header: "<netinet/in.h>".}: cint
var IPPROTO_UDP* {.importc: "IPPROTO_UDP", header: "<netinet/in.h>".}: cint

View File

@@ -143,11 +143,14 @@ proc parseUppercaseMethod(name: string): HttpMethod =
of "TRACE": HttpTrace
else: raise newException(ValueError, "Invalid HTTP method " & name)
proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
client: AsyncSocket,
address: string, lineFut: FutureVar[string],
callback: proc (request: Request):
Future[void] {.closure, gcsafe.}) {.async.} =
proc processRequest(
server: AsyncHttpServer,
req: FutureVar[Request],
client: AsyncSocket,
address: string,
lineFut: FutureVar[string],
callback: proc (request: Request): Future[void] {.closure, gcsafe.},
): Future[bool] {.async.} =
# Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
template request(): Request =
@@ -171,12 +174,12 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
if lineFut.mget == "":
client.close()
return
return false
if lineFut.mget.len > maxLine:
await request.respondError(Http413)
client.close()
return
return false
if lineFut.mget != "\c\L":
break
@@ -189,22 +192,22 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
request.reqMethod = parseUppercaseMethod(linePart)
except ValueError:
asyncCheck request.respondError(Http400)
return
return true # Retry processing of request
of 1:
try:
parseUri(linePart, request.url)
except ValueError:
asyncCheck request.respondError(Http400)
return
return true
of 2:
try:
request.protocol = parseProtocol(linePart)
except ValueError:
asyncCheck request.respondError(Http400)
return
return true
else:
await request.respondError(Http400)
return
return true
inc i
# Headers
@@ -215,10 +218,10 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
await client.recvLineInto(lineFut, maxLength=maxLine)
if lineFut.mget == "":
client.close(); return
client.close(); return false
if lineFut.mget.len > maxLine:
await request.respondError(Http413)
client.close(); return
client.close(); return false
if lineFut.mget == "\c\L": break
let (key, value) = parseHeader(lineFut.mget)
request.headers[key] = value
@@ -226,7 +229,7 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
if request.headers.len > headerLimit:
await client.sendStatus("400 Bad Request")
request.client.close()
return
return false
if request.reqMethod == HttpPost:
# Check for Expect header
@@ -242,24 +245,24 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
var contentLength = 0
if parseSaturatedNatural(request.headers["Content-Length"], contentLength) == 0:
await request.respond(Http400, "Bad Request. Invalid Content-Length.")
return
return true
else:
if contentLength > server.maxBody:
await request.respondError(Http413)
return
return false
request.body = await client.recv(contentLength)
if request.body.len != contentLength:
await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
return
return true
elif request.reqMethod == HttpPost:
await request.respond(Http411, "Content-Length required.")
return
return true
# Call the user's callback.
await callback(request)
if "upgrade" in request.headers.getOrDefault("connection"):
return
return false
# Persistent connections
if (request.protocol == HttpVer11 and
@@ -273,7 +276,7 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request],
discard
else:
request.client.close()
return
return false
proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string,
callback: proc (request: Request):
@@ -285,7 +288,10 @@ proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string
lineFut.mget() = newStringOfCap(80)
while not client.isClosed:
await processRequest(server, request, client, address, lineFut, callback)
let retry = await processRequest(
server, request, client, address, lineFut, callback
)
if not retry: break
proc serve*(server: AsyncHttpServer, port: Port,
callback: proc (request: Request): Future[void] {.closure,gcsafe.},

View File

@@ -9,37 +9,49 @@
## This module implements a base64 encoder and decoder.
##
## Base64 is an encoding and decoding technique used to convert binary
## data to an ASCII string format.
## Each Base64 digit represents exactly 6 bits of data. Three 8-bit
## bytes (i.e., a total of 24 bits) can therefore be represented by
## four 6-bit Base64 digits.
##
##
## Basic usage
## ===========
##
## Encoding data
## -------------
##
## In order to encode some text simply call the ``encode`` procedure:
##
## .. code-block::nim
## import base64
## let encoded = encode("Hello World")
## echo(encoded) # SGVsbG8gV29ybGQ=
## .. code-block::nim
## import base64
## let encoded = encode("Hello World")
## assert encoded == "SGVsbG8gV29ybGQ="
##
## Apart from strings you can also encode lists of integers or characters:
##
## .. code-block::nim
## import base64
## let encodedInts = encode([1,2,3])
## echo(encodedInts) # AQID
## let encodedChars = encode(['h','e','y'])
## echo(encodedChars) # aGV5
## .. code-block::nim
## import base64
## let encodedInts = encode([1,2,3])
## assert encodedInts == "AQID"
## let encodedChars = encode(['h','e','y'])
## assert encodedChars == "aGV5"
##
## The ``encode`` procedure takes an ``openarray`` so both arrays and sequences
## can be passed as parameters.
##
## Decoding data
## -------------
##
## To decode a base64 encoded data string simply call the ``decode``
## procedure:
## .. code-block::nim
## import base64
## let decoded = decode("SGVsbG8gV29ybGQ=")
## assert decoded == "Hello World"
##
## .. code-block::nim
## import base64
## echo(decode("SGVsbG8gV29ybGQ=")) # Hello World
##
## See also
## ========
##
## * `hashes module<hashes.html>`_ for efficient computations of hash values for diverse Nim types
## * `md5 module<md5.html>`_ implements the MD5 checksum algorithm
## * `sha1 module<sha1.html>`_ implements a sha1 encoder and decoder
const
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
@@ -100,18 +112,33 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
discard
proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
## encodes `s` into base64 representation. After `lineLen` characters, a
## `newline` is added.
## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
## ``newline`` is added.
##
## This procedure encodes an openarray (array or sequence) of either integers
## or characters.
##
## **See also:**
## * `encode proc<#encode,string,int,string>`_ for encoding a string
## * `decode proc<#decode,string>`_ for decoding a string
runnableExamples:
assert encode(['n', 'i', 'm']) == "bmlt"
assert encode(@['n', 'i', 'm']) == "bmlt"
assert encode([1, 2, 3, 4, 5]) == "AQIDBAU="
encodeInternal(s, lineLen, newLine)
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
## encodes `s` into base64 representation. After `lineLen` characters, a
## `newline` is added.
## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
## ``newline`` is added.
##
## This procedure encodes a string.
##
## **See also:**
## * `encode proc<#encode,openArray[T],int,string>`_ for encoding an openarray
## * `decode proc<#decode,string>`_ for decoding a string
runnableExamples:
assert encode("Hello World") == "SGVsbG8gV29ybGQ="
assert encode("Hello World", 3, "\n") == "SGVs\nbG8g\nV29ybGQ="
encodeInternal(s, lineLen, newLine)
proc decodeByte(b: char): int {.inline.} =
@@ -123,8 +150,15 @@ proc decodeByte(b: char): int {.inline.} =
else: result = 63
proc decode*(s: string): string =
## decodes a string in base64 representation back into its original form.
## Whitespace is skipped.
## Decodes string ``s`` in base64 representation back into its original form.
## The initial whitespace is skipped.
##
## **See also:**
## * `encode proc<#encode,openArray[T],int,string>`_ for encoding an openarray
## * `encode proc<#encode,string,int,string>`_ for encoding a string
runnableExamples:
assert decode("SGVsbG8gV29ybGQ=") == "Hello World"
assert decode(" SGVsbG8gV29ybGQ=") == "Hello World"
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
var total = ((len(s) + 3) div 4) * 3
# total is an upper bound, as we will skip arbitrary whitespace:

View File

@@ -337,11 +337,6 @@ proc setStackTraceStdout*() =
## Makes Nim output stacktraces to stdout, instead of server log.
errorMessageWriter = writeErrorMessage
proc setStackTraceNewLine*() {.deprecated.} =
## Makes Nim output stacktraces to stdout, instead of server log.
## Depracated alias for setStackTraceStdout.
setStackTraceStdout()
proc setCookie*(name, value: string) =
## Sets a cookie.
write(stdout, "Set-Cookie: ", name, "=", value, "\n")

View File

@@ -202,12 +202,6 @@ proc `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline,
## If `key` is not in `t`, the ``KeyError`` exception is raised.
get(c, key)
proc mget*[T](c: var CritBitTree[T], key: string): var T {.inline, deprecated.} =
## retrieves the value at ``c[key]``. The value can be modified.
## If `key` is not in `t`, the ``KeyError`` exception is raised.
## Use ```[]``` instead.
get(c, key)
iterator leaves[T](n: Node[T]): Node[T] =
if n != nil:
# XXX actually we could compute the necessary stack size in advance:

View File

@@ -20,41 +20,59 @@
## access, unless your program logic guarantees it indirectly.
##
## .. code-block:: Nim
## proc foo(a, b: Positive) = # assume random positive values for `a` and `b`
## var deq = initDeque[int]() # initializes the object
## for i in 1 ..< a: deq.addLast i # populates the deque
## import deques
##
## if b < deq.len: # checking before indexed access
## echo "The element at index position ", b, " is ", deq[b]
## var a = initDeque[int]()
##
## # The following two lines don't need any checking on access due to the
## # logic of the program, but that would not be the case if `a` could be 0.
## assert deq.peekFirst == 1
## assert deq.peekLast == a
## doAssertRaises(IndexError, echo a[0])
##
## while deq.len > 0: # checking if the deque is empty
## echo deq.popLast()
## for i in 1 .. 5:
## a.addLast(10*i)
## assert $a == "[10, 20, 30, 40, 50]"
##
## Note: For inter thread communication use
## a `Channel <channels.html>`_ instead.
## assert a.peekFirst == 10
## assert a.peekLast == 50
## assert len(a) == 5
##
## assert a.popFirst == 10
## assert a.popLast == 50
## assert len(a) == 3
##
## a.addFirst(11)
## a.addFirst(22)
## a.addFirst(33)
## assert $a == "[33, 22, 11, 20, 30, 40]"
##
## a.shrink(fromFirst = 1, fromLast = 2)
## assert $a == "[22, 11, 20]"
##
##
## **See also:**
## * `lists module <lists.html>`_ for singly and doubly linked lists and rings
## * `channels module <channels.html>`_ for inter-thread communication
import math, typetraits
type
Deque*[T] = object
## A double-ended queue backed with a ringed seq buffer.
##
## To initialize an empty deque use `initDeque proc <#initDeque,int>`_.
data: seq[T]
head, tail, count, mask: int
proc initDeque*[T](initialSize: int = 4): Deque[T] =
## Create a new deque.
## Optionally, the initial capacity can be reserved via `initialSize` as a
## performance optimization. The length of a newly created deque will still
## be 0.
## Create a new empty deque.
##
## `initialSize` needs to be a power of two. If you need to accept runtime
## values for this you could use the ``nextPowerOfTwo`` proc from the
## `math <math.html>`_ module.
## Optionally, the initial capacity can be reserved via `initialSize`
## as a performance optimization.
## The length of a newly created deque will still be 0.
##
## ``initialSize`` must be a power of two (default: 4).
## If you need to accept runtime values for this you could use the
## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the
## `math module<math.html>`_.
assert isPowerOfTwo(initialSize)
result.mask = initialSize-1
newSeq(result.data, initialSize)
@@ -75,33 +93,128 @@ template xBoundsCheck(deq, i) =
if unlikely(i >= deq.count): # x < deq.low is taken care by the Natural parameter
raise newException(IndexError,
"Out of bounds: " & $i & " > " & $(deq.count - 1))
if unlikely(i < 0): # when used with BackwardsIndex
raise newException(IndexError,
"Out of bounds: " & $i & " < 0")
proc `[]`*[T](deq: Deque[T], i: Natural) : T {.inline.} =
## Access the i-th element of `deq` by order from first to last.
## deq[0] is the first, deq[^1] is the last.
## Access the i-th element of `deq`.
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addLast(10*i)
assert a[0] == 10
assert a[3] == 40
doAssertRaises(IndexError, echo a[8])
xBoundsCheck(deq, i)
return deq.data[(deq.head + i) and deq.mask]
proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} =
## Access the i-th element of `deq` and returns a mutable
## Access the i-th element of `deq` and return a mutable
## reference to it.
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addLast(10*i)
assert a[0] == 10
assert a[3] == 40
doAssertRaises(IndexError, echo a[8])
xBoundsCheck(deq, i)
return deq.data[(deq.head + i) and deq.mask]
proc `[]=`* [T] (deq: var Deque[T], i: Natural, val : T) {.inline.} =
proc `[]=`*[T](deq: var Deque[T], i: Natural, val : T) {.inline.} =
## Change the i-th element of `deq`.
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addLast(10*i)
a[0] = 99
a[3] = 66
assert $a == "[99, 20, 30, 66, 50]"
xBoundsCheck(deq, i)
deq.data[(deq.head + i) and deq.mask] = val
proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} =
## Access the backwards indexed i-th element.
##
## `deq[^1]` is the last element.
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addLast(10*i)
assert a[^1] == 50
assert a[^4] == 20
doAssertRaises(IndexError, echo a[^9])
xBoundsCheck(deq, deq.len - int(i))
return deq[deq.len - int(i)]
proc `[]`*[T](deq: var Deque[T], i: BackwardsIndex): var T {.inline.} =
## Access the backwards indexed i-th element.
##
## `deq[^1]` is the last element.
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addLast(10*i)
assert a[^1] == 50
assert a[^4] == 20
doAssertRaises(IndexError, echo a[^9])
xBoundsCheck(deq, deq.len - int(i))
return deq[deq.len - int(i)]
proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} =
## Change the backwards indexed i-th element.
##
## `deq[^1]` is the last element.
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addLast(10*i)
a[^1] = 99
a[^3] = 77
assert $a == "[10, 20, 77, 40, 99]"
xBoundsCheck(deq, deq.len - int(i))
deq[deq.len - int(i)] = x
iterator items*[T](deq: Deque[T]): T =
## Yield every element of `deq`.
##
## **Examples:**
##
## .. code-block::
## var a = initDeque[int]()
## for i in 1 .. 3:
## a.addLast(10*i)
##
## for x in a: # the same as: for x in items(a):
## echo x
##
## # 10
## # 20
## # 30
##
var i = deq.head
for c in 0 ..< deq.count:
yield deq.data[i]
i = (i + 1) and deq.mask
iterator mitems*[T](deq: var Deque[T]): var T =
## Yield every element of `deq`.
## Yield every element of `deq`, which can be modified.
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addLast(10*i)
assert $a == "[10, 20, 30, 40, 50]"
for x in mitems(a):
x = 5*x - 1
assert $a == "[49, 99, 149, 199, 249]"
var i = deq.head
for c in 0 ..< deq.count:
yield deq.data[i]
@@ -109,18 +222,35 @@ iterator mitems*[T](deq: var Deque[T]): var T =
iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] =
## Yield every (position, value) of `deq`.
##
## **Examples:**
##
## .. code-block::
## var a = initDeque[int]()
## for i in 1 .. 3:
## a.addLast(10*i)
##
## for k, v in pairs(a):
## echo "key: ", k, ", value: ", v
##
## # key: 0, value: 10
## # key: 1, value: 20
## # key: 2, value: 30
##
var i = deq.head
for c in 0 ..< deq.count:
yield (c, deq.data[i])
i = (i + 1) and deq.mask
proc contains*[T](deq: Deque[T], item: T): bool {.inline.} =
## Return true if `item` is in `deq` or false if not found. Usually used
## via the ``in`` operator. It is the equivalent of ``deq.find(item) >= 0``.
## Return true if `item` is in `deq` or false if not found.
##
## Usually used via the ``in`` operator.
## It is the equivalent of ``deq.find(item) >= 0``.
##
## .. code-block:: Nim
## if x in q:
## assert q.contains x
## assert q.contains(x)
for e in deq:
if e == item: return true
return false
@@ -138,6 +268,19 @@ proc expandIfNeeded[T](deq: var Deque[T]) =
proc addFirst*[T](deq: var Deque[T], item: T) =
## Add an `item` to the beginning of the `deq`.
##
## See also:
## * `addLast proc <#addLast,Deque[T],T>`_
## * `peekFirst proc <#peekFirst,Deque[T]>`_
## * `peekLast proc <#peekLast,Deque[T]>`_
## * `popFirst proc <#popFirst,Deque[T]>`_
## * `popLast proc <#popLast,Deque[T]>`_
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addFirst(10*i)
assert $a == "[50, 40, 30, 20, 10]"
expandIfNeeded(deq)
inc deq.count
deq.head = (deq.head - 1) and deq.mask
@@ -145,6 +288,19 @@ proc addFirst*[T](deq: var Deque[T], item: T) =
proc addLast*[T](deq: var Deque[T], item: T) =
## Add an `item` to the end of the `deq`.
##
## See also:
## * `addFirst proc <#addFirst,Deque[T],T>`_
## * `peekFirst proc <#peekFirst,Deque[T]>`_
## * `peekLast proc <#peekLast,Deque[T]>`_
## * `popFirst proc <#popFirst,Deque[T]>`_
## * `popLast proc <#popLast,Deque[T]>`_
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addLast(10*i)
assert $a == "[10, 20, 30, 40, 50]"
expandIfNeeded(deq)
inc deq.count
deq.data[deq.tail] = item
@@ -152,11 +308,41 @@ proc addLast*[T](deq: var Deque[T], item: T) =
proc peekFirst*[T](deq: Deque[T]): T {.inline.}=
## Returns the first element of `deq`, but does not remove it from the deque.
##
## See also:
## * `addFirst proc <#addFirst,Deque[T],T>`_
## * `addLast proc <#addLast,Deque[T],T>`_
## * `peekLast proc <#peekLast,Deque[T]>`_
## * `popFirst proc <#popFirst,Deque[T]>`_
## * `popLast proc <#popLast,Deque[T]>`_
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addLast(10*i)
assert $a == "[10, 20, 30, 40, 50]"
assert a.peekFirst == 10
assert len(a) == 5
emptyCheck(deq)
result = deq.data[deq.head]
proc peekLast*[T](deq: Deque[T]): T {.inline.} =
## Returns the last element of `deq`, but does not remove it from the deque.
##
## See also:
## * `addFirst proc <#addFirst,Deque[T],T>`_
## * `addLast proc <#addLast,Deque[T],T>`_
## * `peekFirst proc <#peekFirst,Deque[T]>`_
## * `popFirst proc <#popFirst,Deque[T]>`_
## * `popLast proc <#popLast,Deque[T]>`_
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addLast(10*i)
assert $a == "[10, 20, 30, 40, 50]"
assert a.peekLast == 50
assert len(a) == 5
emptyCheck(deq)
result = deq.data[(deq.tail - 1) and deq.mask]
@@ -165,6 +351,23 @@ template destroy(x: untyped) =
proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
## Remove and returns the first element of the `deq`.
##
## See also:
## * `addFirst proc <#addFirst,Deque[T],T>`_
## * `addLast proc <#addLast,Deque[T],T>`_
## * `peekFirst proc <#peekFirst,Deque[T]>`_
## * `peekLast proc <#peekLast,Deque[T]>`_
## * `popLast proc <#popLast,Deque[T]>`_
## * `clear proc <#clear,Deque[T]>`_
## * `shrink proc <#shrink,Deque[T],int,int>`_
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addLast(10*i)
assert $a == "[10, 20, 30, 40, 50]"
assert a.popFirst == 10
assert $a == "[20, 30, 40, 50]"
emptyCheck(deq)
dec deq.count
result = deq.data[deq.head]
@@ -173,6 +376,23 @@ proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
## Remove and returns the last element of the `deq`.
##
## See also:
## * `addFirst proc <#addFirst,Deque[T],T>`_
## * `addLast proc <#addLast,Deque[T],T>`_
## * `peekFirst proc <#peekFirst,Deque[T]>`_
## * `peekLast proc <#peekLast,Deque[T]>`_
## * `popFirst proc <#popFirst,Deque[T]>`_
## * `clear proc <#clear,Deque[T]>`_
## * `shrink proc <#shrink,Deque[T],int,int>`_
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addLast(10*i)
assert $a == "[10, 20, 30, 40, 50]"
assert a.popLast == 50
assert $a == "[10, 20, 30, 40]"
emptyCheck(deq)
dec deq.count
deq.tail = (deq.tail - 1) and deq.mask
@@ -181,17 +401,39 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
proc clear*[T](deq: var Deque[T]) {.inline.} =
## Resets the deque so that it is empty.
##
## See also:
## * `clear proc <#clear,Deque[T]>`_
## * `shrink proc <#shrink,Deque[T],int,int>`_
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addFirst(10*i)
assert $a == "[50, 40, 30, 20, 10]"
clear(a)
assert len(a) == 0
for el in mitems(deq): destroy(el)
deq.count = 0
deq.tail = deq.head
proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
## Remove `fromFirst` elements from the front of the deque and
## `fromLast` elements from the back. If the supplied number of
## elements exceeds the total number of elements in the deque,
## the deque will remain empty.
## `fromLast` elements from the back.
##
## Any user defined destructors
## If the supplied number of elements exceeds the total number of elements
## in the deque, the deque will remain empty.
##
## See also:
## * `clear proc <#clear,Deque[T]>`_
runnableExamples:
var a = initDeque[int]()
for i in 1 .. 5:
a.addFirst(10*i)
assert $a == "[50, 40, 30, 20, 10]"
a.shrink(fromFirst = 2, fromLast = 1)
assert $a == "[30, 20]"
if fromFirst + fromLast > deq.count:
clear(deq)
return
@@ -214,6 +456,8 @@ proc `$`*[T](deq: Deque[T]): string =
result.addQuoted(x)
result.add("]")
when isMainModule:
var deq = initDeque[int](1)
deq.addLast(4)

View File

@@ -9,9 +9,12 @@
## The ``intsets`` module implements an efficient int set implemented as a
## `sparse bit set`:idx:.
## **Note**: Since Nim currently does not allow the assignment operator to
## be overloaded, ``=`` for int sets performs some rather meaningless shallow
## copy; use ``assign`` to get a deep copy.
## **Note**: Currently the assignment operator ``=`` for ``intsets``
## performs some rather meaningless shallow copy. Since Nim currently does
## not allow the assignment operator to be overloaded, use ``assign`` to
## get a deep copy.
import
hashes, math
@@ -94,7 +97,7 @@ proc intSetPut(t: var IntSet, key: int): PTrunk =
t.data[h] = result
proc contains*(s: IntSet, key: int): bool =
## returns true iff `key` is in `s`.
## Returns true iff `key` is in `s`.
if s.elems <= s.a.len:
for i in 0..<s.elems:
if s.a[i] == key: return true
@@ -107,7 +110,7 @@ proc contains*(s: IntSet, key: int): bool =
result = false
iterator items*(s: IntSet): int {.inline.} =
## iterates over any included element of `s`.
## Iterates over any included element of `s`.
if s.elems <= s.a.len:
for i in 0..<s.elems:
yield s.a[i]
@@ -135,7 +138,7 @@ proc bitincl(s: var IntSet, key: int) {.inline.} =
`shl`(1, u and IntMask)
proc incl*(s: var IntSet, key: int) =
## includes an element `key` in `s`.
## Includes an element `key` in `s`.
if s.elems <= s.a.len:
for i in 0..<s.elems:
if s.a[i] == key: return
@@ -170,7 +173,7 @@ proc exclImpl(s: var IntSet, key: int) =
not `shl`(1, u and IntMask)
proc excl*(s: var IntSet, key: int) =
## excludes `key` from the set `s`.
## Excludes `key` from the set `s`.
exclImpl(s, key)
proc excl*(s: var IntSet, other: IntSet) =
@@ -178,14 +181,14 @@ proc excl*(s: var IntSet, other: IntSet) =
for item in other: excl(s, item)
proc missingOrExcl*(s: var IntSet, key: int) : bool =
## returns true if `s` does not contain `key`, otherwise
## Returns true if `s` does not contain `key`, otherwise
## `key` is removed from `s` and false is returned.
var count = s.elems
exclImpl(s, key)
result = count == s.elems
proc containsOrIncl*(s: var IntSet, key: int): bool =
## returns true if `s` contains `key`, otherwise `key` is included in `s`
## Returns true if `s` contains `key`, otherwise `key` is included in `s`
## and false is returned.
if s.elems <= s.a.len:
for i in 0..<s.elems:
@@ -206,23 +209,28 @@ proc containsOrIncl*(s: var IntSet, key: int): bool =
result = false
proc initIntSet*: IntSet =
## creates a new int set that is empty.
## Returns an empty IntSet. Example:
##
## .. code-block ::
## var a = initIntSet()
## a.incl(2)
#newSeq(result.data, InitIntSetSize)
#result.max = InitIntSetSize-1
when defined(nimNoNilSeqs):
result.data = @[]
else:
result.data = nil
result.max = 0
result.counter = 0
result.head = nil
result.elems = 0
# newSeq(result.data, InitIntSetSize)
# result.max = InitIntSetSize-1
result = IntSet(
elems: 0,
counter: 0,
max: 0,
head: nil,
data: when defined(nimNoNilSeqs): @[] else: nil)
# a: array[0..33, int] # profiling shows that 34 elements are enough
proc clear*(result: var IntSet) =
#setLen(result.data, InitIntSetSize)
#for i in 0..InitIntSetSize-1: result.data[i] = nil
#result.max = InitIntSetSize-1
## Clears the IntSet back to an empty state.
# setLen(result.data, InitIntSetSize)
# for i in 0..InitIntSetSize-1: result.data[i] = nil
# result.max = InitIntSetSize-1
when defined(nimNoNilSeqs):
result.data = @[]
else:
@@ -304,7 +312,7 @@ proc `-`*(s1, s2: IntSet): IntSet {.inline.} =
result = difference(s1, s2)
proc disjoint*(s1, s2: IntSet): bool =
## Returns true iff the sets `s1` and `s2` have no items in common.
## Returns true if the sets `s1` and `s2` have no items in common.
for item in s1:
if contains(s2, item):
return false
@@ -320,7 +328,7 @@ proc len*(s: IntSet): int {.inline.} =
inc(result)
proc card*(s: IntSet): int {.inline.} =
## alias for `len() <#len>` _.
## Alias for `len() <#len>` _.
result = s.len()
proc `<=`*(s1, s2: IntSet): bool =
@@ -349,12 +357,6 @@ proc `$`*(s: IntSet): string =
## The `$` operator for int sets.
dollarImpl()
proc empty*(s: IntSet): bool {.inline, deprecated.} =
## returns true if `s` is empty. This is safe to call even before
## the set has been initialized with `initIntSet`. Note this never
## worked reliably and so is deprecated.
result = s.counter == 0
when isMainModule:
import sequtils, algorithm

View File

@@ -7,34 +7,112 @@
# distribution, for details about the copyright.
#
## Implementation of singly and doubly linked lists. Because it makes no sense
## to do so, the 'next' and 'prev' pointers are not hidden from you and can
## be manipulated directly for efficiency.
## Implementation of:
## * `singly linked lists <#SinglyLinkedList>`_
## * `doubly linked lists <#DoublyLinkedList>`_
## * `singly linked rings <#SinglyLinkedRing>`_ (circular lists)
## * `doubly linked rings <#DoublyLinkedRing>`_ (circular lists)
##
##
## Basic Usage
## ===========
##
## Because it makes no sense to do otherwise, the `next` and `prev` pointers
## are not hidden from you and can be manipulated directly for efficiency.
##
## Lists
## -----
##
## .. code-block::
## import lists
##
## var
## l = initDoublyLinkedList[int]()
## a = newDoublyLinkedNode[int](3)
## b = newDoublyLinkedNode[int](7)
## c = newDoublyLinkedNode[int](9)
##
## l.append(a)
## l.append(b)
## l.prepend(c)
##
## assert a.next == b
## assert a.prev == c
## assert c.next == a
## assert c.next.next == b
## assert c.prev == nil
## assert b.next == nil
##
##
## Rings
## -----
##
## .. code-block::
## import lists
##
## var
## l = initSinglyLinkedRing[int]()
## a = newSinglyLinkedNode[int](3)
## b = newSinglyLinkedNode[int](7)
## c = newSinglyLinkedNode[int](9)
##
## l.append(a)
## l.append(b)
## l.prepend(c)
##
## assert c.next == a
## assert a.next == b
## assert c.next.next == b
## assert b.next == c
## assert c.next.next.next == c
##
## See also
## ========
##
## * `deques module <#deques.html>`_ for double-ended queues
## * `sharedlist module <#sharedlist.html>`_ for shared singly-linked lists
when not defined(nimhygiene):
{.pragma: dirty.}
type
DoublyLinkedNodeObj*[T] = object ## a node a doubly linked list consists of
DoublyLinkedNodeObj*[T] = object ## A node a doubly linked list consists of.
##
## It consists of a `value` field, and pointers to `next` and `prev`.
next*, prev*: ref DoublyLinkedNodeObj[T]
value*: T
DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T]
SinglyLinkedNodeObj*[T] = object ## a node a singly linked list consists of
SinglyLinkedNodeObj*[T] = object ## A node a singly linked list consists of.
##
## It consists of a `value` field, and a pointer to `next`.
next*: ref SinglyLinkedNodeObj[T]
value*: T
SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T]
SinglyLinkedList*[T] = object ## a singly linked list
SinglyLinkedList*[T] = object ## A singly linked list.
##
## Use `initSinglyLinkedList proc <#initSinglyLinkedList,>`_ to create
## a new empty list.
head*, tail*: SinglyLinkedNode[T]
DoublyLinkedList*[T] = object ## a doubly linked list
DoublyLinkedList*[T] = object ## A doubly linked list.
##
## Use `initDoublyLinkedList proc <#initDoublyLinkedList,>`_ to create
## a new empty list.
head*, tail*: DoublyLinkedNode[T]
SinglyLinkedRing*[T] = object ## a singly linked ring
SinglyLinkedRing*[T] = object ## A singly linked ring.
##
## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing,>`_ to create
## a new empty ring.
head*, tail*: SinglyLinkedNode[T]
DoublyLinkedRing*[T] = object ## a doubly linked ring
DoublyLinkedRing*[T] = object ## A doubly linked ring.
##
## Use `initDoublyLinkedRing proc <#initDoublyLinkedRing,>`_ to create
## a new empty ring.
head*: DoublyLinkedNode[T]
SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T]
@@ -46,28 +124,44 @@ type
SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T]
proc initSinglyLinkedList*[T](): SinglyLinkedList[T] =
## creates a new singly linked list that is empty.
## Creates a new singly linked list that is empty.
runnableExamples:
var a = initSinglyLinkedList[int]()
discard
proc initDoublyLinkedList*[T](): DoublyLinkedList[T] =
## creates a new doubly linked list that is empty.
## Creates a new doubly linked list that is empty.
runnableExamples:
var a = initDoublyLinkedList[int]()
discard
proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] =
## creates a new singly linked ring that is empty.
## Creates a new singly linked ring that is empty.
runnableExamples:
var a = initSinglyLinkedRing[int]()
discard
proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] =
## creates a new doubly linked ring that is empty.
## Creates a new doubly linked ring that is empty.
runnableExamples:
var a = initDoublyLinkedRing[int]()
discard
proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] =
## creates a new doubly linked node with the given `value`.
## Creates a new doubly linked node with the given `value`.
runnableExamples:
var n = newDoublyLinkedNode[int](5)
assert n.value == 5
new(result)
result.value = value
proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
## creates a new singly linked node with the given `value`.
## Creates a new singly linked node with the given `value`.
runnableExamples:
var n = newSinglyLinkedNode[int](5)
assert n.value == 5
new(result)
result.value = value
@@ -86,24 +180,100 @@ template itemsRingImpl() {.dirty.} =
if it == L.head: break
iterator items*[T](L: SomeLinkedList[T]): T =
## yields every value of `L`.
## Yields every value of `L`.
##
## See also:
## * `mitems iterator <#mitems.i,SomeLinkedList[T]>`_
## * `nodes iterator <#nodes.i,SomeLinkedList[T]>`_
##
## **Examples:**
##
## .. code-block::
## var a = initSinglyLinkedList[int]()
## for i in 1 .. 3:
## a.append(10*i)
##
## for x in a: # the same as: for x in items(a):
## echo x
##
## # 10
## # 20
## # 30
itemsListImpl()
iterator items*[T](L: SomeLinkedRing[T]): T =
## yields every value of `L`.
## Yields every value of `L`.
##
## See also:
## * `mitems iterator <#mitems.i,SomeLinkedRing[T]>`_
## * `nodes iterator <#nodes.i,SomeLinkedRing[T]>`_
##
## **Examples:**
##
## .. code-block::
## var a = initSinglyLinkedRing[int]()
## for i in 1 .. 3:
## a.append(10*i)
##
## for x in a: # the same as: for x in items(a):
## echo x
##
## # 10
## # 20
## # 30
itemsRingImpl()
iterator mitems*[T](L: var SomeLinkedList[T]): var T =
## yields every value of `L` so that you can modify it.
## Yields every value of `L` so that you can modify it.
##
## See also:
## * `items iterator <#items.i,SomeLinkedList[T]>`_
## * `nodes iterator <#nodes.i,SomeLinkedList[T]>`_
runnableExamples:
var a = initSinglyLinkedList[int]()
for i in 1 .. 5:
a.append(10*i)
assert $a == "[10, 20, 30, 40, 50]"
for x in mitems(a):
x = 5*x - 1
assert $a == "[49, 99, 149, 199, 249]"
itemsListImpl()
iterator mitems*[T](L: var SomeLinkedRing[T]): var T =
## yields every value of `L` so that you can modify it.
## Yields every value of `L` so that you can modify it.
##
## See also:
## * `items iterator <#items.i,SomeLinkedRing[T]>`_
## * `nodes iterator <#nodes.i,SomeLinkedRing[T]>`_
runnableExamples:
var a = initSinglyLinkedRing[int]()
for i in 1 .. 5:
a.append(10*i)
assert $a == "[10, 20, 30, 40, 50]"
for x in mitems(a):
x = 5*x - 1
assert $a == "[49, 99, 149, 199, 249]"
itemsRingImpl()
iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the
## Iterates over every node of `x`. Removing the current node from the
## list during traversal is supported.
##
## See also:
## * `items iterator <#items.i,SomeLinkedList[T]>`_
## * `mitems iterator <#mitems.i,SomeLinkedList[T]>`_
runnableExamples:
var a = initDoublyLinkedList[int]()
for i in 1 .. 5:
a.append(10*i)
assert $a == "[10, 20, 30, 40, 50]"
for x in nodes(a):
if x.value == 30:
a.remove(x)
else:
x.value = 5*x.value - 1
assert $a == "[49, 99, 199, 249]"
var it = L.head
while it != nil:
var nxt = it.next
@@ -111,8 +281,24 @@ iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
it = nxt
iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
## iterates over every node of `x`. Removing the current node from the
## Iterates over every node of `x`. Removing the current node from the
## list during traversal is supported.
##
## See also:
## * `items iterator <#items.i,SomeLinkedRing[T]>`_
## * `mitems iterator <#mitems.i,SomeLinkedRing[T]>`_
runnableExamples:
var a = initDoublyLinkedRing[int]()
for i in 1 .. 5:
a.append(10*i)
assert $a == "[10, 20, 30, 40, 50]"
for x in nodes(a):
if x.value == 30:
a.remove(x)
else:
x.value = 5*x.value - 1
assert $a == "[49, 99, 199, 249]"
var it = L.head
if it != nil:
while true:
@@ -122,7 +308,7 @@ iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
if it == L.head: break
proc `$`*[T](L: SomeLinkedCollection[T]): string =
## turns a list into its string representation.
## Turns a list into its string representation for logging and printing.
result = "["
for x in nodes(L):
if result.len > 1: result.add(", ")
@@ -130,19 +316,54 @@ proc `$`*[T](L: SomeLinkedCollection[T]): string =
result.add("]")
proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] =
## searches in the list for a value. Returns nil if the value does not
## Searches in the list for a value. Returns `nil` if the value does not
## exist.
##
## See also:
## * `contains proc <#contains,SomeLinkedCollection[T],T>`_
runnableExamples:
var a = initSinglyLinkedList[int]()
a.append(9)
a.append(8)
assert a.find(9).value == 9
assert a.find(1) == nil
for x in nodes(L):
if x.value == value: return x
proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} =
## searches in the list for a value. Returns false if the value does not
## exist, true otherwise.
## Searches in the list for a value. Returns `false` if the value does not
## exist, `true` otherwise.
##
## See also:
## * `find proc <#find,SomeLinkedCollection[T],T>`_
runnableExamples:
var a = initSinglyLinkedList[int]()
a.append(9)
a.append(8)
assert a.contains(9)
assert 8 in a
assert(not a.contains(1))
assert 2 notin a
result = find(L, value) != nil
proc append*[T](L: var SinglyLinkedList[T],
n: SinglyLinkedNode[T]) {.inline.} =
## appends a node `n` to `L`. Efficiency: O(1).
## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
## for prepending a node
## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value
runnableExamples:
var
a = initSinglyLinkedList[int]()
n = newSinglyLinkedNode[int](9)
a.append(n)
assert a.contains(9)
n.next = nil
if L.tail != nil:
assert(L.tail.next == nil)
@@ -151,22 +372,75 @@ proc append*[T](L: var SinglyLinkedList[T],
if L.head == nil: L.head = n
proc append*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
## appends a value to `L`. Efficiency: O(1).
## Appends (adds to the end) a value to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
## for prepending a node
## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value
runnableExamples:
var a = initSinglyLinkedList[int]()
a.append(9)
a.append(8)
assert a.contains(9)
append(L, newSinglyLinkedNode(value))
proc prepend*[T](L: var SinglyLinkedList[T],
n: SinglyLinkedNode[T]) {.inline.} =
## prepends a node to `L`. Efficiency: O(1).
## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
## for appending a node
## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value
runnableExamples:
var
a = initSinglyLinkedList[int]()
n = newSinglyLinkedNode[int](9)
a.prepend(n)
assert a.contains(9)
n.next = L.head
L.head = n
if L.tail == nil: L.tail = n
proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} =
## prepends a node to `L`. Efficiency: O(1).
## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
## for appending a node
## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value
## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_
## for prepending a node
runnableExamples:
var a = initSinglyLinkedList[int]()
a.prepend(9)
a.prepend(8)
assert a.contains(9)
prepend(L, newSinglyLinkedNode(value))
proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
## appends a node `n` to `L`. Efficiency: O(1).
## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value
## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
## for prepending a node
## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value
## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
## for removing a node
runnableExamples:
var
a = initDoublyLinkedList[int]()
n = newDoublyLinkedNode[int](9)
a.append(n)
assert a.contains(9)
n.next = nil
n.prev = L.tail
if L.tail != nil:
@@ -176,11 +450,40 @@ proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
if L.head == nil: L.head = n
proc append*[T](L: var DoublyLinkedList[T], value: T) =
## appends a value to `L`. Efficiency: O(1).
## Appends (adds to the end) a value to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
## for appending a node
## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
## for prepending a node
## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value
## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
## for removing a node
runnableExamples:
var a = initDoublyLinkedList[int]()
a.append(9)
a.append(8)
assert a.contains(9)
append(L, newDoublyLinkedNode(value))
proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
## prepends a node `n` to `L`. Efficiency: O(1).
## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
## for appending a node
## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value
## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value
## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
## for removing a node
runnableExamples:
var
a = initDoublyLinkedList[int]()
n = newDoublyLinkedNode[int](9)
a.prepend(n)
assert a.contains(9)
n.prev = nil
n.next = L.head
if L.head != nil:
@@ -190,18 +493,56 @@ proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
if L.tail == nil: L.tail = n
proc prepend*[T](L: var DoublyLinkedList[T], value: T) =
## prepends a value to `L`. Efficiency: O(1).
## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
## for appending a node
## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value
## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
## for prepending a node
## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_
## for removing a node
runnableExamples:
var a = initDoublyLinkedList[int]()
a.prepend(9)
a.prepend(8)
assert a.contains(9)
prepend(L, newDoublyLinkedNode(value))
proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) =
## removes `n` from `L`. Efficiency: O(1).
## Removes a node `n` from `L`. Efficiency: O(1).
runnableExamples:
var
a = initDoublyLinkedList[int]()
n = newDoublyLinkedNode[int](5)
a.append(n)
assert 5 in a
a.remove(n)
assert 5 notin a
if n == L.tail: L.tail = n.prev
if n == L.head: L.head = n.next
if n.next != nil: n.next.prev = n.prev
if n.prev != nil: n.prev.next = n.next
proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
## appends a node `n` to `L`. Efficiency: O(1).
## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value
## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
## for prepending a node
## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value
runnableExamples:
var
a = initSinglyLinkedRing[int]()
n = newSinglyLinkedNode[int](9)
a.append(n)
assert a.contains(9)
if L.head != nil:
n.next = L.head
assert(L.tail != nil)
@@ -213,11 +554,36 @@ proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
L.tail = n
proc append*[T](L: var SinglyLinkedRing[T], value: T) =
## appends a value to `L`. Efficiency: O(1).
## Appends (adds to the end) a value to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
## for appending a node
## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
## for prepending a node
## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value
runnableExamples:
var a = initSinglyLinkedRing[int]()
a.append(9)
a.append(8)
assert a.contains(9)
append(L, newSinglyLinkedNode(value))
proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
## prepends a node `n` to `L`. Efficiency: O(1).
## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
## for appending a node
## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value
## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value
runnableExamples:
var
a = initSinglyLinkedRing[int]()
n = newSinglyLinkedNode[int](9)
a.prepend(n)
assert a.contains(9)
if L.head != nil:
n.next = L.head
assert(L.tail != nil)
@@ -228,11 +594,40 @@ proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
L.head = n
proc prepend*[T](L: var SinglyLinkedRing[T], value: T) =
## prepends a value to `L`. Efficiency: O(1).
## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
## for appending a node
## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value
## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_
## for prepending a node
runnableExamples:
var a = initSinglyLinkedRing[int]()
a.prepend(9)
a.prepend(8)
assert a.contains(9)
prepend(L, newSinglyLinkedNode(value))
proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
## appends a node `n` to `L`. Efficiency: O(1).
## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value
## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
## for prepending a node
## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value
## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
## for removing a node
runnableExamples:
var
a = initDoublyLinkedRing[int]()
n = newDoublyLinkedNode[int](9)
a.append(n)
assert a.contains(9)
if L.head != nil:
n.next = L.head
n.prev = L.head.prev
@@ -244,11 +639,40 @@ proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
L.head = n
proc append*[T](L: var DoublyLinkedRing[T], value: T) =
## appends a value to `L`. Efficiency: O(1).
## Appends (adds to the end) a value to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
## for appending a node
## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
## for prepending a node
## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value
## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
## for removing a node
runnableExamples:
var a = initDoublyLinkedRing[int]()
a.append(9)
a.append(8)
assert a.contains(9)
append(L, newDoublyLinkedNode(value))
proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
## prepends a node `n` to `L`. Efficiency: O(1).
## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
## for appending a node
## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value
## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value
## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
## for removing a node
runnableExamples:
var
a = initDoublyLinkedRing[int]()
n = newDoublyLinkedNode[int](9)
a.prepend(n)
assert a.contains(9)
if L.head != nil:
n.next = L.head
n.prev = L.head.prev
@@ -260,11 +684,34 @@ proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
L.head = n
proc prepend*[T](L: var DoublyLinkedRing[T], value: T) =
## prepends a value to `L`. Efficiency: O(1).
## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1).
##
## See also:
## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
## for appending a node
## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value
## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
## for prepending a node
## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_
## for removing a node
runnableExamples:
var a = initDoublyLinkedRing[int]()
a.prepend(9)
a.prepend(8)
assert a.contains(9)
prepend(L, newDoublyLinkedNode(value))
proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
## removes `n` from `L`. Efficiency: O(1).
## Removes `n` from `L`. Efficiency: O(1).
runnableExamples:
var
a = initDoublyLinkedRing[int]()
n = newDoublyLinkedNode[int](5)
a.append(n)
assert 5 in a
a.remove(n)
assert 5 notin a
n.next.prev = n.prev
n.prev.next = n.next
if n == L.head:

View File

@@ -1,257 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Implementation of a `queue`:idx:. The underlying implementation uses a ``seq``.
##
## None of the procs that get an individual value from the queue can be used
## on an empty queue.
## If compiled with `boundChecks` option, those procs will raise an `IndexError`
## on such access. This should not be relied upon, as `-d:release` will
## disable those checks and may return garbage or crash the program.
##
## As such, a check to see if the queue is empty is needed before any
## access, unless your program logic guarantees it indirectly.
##
## .. code-block:: Nim
## proc foo(a, b: Positive) = # assume random positive values for `a` and `b`
## var q = initQueue[int]() # initializes the object
## for i in 1 ..< a: q.add i # populates the queue
##
## if b < q.len: # checking before indexed access
## echo "The element at index position ", b, " is ", q[b]
##
## # The following two lines don't need any checking on access due to the
## # logic of the program, but that would not be the case if `a` could be 0.
## assert q.front == 1
## assert q.back == a
##
## while q.len > 0: # checking if the queue is empty
## echo q.pop()
##
## Note: For inter thread communication use
## a `Channel <channels.html>`_ instead.
import math
{.warning: "`queues` module is deprecated - use `deques` instead".}
type
Queue* {.deprecated.} [T] = object ## A queue.
data: seq[T]
rd, wr, count, mask: int
proc initQueue*[T](initialSize: int = 4): Queue[T] =
## Create a new queue.
## Optionally, the initial capacity can be reserved via `initialSize` as a
## performance optimization. The length of a newly created queue will still
## be 0.
##
## `initialSize` needs to be a power of two. If you need to accept runtime
## values for this you could use the ``nextPowerOfTwo`` proc from the
## `math <math.html>`_ module.
assert isPowerOfTwo(initialSize)
result.mask = initialSize-1
newSeq(result.data, initialSize)
proc len*[T](q: Queue[T]): int {.inline.}=
## Return the number of elements of `q`.
result = q.count
template emptyCheck(q) =
# Bounds check for the regular queue access.
when compileOption("boundChecks"):
if unlikely(q.count < 1):
raise newException(IndexError, "Empty queue.")
template xBoundsCheck(q, i) =
# Bounds check for the array like accesses.
when compileOption("boundChecks"): # d:release should disable this.
if unlikely(i >= q.count): # x < q.low is taken care by the Natural parameter
raise newException(IndexError,
"Out of bounds: " & $i & " > " & $(q.count - 1))
proc front*[T](q: Queue[T]): T {.inline.}=
## Return the oldest element of `q`. Equivalent to `q.pop()` but does not
## remove it from the queue.
emptyCheck(q)
result = q.data[q.rd]
proc back*[T](q: Queue[T]): T {.inline.} =
## Return the newest element of `q` but does not remove it from the queue.
emptyCheck(q)
result = q.data[q.wr - 1 and q.mask]
proc `[]`*[T](q: Queue[T], i: Natural) : T {.inline.} =
## Access the i-th element of `q` by order of insertion.
## q[0] is the oldest (the next one q.pop() will extract),
## q[^1] is the newest (last one added to the queue).
xBoundsCheck(q, i)
return q.data[q.rd + i and q.mask]
proc `[]`*[T](q: var Queue[T], i: Natural): var T {.inline.} =
## Access the i-th element of `q` and returns a mutable
## reference to it.
xBoundsCheck(q, i)
return q.data[q.rd + i and q.mask]
proc `[]=`* [T] (q: var Queue[T], i: Natural, val : T) {.inline.} =
## Change the i-th element of `q`.
xBoundsCheck(q, i)
q.data[q.rd + i and q.mask] = val
iterator items*[T](q: Queue[T]): T =
## Yield every element of `q`.
var i = q.rd
for c in 0 ..< q.count:
yield q.data[i]
i = (i + 1) and q.mask
iterator mitems*[T](q: var Queue[T]): var T =
## Yield every element of `q`.
var i = q.rd
for c in 0 ..< q.count:
yield q.data[i]
i = (i + 1) and q.mask
iterator pairs*[T](q: Queue[T]): tuple[key: int, val: T] =
## Yield every (position, value) of `q`.
var i = q.rd
for c in 0 ..< q.count:
yield (c, q.data[i])
i = (i + 1) and q.mask
proc contains*[T](q: Queue[T], item: T): bool {.inline.} =
## Return true if `item` is in `q` or false if not found. Usually used
## via the ``in`` operator. It is the equivalent of ``q.find(item) >= 0``.
##
## .. code-block:: Nim
## if x in q:
## assert q.contains x
for e in q:
if e == item: return true
return false
proc add*[T](q: var Queue[T], item: T) =
## Add an `item` to the end of the queue `q`.
var cap = q.mask+1
if unlikely(q.count >= cap):
var n = newSeq[T](cap*2)
for i, x in pairs(q): # don't use copyMem because the GC and because it's slower.
shallowCopy(n[i], x)
shallowCopy(q.data, n)
q.mask = cap*2 - 1
q.wr = q.count
q.rd = 0
inc q.count
q.data[q.wr] = item
q.wr = (q.wr + 1) and q.mask
template default[T](t: typedesc[T]): T =
var v: T
v
proc pop*[T](q: var Queue[T]): T {.inline, discardable.} =
## Remove and returns the first (oldest) element of the queue `q`.
emptyCheck(q)
dec q.count
result = q.data[q.rd]
q.data[q.rd] = default(type(result))
q.rd = (q.rd + 1) and q.mask
proc enqueue*[T](q: var Queue[T], item: T) =
## Alias for the ``add`` operation.
q.add(item)
proc dequeue*[T](q: var Queue[T]): T =
## Alias for the ``pop`` operation.
q.pop()
proc `$`*[T](q: Queue[T]): string =
## Turn a queue into its string representation.
result = "["
for x in items(q): # Don't remove the items here for reasons that don't fit in this margin.
if result.len > 1: result.add(", ")
result.add($x)
result.add("]")
when isMainModule:
var q = initQueue[int](1)
q.add(123)
q.add(9)
q.enqueue(4)
var first = q.dequeue()
q.add(56)
q.add(6)
var second = q.pop()
q.add(789)
assert first == 123
assert second == 9
assert($q == "[4, 56, 6, 789]")
assert q[0] == q.front and q.front == 4
q[0] = 42
q[q.len - 1] = 7
assert 6 in q and 789 notin q
assert q.find(6) >= 0
assert q.find(789) < 0
for i in -2 .. 10:
if i in q:
assert q.contains(i) and q.find(i) >= 0
else:
assert(not q.contains(i) and q.find(i) < 0)
when compileOption("boundChecks"):
try:
echo q[99]
assert false
except IndexError:
discard
try:
assert q.len == 4
for i in 0 ..< 5: q.pop()
assert false
except IndexError:
discard
# grabs some types of resize error.
q = initQueue[int]()
for i in 1 .. 4: q.add i
q.pop()
q.pop()
for i in 5 .. 8: q.add i
assert $q == "[3, 4, 5, 6, 7, 8]"
# Similar to proc from the documentation example
proc foo(a, b: Positive) = # assume random positive values for `a` and `b`.
var q = initQueue[int]()
assert q.len == 0
for i in 1 .. a: q.add i
if b < q.len: # checking before indexed access.
assert q[b] == b + 1
# The following two lines don't need any checking on access due to the logic
# of the program, but that would not be the case if `a` could be 0.
assert q.front == 1
assert q.back == a
while q.len > 0: # checking if the queue is empty
assert q.pop() > 0
#foo(0,0)
foo(8,5)
foo(10,9)
foo(1,1)
foo(2,1)
foo(1,5)
foo(3,2)

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