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
commit a6de0274ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
285 changed files with 10804 additions and 6931 deletions

View file

@ -1,37 +1,32 @@
--- ---
name: Bug report 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. --> <!-- Think about the title, twice. -->
<!-- Summarize the Problem here, keep it simple. --> <!-- Summarize the problem here, keep it short and simple. -->
<!-- e.g. `echo` outputs the wrong string. --> Function `echo` outputs the wrong string.
### Example ### Example
<!-- This should be a source code block. <!-- Paste your example in the code-block below. -->
```nim ```nim
echo "Hello World!" echo "Hello World!"
``` ```
-->
### Current Output ### Current Output
<!--
``` ```
Hola mundo! Hola mundo!
``` ```
-->
### Expected Output ### Expected Output
<!-- <!-- What should be the correct output? -->
``` ```
Hello World! Hello World!
``` ```
-->
### Possible Solution ### Possible Solution
@ -40,6 +35,11 @@ Hello World!
### Additional Information ### Additional Information
<!--- For Example: <!--- For Example:
A link to a project where the issue is relevant. * Your Nim version (output of `nim -v`).
A link to a related issue or discussion. * 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 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 ### Summary
<!--- Short summary of your proposed feature --> <!--- Short summary of your proposed feature -->
@ -18,6 +27,6 @@ about: You want to suggest a new feature? Use this template.
### Additional Information ### Additional Information
<!--- For Example: <!--- For Example:
A link to a project where the issue is relevant. * A link to a project where the issue is relevant.
A link to a related issue or discussion. * A link to a related issue or discussion.
--> -->

6
.gitignore vendored
View file

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

View file

@ -14,8 +14,9 @@ matrix:
- os: osx - os: osx
env: NIM_COMPILE_TO_CPP=true env: NIM_COMPILE_TO_CPP=true
allow_failures: # To allow failures for a failing configuration, use something like:
- env: NIM_COMPILE_TO_CPP=true # allow_failures:
# - env: NIM_COMPILE_TO_CPP=true
# - os: osx # - os: osx
addons: addons:
@ -40,26 +41,11 @@ before_script:
- sh build.sh - sh build.sh
- cd .. - cd ..
- export PATH=$(pwd)/bin${PATH:+:$PATH} - export PATH=$(pwd)/bin${PATH:+:$PATH}
- echo PATH:${PATH}
script: script:
- nim c koch - nim c koch
- env NIM_COMPILE_TO_CPP=false ./koch boot - ./koch runCI
- ./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
before_deploy: before_deploy:
# Make https://nim-lang.github.io/Nim work the same as https://nim-lang.github.io/Nim/overview.html # 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: build_script:
- bin\nim c koch - bin\nim c koch
- koch boot -d:release - koch runCI
- 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
deploy: off deploy: off

View file

@ -26,6 +26,9 @@ build_nim_csources(){
[ -f $nim_csources ] || echo_run 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 boot -d:release
echo_run ./koch tools # Compile Nimble and other tools. 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 ### Changes affecting backwards compatibility
@ -23,13 +24,19 @@
- The undocumented ``#? strongSpaces`` parsing mode has been removed. - The undocumented ``#? strongSpaces`` parsing mode has been removed.
- The `not` operator is now always a unary operator, this means that code like - The `not` operator is now always a unary operator, this means that code like
``assert not isFalse(3)`` compiles. ``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 #### Breaking changes in the standard library
- `osproc.execProcess` now also takes a `workingDir` parameter. - `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 - nre's `RegexMatch.{captureBounds,captures}[]` no longer return `Option` or
`nil`/`""`, respectivly. Use the newly added `n in p.captures` method to `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 `IndexError` for un-named captures. This is consistant with
`RegexMatch.{captureBounds,captures}[]`. `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 #### Breaking changes in the compiler
- The compiler now implements the "generic symbol prepass" for `when` statements - 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. - ``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 ### Library additions
@ -87,11 +109,18 @@ proc enumToString*(enums: openArray[enum]): string =
is instantiation of generic proc symbol. is instantiation of generic proc symbol.
- Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc. - Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc.
- There is a new stdlib module `std/diff` to compute the famous "diff" - There is a new stdlib module `std/diff` to compute the famous "diff"
of two texts by line. of two texts by line.
- Added `os.relativePath`. - 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 ### Library changes
- The string output of `macros.lispRepr` proc has been tweaked - 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 (default value: true) that can be set to `false` for better Posix
interoperability. (Bug #9619.) interoperability. (Bug #9619.)
- `os.joinPath` and `os.normalizePath` handle edge cases like ``"a/b/../../.."``
differently.
- `securehash` is moved to `lib/deprecated`
### Language additions ### 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 - There is a new pragma block `noSideEffect` that works like
the `gcsafe` pragma block. the `gcsafe` pragma block.
- added os.getCurrentProcessId() - added os.getCurrentProcessId()
- User defined pragmas are now allowed in the pragma blocks - 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 pragmas for further analysis by macros
- Custom pragmas are now supported for `var` and `let` symbols.
### Language changes ### 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, it's more recognizable and allows tools like github to recognize it as Nim,
see [#9647](https://github.com/nim-lang/Nim/issues/9647). see [#9647](https://github.com/nim-lang/Nim/issues/9647).
The previous extension will continue to work. 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 ### Tool changes
- `jsondoc` now include a `moduleDescription` field with the module - `jsondoc` now include a `moduleDescription` field with the module
description. `jsondoc0` shows comments as it's own objects as shown in the description. `jsondoc0` shows comments as it's own objects as shown in the
documentation. 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 ### Compiler changes
- The deprecated `fmod` proc is now unavailable on the VM'. - The deprecated `fmod` proc is now unavailable on the VM'.
### Bugfixes ### Bugfixes

View file

@ -4,6 +4,6 @@ author = "Andreas Rumpf"
description = "Compiler package providing the compiler sources as a library." description = "Compiler package providing the compiler sources as a library."
license = "MIT" license = "MIT"
installDirs = @["compiler"] installDirs = @["compiler", "nimsuggest"]
requires "nim >= 0.14.0" 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: when debugIds:
registerId(result) 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 = proc isMetaType*(t: PType): bool =
return t.kind in tyMetaTypes or return t.kind in tyMetaTypes or
(t.kind == tyStatic and t.n == nil) 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) addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), a.storage)
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
var b: TLoc var b: TLoc
case a.t.kind case skipTypes(a.t, abstractVarRange).kind
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage) putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
of tyString, tySequence: of tyString, tySequence:

View file

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

View file

@ -1087,30 +1087,24 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
result = getCopyright(conf, cfile) result = getCopyright(conf, cfile)
addIntTypes(result, conf) 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) = 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 const
# The use of a volatile function pointer to call Pre/NimMainInner # The use of a volatile function pointer to call Pre/NimMainInner
# prevents inlining of the NimMainInner function and dependent # prevents inlining of the NimMainInner function and dependent
# functions, which might otherwise merge their stack frames. # functions, which might otherwise merge their stack frames.
PreMainBody = PreMainBody =
"void PreMainInner(void) {$N" & "void PreMainInner(void) {$N" &
"\tsystemInit000();$N" &
"$1" &
"$2" & "$2" &
"$3" & "$3" &
"}$N$N" & "}$N$N" &
"void PreMain(void) {$N" & "void PreMain(void) {$N" &
"\tvoid (*volatile inner)(void);$N" & "\tvoid (*volatile inner)(void);$N" &
"\tsystemDatInit000();$N" &
"\tinner = PreMainInner;$N" & "\tinner = PreMainInner;$N" &
"$4$5" & "$1" &
"\t(*inner)();$N" & "\t(*inner)();$N" &
"}$N$N" "}$N$N"
@ -1217,21 +1211,17 @@ proc genMainProc(m: BModule) =
else: else:
nimMain = PosixNimMain nimMain = PosixNimMain
otherMain = PosixCMain otherMain = PosixCMain
if m.g.breakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
if optEndb in m.config.options: 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 = let initStackBottomCall =
if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N") else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
inc(m.labels) inc(m.labels)
appcg(m, m.s[cfsProcs], PreMainBody, [ appcg(m, m.s[cfsProcs], PreMainBody, [
m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit, 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])
appcg(m, m.s[cfsProcs], nimMain, appcg(m, m.s[cfsProcs], nimMain,
[m.g.mainModInit, initStackBottomCall, rope(m.labels)]) [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 getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000")
proc registerModuleToMain(g: BModuleList; m: PSym) =
var proc registerModuleToMain(g: BModuleList; m: BModule) =
init = m.getInitName if m.s[cfsDatInitProc].len > 0:
datInit = m.getDatInitName let datInit = m.module.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])
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
if sfSystemModule notin m.flags:
addf(g.mainDatInit, "\t$1();$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] let initCall = "\t$1();$N" % [init]
if sfMainModule in m.flags: if sfMainModule in m.module.flags:
add(g.mainModInit, initCall) add(g.mainModInit, initCall)
elif sfSystemModule in m.module.flags:
add(g.mainDatInit, initCall) # systemInit must called right after systemDatInit if any
else: else:
add(g.otherModsInit, initCall) 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) = 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] var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" % [initname]
if m.typeNodes > 0: if m.typeNodes > 0:
appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n", 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 # Keep a bogus frame in case the code needs one
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N") 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, genSectionStart(cpsLocals, m.config))
add(prc, m.preInitProc.s(cpsLocals)) add(prc, m.initProc.s(cpsLocals))
add(prc, genSectionEnd(cpsLocals, m.config)) 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, genSectionStart(cpsInit, m.config))
add(prc, m.preInitProc.s(cpsInit)) add(prc, m.initProc.s(cpsInit))
add(prc, genSectionEnd(cpsInit, m.config)) add(prc, genSectionEnd(cpsInit, m.config))
add(prc, genSectionStart(cpsStmts, 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)) 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)) if m.initProc.gcFrameId > 0:
add(prc, m.initProc.s(cpsLocals)) moduleInitRequired = true
add(prc, genSectionEnd(cpsLocals, m.config)) add(prc, deinitGCFrame(m.initProc))
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))
addf(prc, "}$N$N", []) addf(prc, "}$N$N", [])
# we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because # 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 # that would lead to a *nesting* of merge sections which the merger does
# not support. So we add it to another special section: ``cfsInitProc`` # not support. So we add it to another special section: ``cfsInitProc``
add(m.s[cfsInitProc], prc)
for i, el in pairs(m.extensionLoaders): for i, el in pairs(m.extensionLoaders):
if el != nil: if el != nil:
let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" % let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" %
[(i.ord - '0'.ord).rope, el] [(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 = proc genModule(m: BModule, cfile: Cfile): Rope =
var moduleIsEmpty = true
result = getFileHeader(m.config, cfile) result = getFileHeader(m.config, cfile)
result.add(genMergeInfo(m)) result.add(genMergeInfo(m))
if m.config.cppCustomNamespace.len > 0:
result.add openNamespaceNim(m.config.cppCustomNamespace)
generateThreadLocalStorage(m) generateThreadLocalStorage(m)
generateHeaders(m) generateHeaders(m)
for i in countup(cfsHeaders, cfsProcs): add(result, genSectionStart(cfsHeaders, m.config))
add(result, genSectionStart(i, m.config)) add(result, m.s[cfsHeaders])
add(result, m.s[i]) add(result, genSectionEnd(cfsHeaders, m.config))
add(result, genSectionEnd(i, m.config))
if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders: for i in countup(cfsForwardTypes, cfsProcs):
result.add openNamespaceNim(m.config.cppCustomNamespace) if m.s[i].len > 0:
add(result, m.s[cfsInitProc]) moduleIsEmpty = false
if m.config.cppCustomNamespace.len > 0: result.add closeNamespaceNim() 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 = proc newPreInitProc(m: BModule): BProc =
result = newProc(nil, m) result = newProc(nil, m)
@ -1522,27 +1584,30 @@ proc writeModule(m: BModule, pending: bool) =
finishTypeDescriptions(m) finishTypeDescriptions(m)
if sfMainModule in m.module.flags: if sfMainModule in m.module.flags:
# generate main file: # generate main file:
genMainProc(m)
add(m.s[cfsProcHeaders], m.g.mainModProcs) add(m.s[cfsProcHeaders], m.g.mainModProcs)
generateThreadVarsSize(m) generateThreadVarsSize(m)
var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {}) var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
var code = genModule(m, cf) var code = genModule(m, cf)
when hasTinyCBackend: if code != nil:
if conf.cmd == cmdRun: when hasTinyCBackend:
tccgen.compileCCode($code) if conf.cmd == cmdRun:
return tccgen.compileCCode($code)
return
if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached} if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf) addFileToCompile(m.config, cf)
elif pending and mergeRequired(m) and sfMainModule notin m.module.flags: 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: {}) let cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
mergeFiles(cfile, m) mergeFiles(cfile, m)
genInitCode(m) genInitCode(m)
finishTypeDescriptions(m) finishTypeDescriptions(m)
var code = genModule(m, cf) var code = genModule(m, cf)
if not writeRope(code, cfile): if code != nil:
rawMessage(m.config, errCannotOpenFile, cfile.string) if not writeRope(code, cfile):
addFileToCompile(m.config, cf) rawMessage(m.config, errCannotOpenFile, cfile.string)
addFileToCompile(m.config, cf)
else: else:
# Consider: first compilation compiles ``system.nim`` and produces # Consider: first compilation compiles ``system.nim`` and produces
# ``system.c`` but then compilation fails due to an error. This means # ``system.c`` but then compilation fails due to an error. This means
@ -1560,13 +1625,16 @@ proc updateCachedModule(m: BModule) =
mergeFiles(cfile, m) mergeFiles(cfile, m)
genInitCode(m) genInitCode(m)
finishTypeDescriptions(m) finishTypeDescriptions(m)
var code = genModule(m, cf) var code = genModule(m, cf)
if not writeRope(code, cfile): if code != nil:
rawMessage(m.config, errCannotOpenFile, cfile.string) if not writeRope(code, cfile):
rawMessage(m.config, errCannotOpenFile, cfile.string)
addFileToCompile(m.config, cf)
else: else:
if sfMainModule notin m.module.flags:
genMainProc(m)
cf.flags = {CfileFlag.Cached} cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf) addFileToCompile(m.config, cf)
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
result = n result = n
@ -1579,15 +1647,25 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
if n != nil: if n != nil:
m.initProc.options = initProcOptions(m) m.initProc.options = initProcOptions(m)
genStmts(m.initProc, n) genStmts(m.initProc, n)
# cached modules need to registered too:
registerModuleToMain(m.g, m.module)
if sfMainModule in m.module.flags: 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: if m.g.forwardedProcs.len == 0:
incl m.flags, objHasKidsValid incl m.flags, objHasKidsValid
let disp = generateMethodDispatchers(graph) let disp = generateMethodDispatchers(graph)
for x in disp: genProcAux(m, x.sym) for x in disp: genProcAux(m, x.sym)
genMainProc(m)
m.g.modules_closed.add m
proc genForwardedProcs(g: BModuleList) = proc genForwardedProcs(g: BModuleList) =
# Forward declared proc:s lack bodies when first encountered, so they're given # 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 cfsData, # section for C constant data
cfsProcs, # section for C procs that are not inline cfsProcs, # section for C procs that are not inline
cfsInitProc, # section for the C init proc cfsInitProc, # section for the C init proc
cfsDatInitProc, # section for the C datInit proc
cfsTypeInit1, # section 1 for declarations of type information cfsTypeInit1, # section 1 for declarations of type information
cfsTypeInit2, # section 2 for init of type information cfsTypeInit2, # section 2 for init of type information
cfsTypeInit3, # section 3 for init of type information cfsTypeInit3, # section 3 for init of type information
@ -112,6 +113,7 @@ type
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
mapping*: Rope # the generated mapping file (if requested) mapping*: Rope # the generated mapping file (if requested)
modules*: seq[BModule] # list of all compiled modules 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 forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
generatedHeader*: BModule generatedHeader*: BModule
breakPointId*: int breakPointId*: int
@ -191,8 +193,6 @@ proc newModuleList*(g: ModuleGraph): BModuleList =
graph: g, nimtvDeclared: initIntSet()) graph: g, nimtvDeclared: initIntSet())
iterator cgenModules*(g: BModuleList): BModule = iterator cgenModules*(g: BModuleList): BModule =
for m in g.modules: for m in g.modules_closed:
# ultimately, we are iterating over the file ids here. # iterate modules in the order they were closed
# some "files" won't have an associated cgen module (like stdin) yield m
# and we must skip over them.
if m != nil: yield m

View file

@ -48,18 +48,10 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi
if self.suggestMode: if self.suggestMode:
conf.command = "nimsuggest" conf.command = "nimsuggest"
# These defines/options should not be enabled while processing nimscript template runNimScriptIfExists(path: AbsoluteFile) =
# bug #4446, #9420, #8991, #9589, #9153 let p = path # eval once
undefSymbol(conf.symbols, "profiler") if fileExists(p):
undefSymbol(conf.symbols, "memProfiler") runNimScript(cache, p, freshDefines = false, conf)
undefSymbol(conf.symbols, "nodejs")
# bug #9120
conf.globalOptions.excl(optTaintMode)
proc runNimScriptIfExists(path: AbsoluteFile)=
if fileExists(path):
runNimScript(cache, path, freshDefines = false, conf)
# Caution: make sure this stays in sync with `loadConfigs` # Caution: make sure this stays in sync with `loadConfigs`
if optSkipSystemConfigFile notin conf.globalOptions: 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 # 'nimsuggest foo.nims' means to just auto-complete the NimScript file
discard discard
# Reload configuration from .cfg file
loadConfigs(DefaultConfig, cache, conf)
# now process command line arguments again, because some options in the # now process command line arguments again, because some options in the
# command line can overwite the config file's settings # command line can overwite the config file's settings
extccomp.initVars(conf) 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, proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo) = info: TLineInfo) =
case arg.normalize case arg.normalize
of "on": conf.options = conf.options + op of "","on": conf.options = conf.options + op
of "off": conf.options = conf.options - op of "off": conf.options = conf.options - op
else: localError(conf, info, errOnOrOffExpectedButXFound % arg) 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, proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass,
info: TLineInfo) = info: TLineInfo) =
case arg.normalize case arg.normalize
of "on": conf.globalOptions = conf.globalOptions + op of "", "on": conf.globalOptions = conf.globalOptions + op
of "off": conf.globalOptions = conf.globalOptions - op of "off": conf.globalOptions = conf.globalOptions - op
else: localError(conf, info, errOnOrOffExpectedButXFound % arg) else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
@ -224,7 +224,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
case arg.normalize case arg.normalize
of "boehm": result = conf.selectedGC == gcBoehm of "boehm": result = conf.selectedGC == gcBoehm
of "refc": result = conf.selectedGC == gcRefc of "refc": result = conf.selectedGC == gcRefc
of "v2": result = conf.selectedGC == gcV2 of "v2": result = false
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
of "generational": result = false of "generational": result = false
of "destructors": result = conf.selectedGC == gcDestructors 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 "patterns": result = contains(conf.options, optPatterns)
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace) of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
of "nilseqs": result = contains(conf.options, optNilSeqs) of "nilseqs": result = contains(conf.options, optNilSeqs)
of "oldast": result = contains(conf.options, optOldAst)
else: invalidCmdLineOption(conf, passCmd1, switch, info) else: invalidCmdLineOption(conf, passCmd1, switch, info)
proc processPath(conf: ConfigRef; path: string, info: TLineInfo, 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}: if pass in {passCmd2, passPP}:
addExternalFileToLink(conf, AbsoluteFile arg) addExternalFileToLink(conf, AbsoluteFile arg)
of "debuginfo": of "debuginfo":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optCDebug}, arg, pass, info)
incl(conf.globalOptions, optCDebug)
of "embedsrc": of "embedsrc":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optEmbedOrigSrc}, arg, pass, info)
incl(conf.globalOptions, optEmbedOrigSrc)
of "compileonly", "c": of "compileonly", "c":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
incl(conf.globalOptions, optCompileOnly)
of "nolinking": of "nolinking":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optNoLinking}, arg, pass, info)
incl(conf.globalOptions, optNoLinking)
of "nomain": of "nomain":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optNoMain}, arg, pass, info)
incl(conf.globalOptions, optNoMain)
of "forcebuild", "f": of "forcebuild", "f":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info)
incl(conf.globalOptions, optForceFullMake)
of "project": of "project":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optWholeProject}, arg, pass, info)
incl conf.globalOptions, optWholeProject
of "gc": of "gc":
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
case arg.normalize case arg.normalize
@ -442,7 +436,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "refc": of "refc":
conf.selectedGC = gcRefc conf.selectedGC = gcRefc
of "v2": of "v2":
conf.selectedGC = gcV2 message(conf, info, warnDeprecated, "--gc:v2 is deprecated; using default gc")
of "markandsweep": of "markandsweep":
conf.selectedGC = gcMarkAndSweep conf.selectedGC = gcMarkAndSweep
defineSymbol(conf.symbols, "gcmarkandsweep") defineSymbol(conf.symbols, "gcmarkandsweep")
@ -498,7 +492,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
else: undefSymbol(conf.symbols, "hotcodereloading") else: undefSymbol(conf.symbols, "hotcodereloading")
of "oldnewlines": of "oldnewlines":
case arg.normalize case arg.normalize
of "on": of "","on":
conf.oldNewlines = true conf.oldNewlines = true
defineSymbol(conf.symbols, "nimOldNewlines") defineSymbol(conf.symbols, "nimOldNewlines")
of "off": of "off":
@ -508,6 +502,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
localError(conf, info, errOnOrOffExpectedButXFound % arg) localError(conf, info, errOnOrOffExpectedButXFound % arg)
of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info) of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, 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 "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
of "floatchecks": of "floatchecks":
processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info) 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) processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info)
of "import": of "import":
expectArg(conf, switch, arg, pass, info) 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": of "include":
expectArg(conf, switch, arg, pass, info) 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": of "listcmd":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
incl(conf.globalOptions, optListCmd)
of "genmapping": of "genmapping":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optGenMapping}, arg, pass, info)
incl(conf.globalOptions, optGenMapping)
of "os": of "os":
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
if pass in {passCmd1, passPP}: if pass in {passCmd1, passPP}:
@ -615,8 +610,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
elif cpu != conf.target.hostCPU: elif cpu != conf.target.hostCPU:
setTarget(conf.target, conf.target.targetOS, cpu) setTarget(conf.target, conf.target.targetOS, cpu)
of "run", "r": of "run", "r":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optRun}, arg, pass, info)
incl(conf.globalOptions, optRun)
of "errormax": of "errormax":
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)
# Note: `nim check` (etc) can overwrite this. # 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 of "v2": conf.symbolFiles = v2Sf
else: localError(conf, info, "invalid option for --incremental: " & arg) else: localError(conf, info, "invalid option for --incremental: " & arg)
of "skipcfg": of "skipcfg":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info)
incl(conf.globalOptions, optSkipSystemConfigFile)
of "skipprojcfg": of "skipprojcfg":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optSkipProjConfigFile}, arg, pass, info)
incl(conf.globalOptions, optSkipProjConfigFile)
of "skipusercfg": of "skipusercfg":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optSkipUserConfigFile}, arg, pass, info)
incl(conf.globalOptions, optSkipUserConfigFile)
of "skipparentcfg": of "skipparentcfg":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optSkipParentConfigFiles}, arg, pass, info)
incl(conf.globalOptions, optSkipParentConfigFiles)
of "genscript", "gendeps": of "genscript", "gendeps":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optGenScript}, arg, pass, info)
incl(conf.globalOptions, optGenScript) processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
incl(conf.globalOptions, optCompileOnly)
of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info) of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
of "lib": of "lib":
expectArg(conf, switch, arg, pass, info) 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) expectNoArg(conf, switch, arg, pass, info)
conf.ideCmd = ideUse conf.ideCmd = ideUse
of "stdout": of "stdout":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optStdout}, arg, pass, info)
incl(conf.globalOptions, optStdout)
of "listfullpaths": of "listfullpaths":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optListFullPaths}, arg, pass, info)
incl conf.globalOptions, optListFullPaths
of "dynliboverride": of "dynliboverride":
dynlibOverride(conf, switch, arg, pass, info) dynlibOverride(conf, switch, arg, pass, info)
of "dynliboverrideall": of "dynliboverrideall":
expectNoArg(conf, switch, arg, pass, info) processOnOffSwitchG(conf, {optDynlibOverrideAll}, arg, pass, info)
incl conf.globalOptions, optDynlibOverrideAll
of "cs": of "cs":
# only supported for compatibility. Does nothing. # only supported for compatibility. Does nothing.
expectArg(conf, switch, arg, pass, info) expectArg(conf, switch, arg, pass, info)

View file

@ -296,7 +296,8 @@ proc makePtrType(c: Con, baseType: PType): PType =
template genOp(opr, opname, ri) = template genOp(opr, opname, ri) =
let op = opr let op = opr
if op == nil: 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: elif op.ast[genericParamsPos].kind != nkEmpty:
globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic") globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic")
patchHead op patchHead op

View file

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

View file

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

View file

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

View file

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

View file

@ -92,6 +92,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
rawImportSymbol(c, e) rawImportSymbol(c, e)
e = nextIdentIter(it, fromMod.tab) e = nextIdentIter(it, fromMod.tab)
else: rawImportSymbol(c, s) else: rawImportSymbol(c, s)
suggestSym(c.config, n.info, s, c.graph.usageSym, false)
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
var i: TTabIter 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") localError(c.config, n.info, "A module cannot import itself")
if sfDeprecated in result.flags: if sfDeprecated in result.flags:
if result.constraint != nil: 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: 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) suggestSym(c.config, n.info, result, c.graph.usageSym, false)
importStmtResult.add newSymNode(result, n.info) importStmtResult.add newSymNode(result, n.info)
#newStrNode(toFullPath(c.config, f), n.info) #newStrNode(toFullPath(c.config, f), n.info)

View file

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

View file

@ -17,7 +17,7 @@
import import
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream, hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
wordrecg, lineinfos, pathutils wordrecg, lineinfos, pathutils, parseutils
const const
MaxLineLength* = 80 # lines longer than this lead to a warning MaxLineLength* = 80 # lines longer than this lead to a warning
@ -307,20 +307,6 @@ template tokenEndPrevious(tok, pos) =
when defined(nimpretty): when defined(nimpretty):
tok.offsetB = L.offsetBase + pos 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) = template eatChar(L: var TLexer, t: var TToken, replacementChar: char) =
add(t.literal, replacementChar) add(t.literal, replacementChar)
inc(L.bufpos) inc(L.bufpos)
@ -586,33 +572,43 @@ proc getNumber(L: var TLexer, result: var TToken) =
of floatTypes: of floatTypes:
result.fNumber = parseFloat(result.literal) result.fNumber = parseFloat(result.literal)
of tkUint64Lit: of tkUint64Lit:
xi = 0 var iNumber: uint64
let len = unsafeParseUInt(result.literal, xi) var len: int
if len != result.literal.len or len == 0: try:
raise newException(ValueError, "invalid integer: " & $xi) len = parseBiggestUInt(result.literal, iNumber)
result.iNumber = xi 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: 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 = let outOfRange =
case result.tokType case result.tokType
of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high) of tkInt8Lit: result.iNumber > int8.high
of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or of tkUInt8Lit: result.iNumber > BiggestInt(uint8.high)
result.iNumber > BiggestInt(uint8.high)) of tkInt16Lit: result.iNumber > int16.high
of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high) of tkUInt16Lit: result.iNumber > BiggestInt(uint16.high)
of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or of tkInt32Lit: result.iNumber > int32.high
result.iNumber > BiggestInt(uint16.high)) of tkUInt32Lit: result.iNumber > BiggestInt(uint32.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))
else: false else: false
if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos) if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos)
# Promote int literal to int64? Not always necessary, but more consistent # Promote int literal to int64? Not always necessary, but more consistent
if result.tokType == tkIntLit: if result.tokType == tkIntLit:
if (result.iNumber < low(int32)) or (result.iNumber > high(int32)): if result.iNumber > high(int32):
result.tokType = tkInt64Lit result.tokType = tkInt64Lit
except ValueError: except ValueError:

View file

@ -65,7 +65,7 @@ const
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
warnXIsNeverRead: "'$1' is never read", warnXIsNeverRead: "'$1' is never read",
warnXmightNotBeenInit: "'$1' might not have been initialized", warnXmightNotBeenInit: "'$1' might not have been initialized",
warnDeprecated: "$1 is deprecated", warnDeprecated: "$1",
warnConfigDeprecated: "config file '$1' is deprecated", warnConfigDeprecated: "config file '$1' is deprecated",
warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)", warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
warnUnknownMagic: "unknown magic '$1' might crash the compiler", warnUnknownMagic: "unknown magic '$1' might crash the compiler",
@ -171,7 +171,7 @@ proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd, warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
hintSource, hintGlobalVar, hintGCStats} hintSource, hintGlobalVar, hintGCStats}
result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf, result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
hintProcessing, hintPattern, hintExecuting, hintLinking} hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC}
const const
NotesVerbosity* = computeNotesVerbosity() 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) prettybase.replaceDeprecated(conf, n.info, s, result)
else: else:
message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " & 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 = proc localSearchInScope*(c: PContext, s: PIdent): PSym =
result = strTableGet(c.currentScope.symbols, s) result = strTableGet(c.currentScope.symbols, s)
@ -150,7 +150,7 @@ type
proc getSymRepr*(conf: ConfigRef; s: PSym): string = proc getSymRepr*(conf: ConfigRef; s: PSym): string =
case s.kind case s.kind
of skProc, skFunc, skMethod, skConverter, skIterator: of routineKinds, skType:
result = getProcHeader(conf, s) result = getProcHeader(conf, s)
else: else:
result = s.name.s result = s.name.s
@ -174,7 +174,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
# maybe they can be made skGenericParam as well. # maybe they can be made skGenericParam as well.
if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and
s.typ.kind != tyGenericParam: 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) s = nextIter(it, scope.symbols)
proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string; 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) wrongRedefinition(c, info, sym.name.s, conflict.info)
proc addDecl*(c: PContext, sym: PSym) = proc addDecl*(c: PContext, sym: PSym) =
let conflict = c.currentScope.addUniqueSym(sym) let conflict = strTableInclReportConflict(c.currentScope.symbols, sym, true)
if conflict != nil: if conflict != nil:
wrongRedefinition(c, sym.info, sym.name.s, conflict.info) wrongRedefinition(c, sym.info, sym.name.s, conflict.info)

View file

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

View file

@ -147,7 +147,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
# hacky way to implement 'x / y /../ z': # hacky way to implement 'x / y /../ z':
result = renderTree(n, {renderNoComments}).replace(" ") result = renderTree(n, {renderNoComments}).replace(" ")
of nkDotExpr: 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(".", "/") result = renderTree(n, {renderNoComments}).replace(".", "/")
of nkImportAs: of nkImportAs:
result = getModuleName(conf, n.sons[0]) result = getModuleName(conf, n.sons[0])

View file

@ -135,6 +135,10 @@ const
WarningColor = fgYellow WarningColor = fgYellow
HintTitle = "Hint: " HintTitle = "Hint: "
HintColor = fgGreen 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 getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len
proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L) 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: if fileIdx.int32 < 0 or conf == nil:
"???" "???"
else: 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 = proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string =
if fileIdx.int32 < 0 or conf == nil: result = "???" if fileIdx.int32 < 0 or conf == nil: result = "???"
@ -192,10 +199,10 @@ proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
result = "???" result = "???"
return return
let absPath = conf.m.fileInfos[info.fileIndex.int32].fullPath.string 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: if optListFullPaths in conf.globalOptions:
result = absPath result = absPath
else: else:
let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string
result = if absPath.len < relPath.len: absPath else: relPath result = if absPath.len < relPath.len: absPath else: relPath
proc toLinenumber*(info: TLineInfo): int {.inline.} = proc toLinenumber*(info: TLineInfo): int {.inline.} =
@ -208,7 +215,9 @@ proc toFileLine*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
result = toFilename(conf, info) & ":" & $info.line result = toFilename(conf, info) & ":" & $info.line
proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} = 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) proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
@ -359,7 +368,7 @@ proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) =
styledMsgWriteln(styleBright, styledMsgWriteln(styleBright,
PosFormat % [toMsgFilename(conf, context.info), PosFormat % [toMsgFilename(conf, context.info),
coordToStr(context.info.line.int), coordToStr(context.info.line.int),
coordToStr(context.info.col+1)], coordToStr(context.info.col+ColOffset)],
resetStyle, resetStyle,
message) message)
info = context.info info = context.info
@ -446,7 +455,7 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s
of hintMin..hintMax: HintTitle of hintMin..hintMax: HintTitle
else: ErrorTitle else: ErrorTitle
result = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int), result = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int),
coordToStr(info.col+1)] & coordToStr(info.col+ColOffset)] &
title & title &
getMessageStr(msg, arg) getMessageStr(msg, arg)
@ -483,11 +492,8 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
color = HintColor color = HintColor
if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)] if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)]
inc(conf.hintCounter) 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), let x = PosFormat % [toMsgFilename(conf, info), coordToStr(info.line.int),
coordToStr(info.col+1)] coordToStr(info.col+ColOffset)]
let s = getMessageStr(msg, arg) let s = getMessageStr(msg, arg)
if not ignoreMsg: if not ignoreMsg:

View file

@ -40,7 +40,8 @@ type # please make sure we have under 32 options
optMemTracker, optMemTracker,
optHotCodeReloading, optHotCodeReloading,
optLaxStrings, optLaxStrings,
optNilSeqs optNilSeqs,
optOldAst
TOptions* = set[TOption] TOptions* = set[TOption]
TGlobalOption* = enum # **keep binary compatible** TGlobalOption* = enum # **keep binary compatible**
@ -77,7 +78,7 @@ type # please make sure we have under 32 options
optShowAllMismatches # show all overloading resolution candidates optShowAllMismatches # show all overloading resolution candidates
optWholeProject # for 'doc2': output any dependency optWholeProject # for 'doc2': output any dependency
optMixedMode # true if some module triggered C++ codegen optMixedMode # true if some module triggered C++ codegen
optListFullPaths optListFullPaths # use full paths in toMsgFilename, toFilename
optNoNimblePath optNoNimblePath
optDynlibOverrideAll optDynlibOverrideAll
@ -132,7 +133,7 @@ type
TSystemCC* = enum TSystemCC* = enum
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, 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 CfileFlag* {.pure.} = enum
Cached, ## no need to recompile this time Cached, ## no need to recompile this time
@ -482,16 +483,7 @@ proc setDefaultLibpath*(conf: ConfigRef) =
conf.libpath = AbsoluteDir parentNimLibPath conf.libpath = AbsoluteDir parentNimLibPath
proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile = proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
# on Windows, 'expandFilename' calls getFullPathName which doesn't do result = AbsoluteFile path.string.expandFilename
# 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
proc shortenDir*(conf: ConfigRef; dir: string): string {. proc shortenDir*(conf: ConfigRef; dir: string): string {.
deprecated: "use 'relativeTo' instead".} = deprecated: "use 'relativeTo' instead".} =

View file

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

View file

@ -332,12 +332,15 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
parMessage(p, errIdentifierExpected, p.tok) parMessage(p, errIdentifierExpected, p.tok)
break break
of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi: of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi:
let lineinfo = parLineinfo(p)
var accm = "" var accm = ""
while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals, while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals,
tkParLe..tkParDotRi}: tkParLe..tkParDotRi}:
accm.add(tokToStr(p.tok)) accm.add(tokToStr(p.tok))
getTok(p) 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: of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit:
result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p)) result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p))
getTok(p) getTok(p)
@ -1764,23 +1767,8 @@ proc parseSection(p: var TParser, kind: TNodeKind,
else: else:
parMessage(p, errIdentifierExpected, p.tok) 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 = 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) result = newNodeP(nkEnumTy, p)
getTok(p) getTok(p)
addSon(result, p.emptyNode) addSon(result, p.emptyNode)
@ -1790,25 +1778,35 @@ proc parseEnum(p: var TParser): PNode =
while true: while true:
var a = parseSymbol(p) var a = parseSymbol(p)
if a.kind == nkEmpty: return 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: if p.tok.indent >= 0 and p.tok.indent <= p.currInd:
add(result, a) add(result, symPragma)
break break
if p.tok.tokType == tkEquals and p.tok.indent < 0: if p.tok.tokType == tkEquals and p.tok.indent < 0:
getTok(p) getTok(p)
optInd(p, a) optInd(p, symPragma)
var b = a var b = symPragma
a = newNodeP(nkEnumFieldDef, p) symPragma = newNodeP(nkEnumFieldDef, p)
addSon(a, b) addSon(symPragma, b)
addSon(a, parseExpr(p)) addSon(symPragma, parseExpr(p))
if p.tok.indent < 0 or p.tok.indent >= p.currInd: 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: if p.tok.tokType == tkComma and p.tok.indent < 0:
getTok(p) getTok(p)
rawSkipComment(p, a) rawSkipComment(p, symPragma)
else: else:
if p.tok.indent < 0 or p.tok.indent >= p.currInd: if p.tok.indent < 0 or p.tok.indent >= p.currInd:
rawSkipComment(p, a) rawSkipComment(p, symPragma)
addSon(result, a) addSon(result, symPragma)
if p.tok.indent >= 0 and p.tok.indent <= p.currInd or if p.tok.indent >= 0 and p.tok.indent <= p.currInd or
p.tok.tokType == tkEof: p.tok.tokType == tkEof:
break break
@ -1920,6 +1918,8 @@ proc parseObject(p: var TParser): PNode =
result = newNodeP(nkObjectTy, p) result = newNodeP(nkObjectTy, p)
getTok(p) getTok(p)
if p.tok.tokType == tkCurlyDotLe and p.validInd: 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)) addSon(result, parsePragma(p))
else: else:
addSon(result, p.emptyNode) addSon(result, p.emptyNode)
@ -1992,13 +1992,42 @@ proc parseTypeClass(p: var TParser): PNode =
proc parseTypeDef(p: var TParser): PNode = proc parseTypeDef(p: var TParser): PNode =
#| #|
#| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux #| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux
#| indAndComment? / identVisDot genericParamList? pragma '=' optInd typeDefAux
#| indAndComment? #| indAndComment?
result = newNodeP(nkTypeDef, p) 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: 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: 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: if p.tok.tokType == tkEquals:
result.info = parLineInfo(p) result.info = parLineInfo(p)
getTok(p) getTok(p)
@ -2035,6 +2064,23 @@ proc parseVariable(p: var TParser): PNode =
result[^1] = postExprBlocks(p, result[^1]) result[^1] = postExprBlocks(p, result[^1])
indAndComment(p, result) 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 = proc parseBind(p: var TParser, k: TNodeKind): PNode =
#| bindStmt = 'bind' optInd qualifiedIdent ^+ comma #| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
#| mixinStmt = 'mixin' 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): for module in items(implicits):
# implicit imports should not lead to a module importing itself # implicit imports should not lead to a module importing itself
if m.position != resolveMod(graph.config, module, relativeTo).int32: 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) var str = newStrNode(nkStrLit, module)
str.info = gCmdLineInfo str.info = m.info
importStmt.addSon str importStmt.addSon str
if not processTopLevelStmt(graph, importStmt, a): break if not processTopLevelStmt(graph, importStmt, a): break
@ -155,7 +155,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {
while true: while true:
openParsers(p, fileIdx, s, graph.cache, graph.config) 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 # XXX what about caching? no processing then? what if I change the
# modules to include between compilation runs? we'd need to track that # modules to include between compilation runs? we'd need to track that
# in ROD files. I think we should enable this feature only # in ROD files. I think we should enable this feature only

View file

@ -17,11 +17,9 @@ type
AbsoluteDir* = distinct string AbsoluteDir* = distinct string
RelativeFile* = distinct string RelativeFile* = distinct string
RelativeDir* = distinct string RelativeDir* = distinct string
AnyPath* = AbsoluteFile|AbsoluteDir|RelativeFile|RelativeDir
proc isEmpty*(x: AbsoluteFile): bool {.inline.} = x.string.len == 0 proc isEmpty*(x: AnyPath): 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 copyFile*(source, dest: AbsoluteFile) = proc copyFile*(source, dest: AbsoluteFile) =
os.copyFile(source.string, dest.string) os.copyFile(source.string, dest.string)
@ -44,14 +42,13 @@ proc cmpPaths*(x, y: AbsoluteDir): int {.borrow.}
proc createDir*(x: AbsoluteDir) {.borrow.} proc createDir*(x: AbsoluteDir) {.borrow.}
proc `$`*(x: AnyPath): string = x.string
when true: when true:
proc eqImpl(x, y: string): bool {.inline.} = proc eqImpl(x, y: string): bool {.inline.} =
result = cmpPaths(x, y) == 0 result = cmpPaths(x, y) == 0
proc `==`*(x, y: AbsoluteFile): bool = eqImpl(x.string, y.string) proc `==`*[T: AnyPath](x, y: T): 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 `/`*(base: AbsoluteDir; f: RelativeFile): AbsoluteFile = proc `/`*(base: AbsoluteDir; f: RelativeFile): AbsoluteFile =
#assert isAbsolute(base.string) #assert isAbsolute(base.string)
@ -88,6 +85,12 @@ when true:
when isMainModule: when isMainModule:
doAssert AbsoluteDir"/Users/me///" / RelativeFile"z.nim" == AbsoluteFile"/Users/me/z.nim" doAssert AbsoluteDir"/Users/me///" / RelativeFile"z.nim" == AbsoluteFile"/Users/me/z.nim"
doAssert relativePath("/foo/bar.nim", "/foo/", '/') == "bar.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): 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") 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} wThread, wRaises, wLocks, wTags, wGcSafe}
forVarPragmas* = {wInject, wGensym} forVarPragmas* = {wInject, wGensym}
allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
enumFieldPragmas* = {wDeprecated}
proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode = proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
let p = procAst[pragmasPos] let p = procAst[pragmasPos]
@ -241,7 +242,7 @@ proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
# deprecated as of 0.18.1 # deprecated as of 0.18.1
message(c.config, n.info, warnDeprecated, message(c.config, n.info, warnDeprecated,
"use {.experimental: \"codeReordering.\".} instead; " & "use {.experimental: \"codeReordering.\".} instead; " &
(if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}")) (if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}") & " is deprecated")
proc processCallConv(c: PContext, n: PNode) = proc processCallConv(c: PContext, n: PNode) =
if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent: 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) = proc processDefine(c: PContext, n: PNode) =
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent): if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
defineSymbol(c.config.symbols, n[1].ident.s) 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: else:
invalidPragma(c, n) invalidPragma(c, n)
proc processUndef(c: PContext, n: PNode) = proc processUndef(c: PContext, n: PNode) =
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent): if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
undefSymbol(c.config.symbols, n[1].ident.s) 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: else:
invalidPragma(c, n) invalidPragma(c, n)
@ -740,7 +741,7 @@ proc semCustomPragma(c: PContext, n: PNode): PNode =
result.kind = n.kind # pragma(arg) -> pragma: arg result.kind = n.kind # pragma(arg) -> pragma: arg
proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, 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 it = n.sons[i]
var key = if it.kind in nkPragmaCallKinds and it.len > 1: it.sons[0] else: it var key = if it.kind in nkPragmaCallKinds and it.len > 1: it.sons[0] else: it
if key.kind == nkBracketExpr: 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}: if sym.kind in {skTemplate, skMacro}:
incl(sym.flags, sfImmediate) incl(sym.flags, sfImmediate)
incl(sym.flags, sfAllUntyped) 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) else: invalidPragma(c, it)
of wDirty: of wDirty:
if sym.kind == skTemplate: incl(sym.flags, sfDirty) 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: of wExplain:
sym.flags.incl sfExplain sym.flags.incl sfExplain
of wDeprecated: 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) if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
incl(sym.flags, sfDeprecated) incl(sym.flags, sfDeprecated)
elif sym != nil and sym.kind != skModule: elif sym != nil and sym.kind != skModule:
@ -1090,10 +1091,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
of wThis: of wThis:
if it.kind in nkPragmaCallKinds and it.len == 2: if it.kind in nkPragmaCallKinds and it.len == 2:
c.selfName = considerQuotedIdent(c, it[1]) 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: elif it.kind == nkIdent or it.len == 1:
c.selfName = getIdent(c.cache, "self") 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: else:
localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments") localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
of wNoRewrite: of wNoRewrite:
@ -1111,15 +1112,25 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
else: sym.flags.incl sfUsed else: sym.flags.incl sfUsed
of wLiftLocals: discard of wLiftLocals: discard
else: invalidPragma(c, it) else: invalidPragma(c, it)
elif sym == nil or (sym != nil and sym.kind in {skVar, skLet, skParam, elif comesFromPush and whichKeyword(ident) in {wTags, wRaises}:
skField, skProc, skFunc, skConverter, skMethod, skType}): discard "ignore the .push pragma; it doesn't apply"
n.sons[i] = semCustomPragma(c, it)
elif sym != nil:
illegalCustomPragma(c, it, sym)
else: 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) = proc mergePragmas(n, pragmas: PNode) =
var pragmas = copyTree(pragmas)
overwriteLineInfo pragmas, n.info
if n[pragmasPos].kind == nkEmpty: if n[pragmasPos].kind == nkEmpty:
n[pragmasPos] = pragmas n[pragmasPos] = pragmas
else: else:
@ -1134,7 +1145,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
pushInfoContext(c.config, n.info) pushInfoContext(c.config, n.info)
var i = 0 var i = 0
while i < o.len: 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") internalError(c.config, n.info, "implicitPragmas")
inc i inc i
popInfoContext(c.config) popInfoContext(c.config)
@ -1163,7 +1174,7 @@ proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
if n == nil: return if n == nil: return
var i = 0 var i = 0
while i < n.len: while i < n.len:
if singlePragma(c, sym, n, i, validPragmas): break if singlePragma(c, sym, n, i, validPragmas, false): break
inc i inc i
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = 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) = proc (a: VmArgs) =
body body
template cbos(name, body) {.dirty.} = template cbexc(name, exc, body) {.dirty.} =
result.registerCallback "stdlib.system." & astToStr(name), result.registerCallback "stdlib.system." & astToStr(name),
proc (a: VmArgs) = proc (a: VmArgs) =
errorMsg = "" errorMsg = ""
try: try:
body body
except OSError: except exc:
errorMsg = getCurrentExceptionMsg() 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 # Idea: Treat link to file as a file, but ignore link to directory to prevent
# endless recursions out of the box. # endless recursions out of the box.
cbos listFiles: cbos listFiles:
@ -63,8 +66,10 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
os.removeFile getString(a, 0) os.removeFile getString(a, 0)
cbos createDir: cbos createDir:
os.createDir getString(a, 0) os.createDir getString(a, 0)
cbos getOsError:
setResult(a, errorMsg) result.registerCallback "stdlib.system.getError",
proc (a: VmArgs) = setResult(a, errorMsg)
cbos setCurrentDir: cbos setCurrentDir:
os.setCurrentDir getString(a, 0) os.setCurrentDir getString(a, 0)
cbos getCurrentDir: cbos getCurrentDir:
@ -155,6 +160,12 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
setResult(a, os.getAppFilename()) setResult(a, os.getAppFilename())
cbconf cppDefine: cbconf cppDefine:
options.cppDefine(conf, a.getString(0)) 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; proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
freshDefines=true; conf: ConfigRef) = freshDefines=true; conf: ConfigRef) =

View file

@ -556,8 +556,6 @@ proc isEmptyTree(n: PNode): bool =
else: result = false else: result = false
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = 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 c.topStmts == 0 and not isImportSystemStmt(c.graph, n):
if sfSystemModule notin c.module.flags and not isEmptyTree(n): if sfSystemModule notin c.module.flags and not isEmptyTree(n):
c.importTable.addSym c.graph.systemModule # import the "System" identifier c.importTable.addSym c.graph.systemModule # import the "System" identifier

View file

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

View file

@ -286,6 +286,13 @@ proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType =
result = newTypeS(kind, c) result = newTypeS(kind, c)
addSonSkipIntLit(result, baseType) 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 = proc makeTypeDesc*(c: PContext, typ: PType): PType =
if typ.kind == tyTypeDesc: if typ.kind == tyTypeDesc:
result = typ result = typ

View file

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

View file

@ -19,6 +19,9 @@ type
c: PContext c: PContext
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = 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 case n.kind
of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
of nkIdent, nkSym: of nkIdent, nkSym:

View file

@ -132,7 +132,9 @@ proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
return field.name.s return field.name.s
else: else:
return field.ast.strVal 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: else:
result = $x result = $x

View file

@ -129,14 +129,15 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
template typeWithSonsResult(kind, sons): PNode = template typeWithSonsResult(kind, sons): PNode =
newTypeWithSons(context, kind, sons).toNode(traitCall.info) newTypeWithSons(context, kind, sons).toNode(traitCall.info)
case trait.sym.name.s let s = trait.sym.name.s
case s
of "or", "|": of "or", "|":
return typeWithSonsResult(tyOr, @[operand, operand2]) return typeWithSonsResult(tyOr, @[operand, operand2])
of "and": of "and":
return typeWithSonsResult(tyAnd, @[operand, operand2]) return typeWithSonsResult(tyAnd, @[operand, operand2])
of "not": of "not":
return typeWithSonsResult(tyNot, @[operand]) return typeWithSonsResult(tyNot, @[operand])
of "name": of "name", "$":
result = newStrNode(nkStrLit, operand.typeToString(preferTypeName)) result = newStrNode(nkStrLit, operand.typeToString(preferTypeName))
result.typ = newType(tyString, context) result.typ = newType(tyString, context)
result.info = traitCall.info result.info = traitCall.info
@ -160,7 +161,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
hasDestructor(t) hasDestructor(t)
result = newIntNodeT(ord(not complexObj), traitCall, c.graph) result = newIntNodeT(ord(not complexObj), traitCall, c.graph)
else: else:
localError(c.config, traitCall.info, "unknown trait") localError(c.config, traitCall.info, "unknown trait: " & s)
result = newNodeI(nkEmpty, traitCall.info) result = newNodeI(nkEmpty, traitCall.info)
proc semTypeTraits(c: PContext, n: PNode): PNode = 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: elif efInTypeof in flags:
result.typ = n[1].typ 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 semProc(c: PContext, n: PNode): PNode
proc semExprBranch(c: PContext, n: PNode; flags: TExprFlags = {}): 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) for it in n: fixNilType(c, it)
n.typ = nil 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 c.matchedConcept != nil or efInTypeof in flags: return
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}: if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
if implicitlyDiscardable(result): if implicitlyDiscardable(result):
var n = newNodeI(nkDiscardStmt, result.info, 1) result = newNode(nkDiscardStmt, result.info, @[result])
n[0] = result
elif result.typ.kind != tyError and c.config.cmd != cmdInteractive: elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
var n = result var n = result
while n.kind in skipForDiscardable: n = n.lastSon 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) else: illFormedAst(it, c.config)
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
(not hasElse and efInTypeof notin flags): (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 result.kind = nkIfStmt
# propagate any enforced VoidContext: # propagate any enforced VoidContext:
if typ == c.enforceVoidContext: result.typ = c.enforceVoidContext 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 dec c.p.inTryStmt
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}: if isEmptyType(typ) or typ.kind in {tyNil, tyExpr}:
discardCheck(c, n.sons[0], flags) n.sons[0] = discardCheck(c, n.sons[0], 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)
if typ == c.enforceVoidContext: if typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext result.typ = c.enforceVoidContext
else: 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) n.sons[0] = fitNode(c, typ, n.sons[0], n.sons[0].info)
for i in 1..last: for i in 1..last:
var it = n.sons[i] var it = n.sons[i]
@ -322,14 +318,24 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
result = semIdentWithPragma(c, kind, n, {}) result = semIdentWithPragma(c, kind, n, {})
if result.owner.kind == skModule: if result.owner.kind == skModule:
incl(result.flags, sfGlobal) 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) = proc checkNilable(c: PContext; v: PSym) =
if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and
{tfNotNil, tfNeedsInit} * v.typ.flags != {}: {tfNotNil, tfNeedsInit} * v.typ.flags != {}:
if v.ast.isNil: if v.astdef.isNil:
message(c.config, v.info, warnProveInit, v.name.s) 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) message(c.config, v.info, warnProveInit, v.name.s)
include semasgn include semasgn
@ -470,7 +476,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
# this can only happen for errornous var statements: # this can only happen for errornous var statements:
if typ == nil: continue if typ == nil: continue
typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {}) 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}) var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
if a.kind == nkVarTuple: if a.kind == nkVarTuple:
if tup.kind != tyTuple: 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) message(c.config, a.info, warnShadowIdent, v.name.s)
if a.kind != nkVarTuple: if a.kind != nkVarTuple:
if def.kind != nkEmpty: 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) if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit)
setVarType(c, v, typ) setVarType(c, v, typ)
b = newNodeI(nkIdentDefs, a.info) 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, a.sons[length-2])
addSon(b, copyTree(def)) addSon(b, copyTree(def))
addToVarSection(c, result, n, b) 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: else:
if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j] if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
# bug #7663, for 'nim check' this can be a non-tuple: # 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 = proc semConst(c: PContext, n: PNode): PNode =
result = copyNode(n) result = copyNode(n)
inc c.inStaticContext
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
if c.config.cmd == cmdIdeTools: suggestStmt(c, a) if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
if a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
if a.kind != nkConstDef: illFormedAst(a, c.config) if a.kind notin {nkConstDef, nkVarTuple}: illFormedAst(a, c.config)
checkSonsLen(a, 3, c.config) checkMinSonsLen(a, 3, c.config)
var v = semIdentDef(c, a.sons[0], skConst) var length = sonsLen(a)
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)
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: if def == nil:
localError(c.config, a.sons[2].info, errConstExprExpected) localError(c.config, a.sons[length-1].info, errConstExprExpected)
continue continue
# check type compatibility between def.typ and typ: # check type compatibility between def.typ and typ:
if typ != nil: if typ != nil:
@ -567,21 +589,44 @@ proc semConst(c: PContext, n: PNode): PNode =
else: else:
typ = def.typ typ = def.typ
if typ == nil: if typ == nil:
localError(c.config, a.sons[2].info, errConstExprExpected) localError(c.config, a.sons[length-1].info, errConstExprExpected)
continue continue
if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit: if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
localError(c.config, a.info, "invalid type for const: " & typeToString(typ)) localError(c.config, a.info, "invalid type for const: " & typeToString(typ))
continue continue
setVarType(c, v, typ)
v.ast = def # no need to copy var b: PNode
if sfGenSym notin v.flags: addInterfaceDecl(c, v) if a.kind == nkVarTuple:
elif v.owner == nil: v.owner = getCurrOwner(c) if typ.kind != tyTuple:
var b = newNodeI(nkConstDef, a.info) localError(c.config, a.info, errXExpected, "tuple")
if importantComments(c.config): b.comment = a.comment elif length-2 != sonsLen(typ):
addSon(b, newSymNode(v)) localError(c.config, a.info, errWrongNumberOfVariables)
addSon(b, a.sons[1]) b = newNodeI(nkVarTuple, a.info)
addSon(b, copyTree(def)) newSons(b, length)
addSon(result, b) 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 include semfields
@ -637,7 +682,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
openScope(c) openScope(c)
n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags) n.sons[length-1] = semExprBranch(c, n.sons[length-1], flags)
if efInTypeof notin flags: if efInTypeof notin flags:
discardCheck(c, n.sons[length-1], flags) n.sons[^1] = discardCheck(c, n.sons[^1], flags)
closeScope(c) closeScope(c)
dec(c.p.nestedLoopCounter) dec(c.p.nestedLoopCounter)
@ -824,7 +869,8 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
closeScope(c) closeScope(c)
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or
(not hasElse and efInTypeof notin flags): (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: # propagate any enforced VoidContext:
if typ == c.enforceVoidContext: if typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext result.typ = c.enforceVoidContext
@ -1074,6 +1120,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
#debug s.typ #debug s.typ
s.ast = a s.ast = a
popOwner(c) 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] let aa = a.sons[2]
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
aa.sons[0].kind == nkObjectTy: aa.sons[0].kind == nkObjectTy:
@ -1090,9 +1138,13 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
proc checkForMetaFields(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: 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 if n.isNil: return
case n.kind case n.kind
@ -1107,9 +1159,9 @@ proc checkForMetaFields(c: PContext; n: PNode) =
tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink: tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink:
let start = ord(t.kind in {tyGenericInvocation, tyGenericInst}) let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
for i in start ..< t.len: for i in start ..< t.len:
checkMeta(t.sons[i]) checkMeta(c, n, t.sons[i])
else: else:
checkMeta(t) checkMeta(c, n, t)
else: else:
internalAssert c.config, false internalAssert c.config, false
@ -1205,9 +1257,6 @@ proc semTypeSection(c: PContext, n: PNode): PNode =
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) = proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) =
s.typ = semProcTypeNode(c, n, genericParams, nil, s.kind) 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) = proc addParams(c: PContext, n: PNode, kind: TSymKind) =
for i in countup(1, sonsLen(n)-1): 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) addResult(c, s.typ.sons[0], n.info, s.kind)
addResultNode(c, n) 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) = proc semOverride(c: PContext, s: PSym, n: PNode) =
case s.name.s.normalize let name = s.name.s.normalize
case name
of "=destroy": of "=destroy":
let t = s.typ let t = s.typ
var noError = false var noError = false
@ -1436,12 +1492,16 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
elif obj.kind == tyGenericInvocation: obj = obj.sons[0] elif obj.kind == tyGenericInvocation: obj = obj.sons[0]
else: break else: break
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
obj = canonType(c, obj)
if obj.destructor.isNil: if obj.destructor.isNil:
obj.destructor = s obj.destructor = s
else: else:
localError(c.config, n.info, errGenerated, localError(c.config, n.info, errGenerated,
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) "cannot bind another '" & s.name.s & "' to: " & typeToString(obj))
noError = true 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: if not noError and sfSystemModule notin s.owner.flags:
localError(c.config, n.info, errGenerated, localError(c.config, n.info, errGenerated,
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)") "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: else:
localError(c.config, n.info, errGenerated, localError(c.config, n.info, errGenerated,
"cannot bind 'deepCopy' to: " & typeToString(t)) "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: else:
localError(c.config, n.info, errGenerated, localError(c.config, n.info, errGenerated,
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") "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] objB = objB.sons[0]
else: break else: break
if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB): 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) let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink)
if opr[].isNil: if opr[].isNil:
opr[] = s opr[] = s
else: else:
localError(c.config, n.info, errGenerated, localError(c.config, n.info, errGenerated,
"cannot bind another '" & s.name.s & "' to: " & typeToString(obj)) "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 return
if sfSystemModule notin s.owner.flags: if sfSystemModule notin s.owner.flags:
localError(c.config, n.info, errGenerated, localError(c.config, n.info, errGenerated,
@ -1542,7 +1613,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
foundObj = true foundObj = true
x.methods.add((col,s)) x.methods.add((col,s))
if not foundObj: 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: else:
# why check for the body? bug #2400 has none. Checking for sfForward makes # why check for the body? bug #2400 has none. Checking for sfForward makes
# no sense either. # no sense either.
@ -1975,7 +2046,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
n.typ = n.sons[i].typ n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr if not isEmptyType(n.typ): n.kind = nkStmtListExpr
elif i != last or voidContext: elif i != last or voidContext:
discardCheck(c, n.sons[i], flags) n.sons[i] = discardCheck(c, n.sons[i], flags)
else: else:
n.typ = n.sons[i].typ n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr 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) if isPure: initStrTable(symbols)
var hasNull = false var hasNull = false
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
if n.sons[i].kind == nkEmpty: continue
case n.sons[i].kind case n.sons[i].kind
of nkEnumFieldDef: 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 v = semConstExpr(c, n.sons[i].sons[1])
var strVal: PNode = nil var strVal: PNode = nil
case skipTypes(v.typ, abstractInst-{tyTypeDesc}).kind 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 strVal = v.sons[1] # second tuple part is the string value
if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}: if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}:
if not isOrdinalType(v.sons[0].typ, allowEnumWithHoles=true): 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 x = getOrdValue(v.sons[0]) # first tuple part is the ordinal
else: else:
localError(c.config, strVal.info, errStringLiteralExpected) localError(c.config, strVal.info, errStringLiteralExpected)
@ -102,7 +107,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
x = counter x = counter
else: else:
if not isOrdinalType(v.typ, allowEnumWithHoles=true): 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) x = getOrdValue(v)
if i != 1: if i != 1:
if x != counter: incl(result.flags, tfEnumHasHoles) 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 e = n.sons[i].sym
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
e = newSymS(skEnumField, n.sons[i], c) 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: else:
illFormedAst(n[i], c.config) illFormedAst(n[i], c.config)
e.typ = result 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): if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
wrongRedefinition(c, e.info, e.name.s, conflict.info) wrongRedefinition(c, e.info, e.name.s, conflict.info)
inc(counter) 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 = proc semSet(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tySet, prev, c) result = newOrPrevType(tySet, prev, c)
@ -202,7 +210,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
tyError, tyObject}: tyError, tyObject}:
message c.config, n[i].info, errGenerated, "region needs to be an object type" message c.config, n[i].info, errGenerated, "region needs to be an object type"
else: 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, region)
addSonSkipIntLit(result, t) addSonSkipIntLit(result, t)
if tfPartial in result.flags: 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): for i in lastIndex.succ..(sonsLen(branch) - 2):
checkForOverlap(c, t, i, branchIndex) 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, proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
father: PNode, rectype: PType, hasCaseFields = false) father: PNode, rectype: PType, hasCaseFields = false)
proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, 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 return
incl(a.sons[0].sym.flags, sfDiscriminant) incl(a.sons[0].sym.flags, sfDiscriminant)
var covered: BiggestInt = 0 var covered: BiggestInt = 0
var chckCovered = false
var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc}) var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc})
if not isOrdinalType(typ): case typ.kind
localError(c.config, n.info, "selector must be of an ordinal type") of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool, tyRange:
elif firstOrd(c.config, typ) != 0: chckCovered = true
localError(c.config, n.info, "low(" & $a.sons[0].sym.name.s & of tyFloat..tyFloat128, tyString, tyError:
") must be 0 for discriminant") discard
elif lengthOrd(c.config, typ) > 0x00007FFF: else:
localError(c.config, n.info, "len($1) must be less than 32768" % a.sons[0].sym.name.s) if not isOrdinalType(typ):
var chckCovered = true localError(c.config, n.info, "selector must be of an ordinal type, float or string")
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
var b = copyTree(n.sons[i]) var b = copyTree(n.sons[i])
addSon(a, b) addSon(a, b)
@ -620,12 +636,14 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
checkMinSonsLen(b, 2, c.config) checkMinSonsLen(b, 2, c.config)
semCaseBranch(c, a, b, i, covered) semCaseBranch(c, a, b, i, covered)
of nkElse: of nkElse:
chckCovered = false
checkSonsLen(b, 1, c.config) 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) else: illFormedAst(n, c.config)
delSon(b, sonsLen(b) - 1) delSon(b, sonsLen(b) - 1)
semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype, hasCaseFields = true) 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") localError(c.config, a.info, "not all cases are covered")
addSon(father, a) 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 = proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
if n.kind == nkCurlyExpr: if n.kind == nkCurlyExpr:
result = semTypeNode(c, n.sons[0], nil) 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: else:
result = semTypeNode(c, n, nil) result = semTypeNode(c, n, nil)
@ -1054,6 +1077,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if hasType: if hasType:
typ = semParamType(c, a.sons[length-2], constraint) 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: if hasDefault:
def = a[^1] 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 # turn explicit 'void' return type into 'nil' because the rest of the
# compiler only checks for 'nil': # compiler only checks for 'nil':
if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid: 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: # '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 # 'p(): auto' and 'p(): expr' are equivalent, but the rest of the
# compiler is hardly aware of 'auto': # compiler is hardly aware of 'auto':
r = newTypeS(tyExpr, c) r = newTypeS(tyExpr, c)
elif r.kind != tyExpr: else:
if r.sym == nil or sfAnon notin r.sym.flags: if r.sym == nil or sfAnon notin r.sym.flags:
let lifted = liftParamType(c, kind, genericParams, r, "result", let lifted = liftParamType(c, kind, genericParams, r, "result",
n.sons[0].info) 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: if tx != result and tx.kind == tyObject and tx.sons[0] != nil:
semObjectTypeForInheritedGenericInst(c, n, tx) 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 = proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
var n = semExprWithType(c, n, {efDetermineType}) 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]) when useEffectSystem: setEffectsForProcType(c.graph, result, n.sons[1])
closeScope(c) 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 = proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
if n.kind == nkType: if n.kind == nkType:
result = symFromType(c, n.typ, n.info) 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... # XXX figure out why this has children already...
result.sons.setLen 0 result.sons.setLen 0
result.n = nil result.n = nil
if c.config.selectedGc == gcDestructors: result.flags = {tfHasAsgn}
result.flags = {tfHasAsgn}
else:
result.flags = {}
semContainerArg(c, n, "seq", result) semContainerArg(c, n, "seq", result)
else: else:
result = semContainer(c, n, tySequence, "seq", prev) 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: # constructor in a call:
if result == nil and f.kind == tyVarargs: if result == nil and f.kind == tyVarargs:
if f.n != nil: if f.n != nil:
# Forward to the varargs converter
result = localConvMatch(c, m, f, a, arg) result = localConvMatch(c, m, f, a, arg)
else: else:
r = typeRel(m, base(f), a) 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 # bug #4799, varargs accepting subtype relation object
elif r == isSubtype: elif r == isSubtype:
inc(m.subtypeMatches) inc(m.subtypeMatches)
if f.kind == tyTypeDesc: if base(f).kind == tyTypeDesc:
result = arg result = arg
else: else:
result = implicitConv(nkHiddenSubConv, f, arg, m, c) result = implicitConv(nkHiddenSubConv, base(f), arg, m, c)
m.baseTypeMatch = true m.baseTypeMatch = true
else: else:
result = userConvMatch(c, m, base(f), a, arg) result = userConvMatch(c, m, base(f), a, arg)
@ -2233,9 +2234,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
return return
if formal.typ.kind == tyVar: if formal.typ.kind == tyVar:
let arg_converter = if arg.kind == nkHiddenDeref: arg[0] else: arg let argConverter = if arg.kind == nkHiddenDeref: arg[0] else: arg
if arg_converter.kind == nkHiddenCallConv: if argConverter.kind == nkHiddenCallConv:
if arg_converter.typ.kind != tyVar: if argConverter.typ.kind != tyVar:
m.state = csNoMatch m.state = csNoMatch
m.mutabilityProblem = uint8(f-1) m.mutabilityProblem = uint8(f-1)
return return
@ -2267,8 +2268,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
if n.sons[a].kind == nkHiddenStdConv: if n.sons[a].kind == nkHiddenStdConv:
doAssert n.sons[a].sons[0].kind == nkEmpty and doAssert n.sons[a].sons[0].kind == nkEmpty and
n.sons[a].sons[1].kind == nkArgList and n.sons[a].sons[1].kind in {nkBracket, nkArgList}
n.sons[a].sons[1].len == 0
# Steal the container and pass it along # Steal the container and pass it along
setSon(m.call, formal.position + 1, n.sons[a].sons[1]) setSon(m.call, formal.position + 1, n.sons[a].sons[1])
else: else:

View file

@ -18,7 +18,7 @@ const
szIllegalRecursion* = -2 szIllegalRecursion* = -2
szUncomputedSize* = -1 szUncomputedSize* = -1
proc computeSizeAlign(conf: ConfigRef; typ: PType): void proc computeSizeAlign(conf: ConfigRef; typ: PType)
proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt = proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
## returns object alignment ## returns object alignment
@ -49,13 +49,14 @@ proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
else: else:
result = 1 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 ## ``offset`` is the offset within the object, after the node has been written, no padding bytes added
## ``align`` maximum alignment from all sub nodes ## ``align`` maximum alignment from all sub nodes
assert n != nil assert n != nil
if n.typ != nil and n.typ.size == szIllegalRecursion: if n.typ != nil and n.typ.size == szIllegalRecursion:
result.offset = szIllegalRecursion result.offset = szIllegalRecursion
result.align = szIllegalRecursion result.align = szIllegalRecursion
return return
result.align = 1 result.align = 1
@ -71,66 +72,52 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset:
of nkOfBranch, nkElse: of nkOfBranch, nkElse:
# offset parameter cannot be known yet, it needs to know the alignment first # offset parameter cannot be known yet, it needs to know the alignment first
let align = computeSubObjectAlign(conf, n.sons[i].lastSon) let align = computeSubObjectAlign(conf, n.sons[i].lastSon)
if align == szIllegalRecursion: if align == szIllegalRecursion:
result.offset = szIllegalRecursion result.offset = szIllegalRecursion
result.align = szIllegalRecursion result.align = szIllegalRecursion
return return
if align == szUnknownSize or maxChildAlign == szUnknownSize: if align == szUnknownSize or maxChildAlign == szUnknownSize:
maxChildAlign = szUnknownSize maxChildAlign = szUnknownSize
else: else:
maxChildAlign = max(maxChildAlign, align) maxChildAlign = max(maxChildAlign, align)
else: else:
internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)") internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
if maxChildAlign == szUnknownSize: if maxChildAlign == szUnknownSize:
result.align = szUnknownSize result.align = szUnknownSize
result.offset = szUnknownSize result.offset = szUnknownSize
else: else:
# the union neds to be aligned first, before the offsets can be assigned # the union neds to be aligned first, before the offsets can be assigned
let kindUnionOffset = align(kindOffset, maxChildAlign) let kindUnionOffset = align(kindOffset, maxChildAlign)
var maxChildOffset: BiggestInt = 0 var maxChildOffset: BiggestInt = 0
for i in 1 ..< sonsLen(n): for i in 1 ..< sonsLen(n):
let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset) let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset)
maxChildOffset = max(maxChildOffset, offset) maxChildOffset = max(maxChildOffset, offset)
result.align = max(kindAlign, maxChildAlign) result.align = max(kindAlign, maxChildAlign)
result.offset = maxChildOffset result.offset = maxChildOffset
of nkRecList: of nkRecList:
result.align = 1 # maximum of all member alignments result.align = 1 # maximum of all member alignments
var offset = initialOffset var offset = initialOffset
for i, child in n.sons: for i, child in n.sons:
let (new_offset, align) = computeObjectOffsetsFoldFunction(conf, child, offset) let (new_offset, align) = computeObjectOffsetsFoldFunction(conf, child, offset)
if new_offset == szIllegalRecursion: if new_offset == szIllegalRecursion:
result.offset = szIllegalRecursion result.offset = szIllegalRecursion
result.align = szIllegalRecursion result.align = szIllegalRecursion
return return
elif new_offset == szUnknownSize or offset == szUnknownSize: elif new_offset == szUnknownSize or offset == szUnknownSize:
# if anything is unknown, the rest becomes unknown as well # if anything is unknown, the rest becomes unknown as well
offset = szUnknownSize offset = szUnknownSize
result.align = szUnknownSize result.align = szUnknownSize
else: else:
offset = new_offset offset = new_offset
result.align = max(result.align, align) result.align = max(result.align, align)
# final alignment # final alignment
if offset == szUnknownSize: if offset == szUnknownSize:
result.offset = szUnknownSize result.offset = szUnknownSize
else: else:
result.offset = align(offset, result.align) result.offset = align(offset, result.align)
of nkSym: of nkSym:
var size = szUnknownSize var size = szUnknownSize
var align = szUnknownSize var align = szUnknownSize
if n.sym.bitsize == 0: # 0 represents bitsize not set if n.sym.bitsize == 0: # 0 represents bitsize not set
computeSizeAlign(conf, n.sym.typ) computeSizeAlign(conf, n.sym.typ)
size = n.sym.typ.size.int 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) let kindOffset = computePackedObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset, debug)
# the union neds to be aligned first, before the offsets can be assigned # the union neds to be aligned first, before the offsets can be assigned
let kindUnionOffset = kindOffset let kindUnionOffset = kindOffset
var maxChildOffset: BiggestInt = kindUnionOffset var maxChildOffset: BiggestInt = kindUnionOffset
for i in 1 ..< sonsLen(n): for i in 1 ..< sonsLen(n):
let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug) let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug)
@ -168,9 +154,17 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf
if result == szIllegalRecursion: if result == szIllegalRecursion:
break break
of nkSym: of nkSym:
computeSizeAlign(conf, n.sym.typ) var size = szUnknownSize
n.sym.offset = initialOffset.int if n.sym.bitsize == 0:
result = n.sym.offset + n.sym.typ.size 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: else:
result = szUnknownSize result = szUnknownSize
@ -324,7 +318,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
var headerAlign: int16 var headerAlign: int16
if typ.sons[0] != nil: if typ.sons[0] != nil:
# compute header size # compute header size
if conf.cmd == cmdCompileToCpp: if conf.cmd == cmdCompileToCpp:
# if the target is C++ the members of this type are written # 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 # 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.size = szUnknownSize
typ.align = szUnknownSize typ.align = szUnknownSize
return return
# header size is already in size from computeObjectOffsetsFoldFunction # header size is already in size from computeObjectOffsetsFoldFunction
# maxAlign is probably not changed at all from headerAlign # maxAlign is probably not changed at all from headerAlign
if tfPacked in typ.flags: if tfPacked in typ.flags:
@ -373,7 +365,6 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
else: else:
typ.align = int16(max(align, headerAlign)) typ.align = int16(max(align, headerAlign))
typ.size = align(offset, typ.align) typ.size = align(offset, typ.align)
of tyInferred: of tyInferred:
if typ.len > 1: if typ.len > 1:
computeSizeAlign(conf, typ.lastSon) 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 = proc extractPragma(s: PSym): PNode =
if s.kind in routineKinds: if s.kind in routineKinds:
result = s.ast[pragmasPos] result = s.ast[pragmasPos]
elif s.kind in {skType}: elif s.kind in {skType, skVar, skLet}:
# s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma] # s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
result = s.ast[0][1] result = s.ast[0][1]
doAssert result == nil or result.kind == nkPragma doAssert result == nil or result.kind == nkPragma
proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) = 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: if pragmaNode != nil:
for it in pragmaNode: for it in pragmaNode:
if whichPragma(it) == wDeprecated and it.safeLen == 2 and if whichPragma(it) == wDeprecated and it.safeLen == 2 and
it[1].kind in {nkStrLit..nkTripleStrLit}: 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 return
message(conf, info, warnDeprecated, s.name.s) message(conf, info, warnDeprecated, name & " is deprecated")
proc userError(conf: ConfigRef; info: TLineInfo; s: PSym) = proc userError(conf: ConfigRef; info: TLineInfo; s: PSym) =
let pragmaNode = extractPragma(s) let pragmaNode = extractPragma(s)
@ -486,6 +493,8 @@ proc markUsed(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
if s.kind == skEnumField and s.owner != nil: if s.kind == skEnumField and s.owner != nil:
incl(s.owner.flags, sfUsed) incl(s.owner.flags, sfUsed)
if sfDeprecated in s.owner.flags:
warnAboutDeprecated(conf, info, s)
if {sfDeprecated, sfError} * s.flags != {}: if {sfDeprecated, sfError} * s.flags != {}:
if sfDeprecated in s.flags: warnAboutDeprecated(conf, info, s) if sfDeprecated in s.flags: warnAboutDeprecated(conf, info, s)
if sfError in s.flags: userError(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 result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit
proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName): string = proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName): string =
result = sym.owner.name.s & '.' & sym.name.s & '(' assert sym != nil
var n = sym.typ.n result = sym.owner.name.s & '.' & sym.name.s
for i in countup(1, sonsLen(n) - 1): if sym.kind in routineKinds:
let p = n.sons[i] result.add '('
if p.kind == nkSym: var n = sym.typ.n
add(result, p.sym.name.s) for i in countup(1, sonsLen(n) - 1):
add(result, ": ") let p = n.sons[i]
add(result, typeToString(p.sym.typ, prefer)) if p.kind == nkSym:
if i != sonsLen(n)-1: add(result, ", ") add(result, p.sym.name.s)
else: add(result, ": ")
result.add renderTree(p) add(result, typeToString(p.sym.typ, prefer))
add(result, ')') if i != sonsLen(n)-1: add(result, ", ")
if n.sons[0].typ != nil: else:
result.add(": " & typeToString(n.sons[0].typ, prefer)) 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 "[declared in "
result.add(conf$sym.info) result.add(conf$sym.info)
result.add "]" result.add "]"
@ -429,7 +432,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
sfAnon notin t.sym.flags: sfAnon notin t.sym.flags:
if t.kind == tyInt and isIntLit(t): if t.kind == tyInt and isIntLit(t):
result = t.sym.name.s & " literal(" & $t.n.intVal & ")" 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]) result = typeToString(t.sons[0])
elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil: elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
result = t.sym.name.s result = t.sym.name.s
@ -756,7 +759,7 @@ type
TTypeCmpFlags* = set[TTypeCmpFlag] TTypeCmpFlags* = set[TTypeCmpFlag]
TSameTypeClosure = object {.pure.} TSameTypeClosure = object
cmp: TDistinctCompare cmp: TDistinctCompare
recCheck: int recCheck: int
flags: TTypeCmpFlags 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. ## This file implements the new evaluation engine for Nim code.
## An instruction is 1-3 int32s in memory, it is a register based VM. ## An instruction is 1-3 int32s in memory, it is a register based VM.
const
debugEchoCode = false
traceCode = debugEchoCode
import ast except getstr import ast except getstr
import import
@ -26,6 +22,9 @@ from evaltempl import evalTemplate
from modulegraphs import ModuleGraph, PPassContext from modulegraphs import ModuleGraph, PPassContext
const
traceCode = debugEchoCode
when hasFFI: when hasFFI:
import evalffi import evalffi
@ -65,22 +64,27 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) =
return return
stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1) stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1)
var info = c.debug[pc] var info = c.debug[pc]
# we now use the same format as in system/except.nim # we now use a format similar to the one in lib/system/excpt.nim
var s = substr(toFilename(c.config, info), 0) var s = ""
# this 'substr' prevents a strange corruption. XXX This needs to be # todo: factor with quotedFilename
# investigated eventually but first attempts to fix it broke everything if optExcessiveStackTrace in c.config.globalOptions:
# see the araq-wip-fixed-writebarrier branch. s = toFullPath(c.config, info)
else:
s = toFilename(c.config, info)
var line = toLinenumber(info) var line = toLinenumber(info)
var col = toColumn(info)
if line > 0: if line > 0:
add(s, '(') add(s, '(')
add(s, $line) add(s, $line)
add(s, ", ")
add(s, $(col + ColOffset))
add(s, ')') add(s, ')')
if x.prc != nil: if x.prc != nil:
for k in 1..max(1, 25-s.len): add(s, ' ') for k in 1..max(1, 25-s.len): add(s, ' ')
add(s, x.prc.name.s) add(s, x.prc.name.s)
msgWriteln(c.config, 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) = msg: string, lineInfo: TLineInfo) =
msgWriteln(c.config, "stack trace: (most recent call last)") msgWriteln(c.config, "stack trace: (most recent call last)")
stackTraceAux(c, tos, pc) 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) if c.mode == emRepl: globalError(c.config, lineInfo, msg)
else: localError(c.config, lineInfo, msg) else: localError(c.config, lineInfo, msg)
proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) = template stackTrace(c: PCtx, tos: PStackFrame, pc: int,
stackTrace(c, tos, pc, msg, c.debug[pc]) 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) = proc bailOut(c: PCtx; tos: PStackFrame) =
stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " & stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " &
@ -1239,8 +1249,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
let newLen = regs[rb].intVal.int let newLen = regs[rb].intVal.int
if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess) if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc]) else: c.setLenSeq(regs[ra].node, newLen, c.debug[pc])
of opcReset:
internalError(c.config, c.debug[pc], "too implement")
of opcNarrowS: of opcNarrowS:
decodeB(rkInt) decodeB(rkInt)
let min = -(1.BiggestInt shl (rb-1)) let min = -(1.BiggestInt shl (rb-1))

View file

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

View file

@ -33,6 +33,11 @@ import
import platform import platform
from os import splitFile from os import splitFile
const
debugEchoCode* = defined(nimVMDebug)
when debugEchoCode:
import asciitables
when hasFFI: when hasFFI:
import evalffi import evalffi
@ -43,9 +48,10 @@ type
TGenFlags = set[TGenFlag] TGenFlags = set[TGenFlag]
proc debugInfo(c: PCtx; info: TLineInfo): string = 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) = proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
## for debugging purposes
# first iteration: compute all necessary labels: # first iteration: compute all necessary labels:
var jumpTargets = initIntSet() var jumpTargets = initIntSet()
let last = if last < 0: c.code.len-1 else: min(last, c.code.len-1) 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: if x.opcode in relativeJumps:
jumpTargets.incl(i+x.regBx-wordExcess) 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 var i = start
while i <= last: while i <= last:
if i in jumpTargets: result.addf("L$1:\n", i) 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) result.add($i)
let opc = opcode(x) 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 y = c.code[i+1]
let z = c.code[i+2] 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[y.regBx-wordExcess].typeToString,
c.types[z.regBx-wordExcess].typeToString) c.types[z.regBx-wordExcess].typeToString)
inc i, 2 inc i, 2
elif opc < firstABxInstr: 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) x.regB, x.regC)
elif opc in relativeJumps: 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) i+x.regBx-wordExcess)
elif opc in {opcLdConst, opcAsgnConst}: elif opc in {opcLdConst, opcAsgnConst}:
let idx = x.regBx-wordExcess 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) c.constants[idx].renderTree, $idx)
elif opc in {opcMarshalLoad, opcMarshalStore}: elif opc in {opcMarshalLoad, opcMarshalStore}:
let y = c.code[i+1] 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) c.types[y.regBx-wordExcess].typeToString)
inc i inc i
else: 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("\t#")
result.add(debugInfo(c, c.debug[i])) result.add(debugInfo(c, c.debug[i]))
result.add("\n") result.add("\n")
inc i inc i
when debugEchoCode:
result = result.alignTable
proc echoCode*(c: PCtx; start=0; last = -1) {.deprecated.} = proc echoCode*(c: PCtx; start=0; last = -1) {.deprecated.} =
var buf = "" var buf = ""
@ -1092,7 +1105,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
of mReset: of mReset:
unused(c, n, dest) unused(c, n, dest)
var d = c.genx(n.sons[1]) 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: of mOf, mIs:
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
var tmp = c.genx(n.sons[1]) 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 #assert(a.sons[0].kind == nkSym) can happen for transformed vars
if a.kind == nkVarTuple: if a.kind == nkVarTuple:
for i in 0 .. a.len-3: for i in 0 .. a.len-3:
if not a[i].sym.isGlobal: setSlot(c, a[i].sym) if a[i].kind == nkSym:
checkCanEval(c, a[i]) if not a[i].sym.isGlobal: setSlot(c, a[i].sym)
checkCanEval(c, a[i])
c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner)) c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner))
elif a.sons[0].kind == nkSym: elif a.sons[0].kind == nkSym:
let s = a.sons[0].sym 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, arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
floor, ceil, `mod` 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.} = template mathop(op) {.dirty.} =
registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`) 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))) setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
systemop gorgeEx systemop gorgeEx
macrosop getProjectPath 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 tlsEmulation:on
@end @end
@if haiku: @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.options.linker = "-Wl,--as-needed -lnetwork"
gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork" gcc.cpp.options.linker = "-Wl,--as-needed -lnetwork"
clang.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: # Configuration for the LLVM CLang compiler:
clang.options.debug = "-g" clang.options.debug = "-g"
clang.cpp.options.debug = "-g"
clang.options.always = "-w" clang.options.always = "-w"
clang.options.speed = "-O3" clang.options.speed = "-O3"
clang.options.size = "-Os" 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: # Configuration for the Visual C/C++ compiler:
# VCCEXE is a tool that invokes the Visual Studio Developer Command Prompt # VCCEXE is a tool that invokes the Visual Studio Developer Command Prompt
# before calling the compiler. # before calling the compiler.

View file

@ -18,9 +18,9 @@ Advanced commands:
Advanced options: Advanced options:
-o:FILE, --out:FILE set the output filename -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 --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 -w:on|off|list, --warnings:on|off|list
turn all warnings on|off or list all available turn all warnings on|off or list all available
--warning[X]:on|off turn specific warning X on|off --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 --nimcache:PATH set the path used for generated files
--header:FILE the compiler should produce a .h file (FILE --header:FILE the compiler should produce a .h file (FILE
is optional) is optional)
-c, --compileOnly compile Nim files only; do not assemble or link -c, --compileOnly:on|off compile Nim files only; do not assemble or link
--noLinking compile Nim and generated files but do not link --noLinking:on|off compile Nim and generated files but do not link
--noMain do not generate a main procedure --noMain:on|off do not generate a main procedure
--genScript generate a compile script (in the 'nimcache' --genScript:on|off generate a compile script (in the 'nimcache'
subdirectory named 'compile_$$project$$scriptext'), subdirectory named 'compile_$$project$$scriptext'),
implies --compileOnly 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) --os:SYMBOL set the target operating system (cross-compilation)
--cpu:SYMBOL set the target processor (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 -t, --passC:OPTION pass an option to the C compiler
-l, --passL:OPTION pass an option to the linker -l, --passL:OPTION pass an option to the linker
--cincludes:DIR modify the C compiler header search path --cincludes:DIR modify the C compiler header search path
--clibdir:DIR modify the linker library search path --clibdir:DIR modify the linker library search path
--clib:LIBNAME link an additional C library --clib:LIBNAME link an additional C library
(you should omit platform-specific extensions) (you should omit platform-specific extensions)
--genMapping generate a mapping file containing
(Nim, mangled) identifier pairs
--project document the whole project (doc2) --project document the whole project (doc2)
--docSeeSrcUrl:url activate 'see source' for doc and doc2 commands --docSeeSrcUrl:url activate 'see source' for doc and doc2 commands
(see doc.item.seesrc in config/nimdoc.cfg) (see doc.item.seesrc in config/nimdoc.cfg)
--lineDir:on|off generation of #line directive on|off --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 in the generated output
--threadanalysis:on|off turn thread analysis on|off --threadanalysis:on|off turn thread analysis on|off
--tlsEmulation:on|off turn thread local storage emulation 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 strings is allowed; only for backwards compatibility
--nilseqs:on|off allow 'nil' for strings/seqs for --nilseqs:on|off allow 'nil' for strings/seqs for
backwards compatibility backwards compatibility
--skipCfg do not read the general configuration file --oldast:on|off use old AST for backwards compatibility
--skipUserCfg do not read the user's configuration file --skipCfg:on|off do not read the nim installation's configuration file
--skipParentCfg do not read the parent dirs' configuration files --skipUserCfg:on|off do not read the user's configuration file
--skipProjCfg do not read the project's configuration file --skipParentCfg:on|off do not read the parent dirs' configuration files
--gc:refc|v2|markAndSweep|boehm|go|none|regions --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' select the GC to use; default is 'refc'
--index:on|off turn index file generation on|off --index:on|off turn index file generation on|off
--putenv:key=value set an environment variable --putenv:key=value set an environment variable
@ -98,8 +97,8 @@ Advanced options:
symbol matching is fuzzy so symbol matching is fuzzy so
that --dynlibOverride:lua matches that --dynlibOverride:lua matches
dynlib: "liblua.so.3" dynlib: "liblua.so.3"
--dynlibOverrideAll makes the dynlib pragma have no effect --dynlibOverrideAll:on|off makes the dynlib pragma have no effect
--listCmd list the commands used to execute external programs --listCmd:on|off list the commands used to execute external programs
--parallelBuild:0|1|... perform a parallel build --parallelBuild:0|1|... perform a parallel build
value = number of processors (0 for auto-detect) value = number of processors (0 for auto-detect)
--incremental:on|off only recompile the changed modules (experimental!) --incremental:on|off only recompile the changed modules (experimental!)
@ -108,3 +107,6 @@ Advanced options:
--experimental:$1 --experimental:$1
enable experimental language feature enable experimental language feature
-v, --version show detailed version information -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, (Optionally: Define the value for that symbol,
see: "compile time define pragmas") see: "compile time define pragmas")
-u, --undef:SYMBOL undefine a conditional symbol -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 --stackTrace:on|off turn stack tracing on|off
--lineTrace:on|off turn line tracing on|off --lineTrace:on|off turn line tracing on|off
--threads:on|off turn support for multi-threading 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) --debugger:native|endb use native debugger (gdb) | ENDB (experimental)
--app:console|gui|lib|staticlib --app:console|gui|lib|staticlib
generate a console app|GUI app|DLL|static library 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 --fullhelp show all command line switches
-h, --help show this help -h, --help show this help

View file

@ -6,6 +6,9 @@ The documentation consists of several documents:
- | `Tutorial (part II) <tut2.html>`_ - | `Tutorial (part II) <tut2.html>`_
| The Nim tutorial part two deals with the advanced language constructs. | 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>`_ - | `Language Manual <manual.html>`_
| The Nim manual is a draft that will evolve into a proper specification. | The Nim manual is a draft that will evolve into a proper specification.

View file

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

View file

@ -480,7 +480,6 @@ The type suffixes are:
``'d`` float64 ``'d`` float64
``'f32`` float32 ``'f32`` float32
``'f64`` float64 ``'f64`` float64
``'f128`` float128
================= ========================= ================= =========================
Floating point literals may also be in binary, octal or hexadecimal 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 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: exclude ``nil`` as a valid value with the ``not nil`` annotation:
.. code-block:: nim .. code-block:: nim
@ -1664,12 +1663,12 @@ Nim supports these `calling conventions`:idx:\:
and another one for the pointer to implicitly passed environment. and another one for the pointer to implicitly passed environment.
`stdcall`:idx: `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. procedure is declared with the ``__stdcall`` keyword.
`cdecl`:idx: `cdecl`:idx:
The cdecl convention means that a procedure shall use the same convention 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. the ``__cdecl`` keyword.
`safecall`:idx: `safecall`:idx:
@ -7365,6 +7364,17 @@ Example:
embedsC() 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 For backwards compatibility, if the argument to the ``emit`` statement
is a single string literal, Nim symbols can be referred to via backticks. is a single string literal, Nim symbols can be referred to via backticks.
This usage is however deprecated. This usage is however deprecated.

View file

@ -347,13 +347,16 @@ complete list.
Define Effect Define Effect
====================== ========================================================= ====================== =========================================================
``release`` Turns off runtime checks and turns on the optimizer. ``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 ``useWinAnsi`` Modules like ``os`` and ``osproc`` use the Ansi versions
of the Windows API. The default build uses the Unicode of the Windows API. The default build uses the Unicode
version. version.
``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``. ``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``.
``useNimRtl`` Compile and link against ``nimrtl.dll``. ``useNimRtl`` Compile and link against ``nimrtl.dll``.
``useMalloc`` Makes Nim use C's `malloc`:idx: instead of Nim's ``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. its size to support clearing memory on reallocation.
This only works with ``gc:none``. This only works with ``gc:none``.
``useRealtimeGC`` Enables support of Nim's GC for *soft* realtime ``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 ``$project.nim``. This file can be skipped with the same
``--skipProjCfg`` command line option. ``--skipProjCfg`` command line option.
The VM cannot deal with ``importc`` because the FFI is not Limitations
available. So the stdlib modules using ``importc`` cannot be used with =================================
Nim's VM. However, at least the following modules are available:
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>`_ * `macros <macros.html>`_
* `os <os.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 Look at the module `distros <distros.html>`_ for some support of the
OS's native package managers. OS's native package managers.

104
koch.nim
View file

@ -46,6 +46,8 @@ Possible Commands:
boot [options] bootstraps with given command line options boot [options] bootstraps with given command line options
distrohelper [bindir] helper for distro packagers distrohelper [bindir] helper for distro packagers
tools builds Nim related tools tools builds Nim related tools
toolsNoNimble builds Nim related tools (except nimble)
doesn't require network connectivity
nimble builds the Nimble tool nimble builds the Nimble tool
Boot options: Boot options:
-d:release produce a release version of the compiler -d:release produce a release version of the compiler
@ -56,6 +58,7 @@ Boot options:
for bootstrapping for bootstrapping
Commands for core developers: Commands for core developers:
runCI runs continuous integration (CI), eg from travis
docs [options] generates the full documentation docs [options] generates the full documentation
csource -d:release builds the C sources for installation csource -d:release builds the C sources for installation
pdf builds the PDF documentation pdf builds the PDF documentation
@ -71,6 +74,12 @@ Web options:
build the official docs, use UA-48159761-1 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) = template withDir(dir, body) =
let old = getCurrentDir() let old = getCurrentDir()
try: try:
@ -121,9 +130,6 @@ proc bundleNimbleExe(latest: bool) =
# installer.ini expects it under $nim/bin # installer.ini expects it under $nim/bin
nimCompile("dist/nimble/src/nimble.nim", options = "-d:release --nilseqs:on") nimCompile("dist/nimble/src/nimble.nim", options = "-d:release --nilseqs:on")
proc buildNimfind() =
nimCompile("tools/nimfind.nim", options = "-d:release")
proc buildNimble(latest: bool) = proc buildNimble(latest: bool) =
# old installations created nim/nimblepkg/*.nim files. We remove these # old installations created nim/nimblepkg/*.nim files. We remove these
# here so that it cannot cause problems (nimble bug #306): # here so that it cannot cause problems (nimble bug #306):
@ -197,13 +203,12 @@ proc buildTool(toolname, args: string) =
nimexec("cc $# $#" % [args, toolname]) nimexec("cc $# $#" % [args, toolname])
copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe) copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe)
proc buildTools(latest: bool) = proc buildTools() =
bundleNimsuggest() bundleNimsuggest()
nimCompile("tools/nimgrep.nim", options = "-d:release") nimCompile("tools/nimgrep.nim", options = "-d:release")
when defined(windows): buildVccTool() when defined(windows): buildVccTool()
nimCompile("nimpretty/nimpretty.nim", options = "-d:release") nimCompile("nimpretty/nimpretty.nim", options = "-d:release")
buildNimble(latest) nimCompile("tools/nimfind.nim", options = "-d:release")
buildNimfind()
proc nsis(latest: bool; args: string) = proc nsis(latest: bool; args: string) =
bundleNimbleExe(latest) bundleNimbleExe(latest)
@ -272,16 +277,30 @@ proc boot(args: string) =
var output = "compiler" / "nim".exe var output = "compiler" / "nim".exe
var finalDest = "bin" / "nim".exe var finalDest = "bin" / "nim".exe
# default to use the 'c' command: # default to use the 'c' command:
let defaultCommand = if getEnv("NIM_COMPILE_TO_CPP", "false") == "true": "cpp" else: "c" let useCpp = getEnv("NIM_COMPILE_TO_CPP", "false") == "true"
let bootOptions = if args.len == 0 or args.startsWith("-"): defaultCommand else: ""
let smartNimcache = (if "release" in args: "nimcache/r_" else: "nimcache/d_") & let smartNimcache = (if "release" in args: "nimcache/r_" else: "nimcache/d_") &
hostOs & "_" & hostCpu hostOs & "_" & hostCpu
copyExe(findStartNim(), 0.thVersion) let nimStart = findStartNim()
copyExe(nimStart, 0.thVersion)
for i in 0..2: 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 echo "iteration: ", i+1
exec i.thVersion & " $# $# --nimcache:$# compiler" / "nim.nim" % [bootOptions, args, var extraOption = ""
smartNimcache] 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): if sameFileContent(output, i.thVersion):
copyExe(output, finalDest) copyExe(output, finalDest)
echo "executables are equal: SUCCESS!" echo "executables are equal: SUCCESS!"
@ -352,8 +371,8 @@ proc winReleaseArch(arch: string) =
# Rebuilding koch is necessary because it uses its pointer size to # Rebuilding koch is necessary because it uses its pointer size to
# determine which mingw link to put in the NSIS installer. # determine which mingw link to put in the NSIS installer.
nimexec "c --cpu:$# koch" % cpu nimexec "c --cpu:$# koch" % cpu
exec "koch boot -d:release --cpu:$#" % cpu kochExec "boot -d:release --cpu:$#" % cpu
exec "koch --latest zip -d:release" kochExec "--latest zip -d:release"
overwriteFile r"build\nim-$#.zip" % VersionAsString, overwriteFile r"build\nim-$#.zip" % VersionAsString,
r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch] r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch]
@ -428,6 +447,47 @@ proc xtemp(cmd: string) =
finally: finally:
copyExe(d / "bin" / "nim_backup".exe, d / "bin" / "nim".exe) 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() = proc pushCsources() =
if not dirExists("../csources/.git"): if not dirExists("../csources/.git"):
quit "[Error] no csources git repository found" quit "[Error] no csources git repository found"
@ -475,8 +535,7 @@ proc testUnixInstall(cmdLineRest: string) =
execCleanPath("./koch --latest tools") execCleanPath("./koch --latest tools")
# check the tests work: # check the tests work:
putEnv("NIM_EXE_NOT_IN_PATH", "NOT_IN_PATH") putEnv("NIM_EXE_NOT_IN_PATH", "NOT_IN_PATH")
execCleanPath("./koch tests", destDir / "bin") execCleanPath("./koch tests cat megatest", destDir / "bin")
#execCleanPath("./koch tests cat newconfig", destDir / "bin")
else: else:
echo "Version check: failure" echo "Version check: failure"
finally: finally:
@ -512,6 +571,10 @@ when isMainModule:
var op = initOptParser() var op = initOptParser()
var latest = false var latest = false
var stable = false var stable = false
template isLatest(): bool =
if stable: false
else:
existsDir(".git") or latest
while true: while true:
op.next() op.next()
case op.kind case op.kind
@ -537,17 +600,18 @@ when isMainModule:
of "distrohelper": geninstall() of "distrohelper": geninstall()
of "install": install(op.cmdLineRest) of "install": install(op.cmdLineRest)
of "testinstall": testUnixInstall(op.cmdLineRest) of "testinstall": testUnixInstall(op.cmdLineRest)
of "runci": runCI(op.cmdLineRest)
of "test", "tests": tests(op.cmdLineRest) of "test", "tests": tests(op.cmdLineRest)
of "temp": temp(op.cmdLineRest) of "temp": temp(op.cmdLineRest)
of "xtemp": xtemp(op.cmdLineRest) of "xtemp": xtemp(op.cmdLineRest)
of "wintools": bundleWinTools() of "wintools": bundleWinTools()
of "nimble": of "nimble": buildNimble(isLatest())
if stable: buildNimble(false)
else: buildNimble(existsDir(".git") or latest)
of "nimsuggest": bundleNimsuggest() of "nimsuggest": bundleNimsuggest()
of "toolsnonimble":
buildTools()
of "tools": of "tools":
if stable: buildTools(false) buildTools()
else: buildTools(existsDir(".git") or latest) buildNimble(isLatest())
of "pushcsource", "pushcsources": pushCsources() of "pushcsource", "pushcsources": pushCsources()
of "valgrind": valgrind(op.cmdLineRest) of "valgrind": valgrind(op.cmdLineRest)
else: showHelp() 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.} proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated.}
## compares two Nim symbols ## 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.} = proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} =
@ -377,7 +377,9 @@ proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.}
proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.} proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.}
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.} 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.} proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.}
## writes a warning message at compile time ## writes a warning message at compile time
@ -1402,8 +1404,14 @@ proc customPragmaNode(n: NimNode): NimNode =
let impl = n.getImpl() let impl = n.getImpl()
if impl.kind in RoutineNodes: if impl.kind in RoutineNodes:
return impl.pragma return impl.pragma
elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr:
return impl[0][1]
else: 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}: if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}:
let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1]) 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) let pragmaNode = customPragmaNode(n)
for p in pragmaNode: for p in pragmaNode:
if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp: if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp:
return p[1] if p.len == 2:
result = 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, 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): 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.len = b.len
a.p = b.p 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 type
PayloadBase = object 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. oneSecond: BiggestInt # Bytes transferred in one second.
lastProgressReport: float # Time lastProgressReport: float # Time
toStore: string # Data left to upload (Only used with async) toStore: string # Data left to upload (Only used with async)
else: nil
FtpClientObj* = FtpBaseObj[Socket] FtpClientObj* = FtpBaseObj[Socket]
FtpClient* = ref FtpClientObj 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] = proc diffInt*(arrayA, arrayB: openArray[int]): seq[Item] =
## Find the difference in 2 arrays of integers. ## Find the difference in 2 arrays of integers.
##
## ``arrayA`` A-version of the numbers (usualy the old one) ## ``arrayA`` A-version of the numbers (usualy the old one)
##
## ``arrayB`` B-version of the numbers (usualy the new one) ## ``arrayB`` B-version of the numbers (usualy the new one)
##
## Returns a array of Items that describe the differences. ## Returns a array of Items that describe the differences.
# The A-Version of the data (original data) to be compared. # 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] = proc diffText*(textA, textB: string): seq[Item] =
## Find the difference in 2 text documents, comparing by textlines. ## 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 ## 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 ## 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 ## textlines into a common hashtable so i can find dublicates in there, and generating a
## new number each time a new textline is inserted. ## 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) ## ``textA`` A-version of the text (usually the old 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. ## ``textB`` B-version of the text (usually the new one)
## ``ignoreCase`` When set to true, all characters are converted to their lowercase equivivalence before the comparation is done. ##
## Returns a seq of Items that describe the differences. ## Returns a seq of Items that describe the differences.
# prepare the input-text and convert to comparable numbers. # 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) 511.TSqlSmallInt, retSz.addr.PSQLSMALLINT)
except: except:
discard discard
return (res.int, $sqlState, $nativeErr, $errMsg) return (res.int, $(addr sqlState), $(addr nativeErr), $(addr errMsg))
proc dbError*(db: var DbConn) {. proc dbError*(db: var DbConn) {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} = tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =

View file

@ -166,10 +166,12 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
var stmt = setupQuery(db, query, args) var stmt = setupQuery(db, query, args)
var L = (column_count(stmt)) var L = (column_count(stmt))
var result = newRow(L) var result = newRow(L)
while step(stmt) == SQLITE_ROW: try:
setRow(stmt, result, L) while step(stmt) == SQLITE_ROW:
yield result setRow(stmt, result, L)
if finalize(stmt) != SQLITE_OK: dbError(db) yield result
finally:
if finalize(stmt) != SQLITE_OK: dbError(db)
iterator instantRows*(db: DbConn, query: SqlQuery, iterator instantRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): InstantRow 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 ## 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. ## on demand using []. Returned handle is valid only within the iterator body.
var stmt = setupQuery(db, query, args) var stmt = setupQuery(db, query, args)
while step(stmt) == SQLITE_ROW: try:
yield stmt while step(stmt) == SQLITE_ROW:
if finalize(stmt) != SQLITE_OK: dbError(db) yield stmt
finally:
if finalize(stmt) != SQLITE_OK: dbError(db)
proc toTypeKind(t: var DbType; x: int32) = proc toTypeKind(t: var DbType; x: int32) =
case x 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. ## on demand using []. Returned handle is valid only within the iterator body.
var stmt = setupQuery(db, query, args) var stmt = setupQuery(db, query, args)
setColumns(columns, stmt) setColumns(columns, stmt)
while step(stmt) == SQLITE_ROW: try:
yield stmt while step(stmt) == SQLITE_ROW:
if finalize(stmt) != SQLITE_OK: dbError(db) yield stmt
finally:
if finalize(stmt) != SQLITE_OK: dbError(db)
proc `[]`*(row: InstantRow, col: int32): string {.inline.} = proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
## returns text for given column of the row ## 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) raise RegexInternalError(msg : "Unknown internal error: " & $execRet)
proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] = 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. ## string.
## ##
runnableExamples: 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) return str.matchImpl(pattern, start, endpos, pcre.ANCHORED)
iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): RegexMatch = 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 ## non-overlapping match. ``"2222".find(re"22")`` is ``"22", "22"``, not
## ``"22", "22", "22"``. ## ``"22", "22", "22"``.
## ##
## Arguments are the same as ```find(...)`` <#proc-find>`_ ## Arguments are the same as ` ``find(...)`` <#proc-find>`_
## ##
## Variants: ## 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 ## Splits the string with the given regex. This works according to the
## rules that Perl and Javascript use. ## 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: runnableExamples:
# - If the match is zero-width, then the string is still split: # - 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("\\x")
result.add(toHex(ord(c), 2)) 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: when isMainModule:
doAssert match("(a b c)", rex"\( .* \)") doAssert match("(a b c)", rex"\( .* \)")
doAssert match("WHiLe", re("while", {reIgnoreCase})) doAssert match("WHiLe", re("while", {reIgnoreCase}))
@ -595,7 +570,7 @@ when isMainModule:
doAssert "ABC".match(rex"\d+ | \w+") doAssert "ABC".match(rex"\d+ | \w+")
{.push warnings:off.} {.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.} {.pop.}
var pattern = re"[a-z0-9]+\s*=\s*[a-z0-9]+" 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

@ -459,7 +459,6 @@ proc dispatchEvent*(et: EventTarget, ev: Event)
proc alert*(w: Window, msg: cstring) proc alert*(w: Window, msg: cstring)
proc back*(w: Window) proc back*(w: Window)
proc blur*(w: Window) proc blur*(w: Window)
proc captureEvents*(w: Window, eventMask: int) {.deprecated.}
proc clearInterval*(w: Window, interval: ref TInterval) proc clearInterval*(w: Window, interval: ref TInterval)
proc clearTimeout*(w: Window, timeout: ref TTimeOut) proc clearTimeout*(w: Window, timeout: ref TTimeOut)
proc close*(w: Window) proc close*(w: Window)
@ -478,7 +477,6 @@ proc open*(w: Window, uri, windowname: cstring,
properties: cstring = nil): Window properties: cstring = nil): Window
proc print*(w: Window) proc print*(w: Window)
proc prompt*(w: Window, text, default: cstring): cstring proc prompt*(w: Window, text, default: cstring): cstring
proc releaseEvents*(w: Window, eventMask: int) {.deprecated.}
proc resizeBy*(w: Window, x, y: int) proc resizeBy*(w: Window, x, y: int)
proc resizeTo*(w: Window, x, y: int) proc resizeTo*(w: Window, x, y: int)
proc routeEvent*(w: Window, event: Event) proc routeEvent*(w: Window, event: Event)
@ -513,7 +511,6 @@ proc setAttribute*(n: Node, name, value: cstring)
proc setAttributeNode*(n: Node, attr: Node) proc setAttributeNode*(n: Node, attr: Node)
# Document "methods" # Document "methods"
proc captureEvents*(d: Document, eventMask: int) {.deprecated.}
proc createAttribute*(d: Document, identifier: cstring): Node proc createAttribute*(d: Document, identifier: cstring): Node
proc createElement*(d: Document, identifier: cstring): Element proc createElement*(d: Document, identifier: cstring): Element
proc createTextNode*(d: Document, identifier: cstring): Node 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 getSelection*(d: Document): cstring
proc handleEvent*(d: Document, event: Event) proc handleEvent*(d: Document, event: Event)
proc open*(d: Document) proc open*(d: Document)
proc releaseEvents*(d: Document, eventMask: int) {.deprecated.}
proc routeEvent*(d: Document, event: Event) proc routeEvent*(d: Document, event: Event)
proc write*(d: Document, text: cstring) proc write*(d: Document, text: cstring)
proc writeln*(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 parseInt*(s: cstring, radix: int):int {.importc, nodecl.}
proc newEvent*(name: cstring): Event {.importcpp: "new Event(@)", constructor.} 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 {. proc newDate*(): DateTime {.
importcpp: "new Date()".} importcpp: "new Date()".}
proc newDate*(date: int|string): DateTime {. proc newDate*(date: int|int64|string): DateTime {.
importcpp: "new Date(#)".} importcpp: "new Date(#)".}
proc newDate*(year, month, day, hours, minutes, proc newDate*(year, month, day, hours, minutes,
@ -90,6 +90,16 @@ proc getSeconds*(d: DateTime): int {.importcpp.}
proc getYear*(d: DateTime): int {.importcpp.} proc getYear*(d: DateTime): int {.importcpp.}
proc getTime*(d: DateTime): int {.importcpp.} proc getTime*(d: DateTime): int {.importcpp.}
proc toString*(d: DateTime): cstring {.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 #JSON library
proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.} proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.}

View file

@ -104,6 +104,12 @@ var
jsFilename* {.importc: "__filename", nodecl.}: cstring jsFilename* {.importc: "__filename", nodecl.}: cstring
## JavaScript's __filename pseudo-variable ## 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 # Exceptions
type type
JsError* {.importc: "Error".} = object of JsRoot JsError* {.importc: "Error".} = object of JsRoot

View file

@ -166,13 +166,13 @@ __clang__
# define N_STDCALL(rettype, name) rettype __stdcall name # define N_STDCALL(rettype, name) rettype __stdcall name
# define N_SYSCALL(rettype, name) rettype __syscall name # define N_SYSCALL(rettype, name) rettype __syscall name
# define N_FASTCALL(rettype, name) rettype __fastcall 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: */ /* function pointers with calling convention: */
# define N_CDECL_PTR(rettype, name) rettype (__cdecl *name) # define N_CDECL_PTR(rettype, name) rettype (__cdecl *name)
# define N_STDCALL_PTR(rettype, name) rettype (__stdcall *name) # define N_STDCALL_PTR(rettype, name) rettype (__stdcall *name)
# define N_SYSCALL_PTR(rettype, name) rettype (__syscall *name) # define N_SYSCALL_PTR(rettype, name) rettype (__syscall *name)
# define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *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 # ifdef __cplusplus
# define N_LIB_EXPORT extern "C" __declspec(dllexport) # 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 == "#") result = (p.tok[j].kind == tkWord) or (p.tok[j].symbol == "#")
if result: if result:
case p.tok[j].symbol[0] 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'}) of '0'..'9': result = allCharsInSet(p.tok[j].symbol, {'0'..'9'})
else: discard else: result = false
else: else:
var c = expr[i] var c = expr[i]
var length = 0 var length = 0
@ -780,6 +780,31 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
add(result, nil) add(result, nil)
add(result, lb) 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) = proc parseInline(p: var RstParser, father: PRstNode) =
case p.tok[p.idx].kind case p.tok[p.idx].kind
of tkPunct: of tkPunct:
@ -811,6 +836,9 @@ proc parseInline(p: var RstParser, father: PRstNode) =
var n = newRstNode(rnSubstitutionReferences) var n = newRstNode(rnSubstitutionReferences)
parseUntil(p, n, "|", false) parseUntil(p, n, "|", false)
add(father, n) 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: else:
if roSupportSmilies in p.s.options: if roSupportSmilies in p.s.options:
let n = parseSmiley(p) 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 result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or
match(p, p.idx, "/w") or match(p, p.idx, "//w") 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 = proc whichSection(p: RstParser): RstNodeKind =
case p.tok[p.idx].kind case p.tok[p.idx].kind
of tkAdornment: of tkAdornment:
if match(p, p.idx + 1, "ii"): result = rnTransition if match(p, p.idx + 1, "ii"): result = rnTransition
elif match(p, p.idx + 1, " a"): result = rnTable elif match(p, p.idx + 1, " a"): result = rnTable
elif match(p, p.idx + 1, "i"): result = rnOverline elif match(p, p.idx + 1, "i"): result = rnOverline
else: result = rnLeaf elif isMarkdownHeadline(p):
result = rnHeadline
else:
result = rnLeaf
of tkPunct: of tkPunct:
if match(p, tokenAfterNewline(p), "ai"): if isMarkdownHeadline(p):
result = rnHeadline
elif match(p, tokenAfterNewline(p), "ai"):
result = rnHeadline result = rnHeadline
elif p.tok[p.idx].symbol == "::": elif p.tok[p.idx].symbol == "::":
result = rnLiteralBlock result = rnLiteralBlock
@ -1060,7 +1105,7 @@ proc whichSection(p: RstParser): RstNodeKind =
elif match(p, p.idx, ":w:") and predNL(p): elif match(p, p.idx, ":w:") and predNL(p):
# (p.tok[p.idx].symbol == ":") # (p.tok[p.idx].symbol == ":")
result = rnFieldList result = rnFieldList
elif match(p, p.idx, "(e) "): elif match(p, p.idx, "(e) ") or match(p, p.idx, "e. "):
result = rnEnumList result = rnEnumList
elif match(p, p.idx, "+a+"): elif match(p, p.idx, "+a+"):
result = rnGridTable result = rnGridTable
@ -1130,12 +1175,18 @@ proc parseParagraph(p: var RstParser, result: PRstNode) =
proc parseHeadline(p: var RstParser): PRstNode = proc parseHeadline(p: var RstParser): PRstNode =
result = newRstNode(rnHeadline) result = newRstNode(rnHeadline)
parseUntilNewline(p, result) if isMarkdownHeadline(p):
assert(p.tok[p.idx].kind == tkIndent) result.level = p.tok[p.idx].symbol.len
assert(p.tok[p.idx + 1].kind == tkAdornment) assert(p.tok[p.idx+1].kind == tkWhite)
var c = p.tok[p.idx + 1].symbol[0] inc p.idx, 2
inc(p.idx, 2) parseUntilNewline(p, result)
result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c) 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 type
IntSeq = seq[int] IntSeq = seq[int]

View file

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

View file

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

View file

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

View file

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

View file

@ -9,37 +9,49 @@
## This module implements a base64 encoder and decoder. ## 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 ## Encoding data
## ------------- ## -------------
## ##
## In order to encode some text simply call the ``encode`` procedure: ## .. code-block::nim
## ## import base64
## .. code-block::nim ## let encoded = encode("Hello World")
## import base64 ## assert encoded == "SGVsbG8gV29ybGQ="
## let encoded = encode("Hello World")
## echo(encoded) # SGVsbG8gV29ybGQ=
## ##
## Apart from strings you can also encode lists of integers or characters: ## Apart from strings you can also encode lists of integers or characters:
## ##
## .. code-block::nim ## .. code-block::nim
## import base64 ## import base64
## let encodedInts = encode([1,2,3]) ## let encodedInts = encode([1,2,3])
## echo(encodedInts) # AQID ## assert encodedInts == "AQID"
## let encodedChars = encode(['h','e','y']) ## let encodedChars = encode(['h','e','y'])
## echo(encodedChars) # aGV5 ## assert encodedChars == "aGV5"
## ##
## The ``encode`` procedure takes an ``openarray`` so both arrays and sequences
## can be passed as parameters.
## ##
## Decoding data ## Decoding data
## ------------- ## -------------
## ##
## To decode a base64 encoded data string simply call the ``decode`` ## .. code-block::nim
## procedure: ## import base64
## let decoded = decode("SGVsbG8gV29ybGQ=")
## assert decoded == "Hello World"
## ##
## .. code-block::nim ##
## import base64 ## See also
## echo(decode("SGVsbG8gV29ybGQ=")) # Hello World ## ========
##
## * `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 const
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
@ -100,18 +112,33 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
discard discard
proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string = proc encode*[T:SomeInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
## encodes `s` into base64 representation. After `lineLen` characters, a ## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
## `newline` is added. ## ``newline`` is added.
## ##
## This procedure encodes an openarray (array or sequence) of either integers ## This procedure encodes an openarray (array or sequence) of either integers
## or characters. ## 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) encodeInternal(s, lineLen, newLine)
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string = proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
## encodes `s` into base64 representation. After `lineLen` characters, a ## Encodes ``s`` into base64 representation. After ``lineLen`` characters, a
## `newline` is added. ## ``newline`` is added.
## ##
## This procedure encodes a string. ## 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) encodeInternal(s, lineLen, newLine)
proc decodeByte(b: char): int {.inline.} = proc decodeByte(b: char): int {.inline.} =
@ -123,8 +150,15 @@ proc decodeByte(b: char): int {.inline.} =
else: result = 63 else: result = 63
proc decode*(s: string): string = proc decode*(s: string): string =
## decodes a string in base64 representation back into its original form. ## Decodes string ``s`` in base64 representation back into its original form.
## Whitespace is skipped. ## 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'} const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
var total = ((len(s) + 3) div 4) * 3 var total = ((len(s) + 3) div 4) * 3
# total is an upper bound, as we will skip arbitrary whitespace: # 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. ## Makes Nim output stacktraces to stdout, instead of server log.
errorMessageWriter = writeErrorMessage 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) = proc setCookie*(name, value: string) =
## Sets a cookie. ## Sets a cookie.
write(stdout, "Set-Cookie: ", name, "=", value, "\n") 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. ## If `key` is not in `t`, the ``KeyError`` exception is raised.
get(c, key) 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] = iterator leaves[T](n: Node[T]): Node[T] =
if n != nil: if n != nil:
# XXX actually we could compute the necessary stack size in advance: # 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. ## access, unless your program logic guarantees it indirectly.
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## proc foo(a, b: Positive) = # assume random positive values for `a` and `b` ## import deques
## var deq = initDeque[int]() # initializes the object
## for i in 1 ..< a: deq.addLast i # populates the deque
## ##
## if b < deq.len: # checking before indexed access ## var a = initDeque[int]()
## echo "The element at index position ", b, " is ", deq[b]
## ##
## # The following two lines don't need any checking on access due to the ## doAssertRaises(IndexError, echo a[0])
## # logic of the program, but that would not be the case if `a` could be 0.
## assert deq.peekFirst == 1
## assert deq.peekLast == a
## ##
## while deq.len > 0: # checking if the deque is empty ## for i in 1 .. 5:
## echo deq.popLast() ## a.addLast(10*i)
## assert $a == "[10, 20, 30, 40, 50]"
## ##
## Note: For inter thread communication use ## assert a.peekFirst == 10
## a `Channel <channels.html>`_ instead. ## 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 import math, typetraits
type type
Deque*[T] = object Deque*[T] = object
## A double-ended queue backed with a ringed seq buffer. ## A double-ended queue backed with a ringed seq buffer.
##
## To initialize an empty deque use `initDeque proc <#initDeque,int>`_.
data: seq[T] data: seq[T]
head, tail, count, mask: int head, tail, count, mask: int
proc initDeque*[T](initialSize: int = 4): Deque[T] = proc initDeque*[T](initialSize: int = 4): Deque[T] =
## Create a new deque. ## Create a new empty 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.
## ##
## `initialSize` needs to be a power of two. If you need to accept runtime ## Optionally, the initial capacity can be reserved via `initialSize`
## values for this you could use the ``nextPowerOfTwo`` proc from the ## as a performance optimization.
## `math <math.html>`_ module. ## 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) assert isPowerOfTwo(initialSize)
result.mask = initialSize-1 result.mask = initialSize-1
newSeq(result.data, initialSize) 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 if unlikely(i >= deq.count): # x < deq.low is taken care by the Natural parameter
raise newException(IndexError, raise newException(IndexError,
"Out of bounds: " & $i & " > " & $(deq.count - 1)) "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.} = proc `[]`*[T](deq: Deque[T], i: Natural) : T {.inline.} =
## Access the i-th element of `deq` by order from first to last. ## Access the i-th element of `deq`.
## deq[0] is the first, deq[^1] is the last. 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) xBoundsCheck(deq, i)
return deq.data[(deq.head + i) and deq.mask] return deq.data[(deq.head + i) and deq.mask]
proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} = proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} =
## Access the i-th element of `deq` and returns a mutable ## Access the i-th element of `deq` and return a mutable
## reference to it. ## 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) xBoundsCheck(deq, i)
return deq.data[(deq.head + i) and deq.mask] 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`. ## 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) xBoundsCheck(deq, i)
deq.data[(deq.head + i) and deq.mask] = val 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 = iterator items*[T](deq: Deque[T]): T =
## Yield every element of `deq`. ## 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 var i = deq.head
for c in 0 ..< deq.count: for c in 0 ..< deq.count:
yield deq.data[i] yield deq.data[i]
i = (i + 1) and deq.mask i = (i + 1) and deq.mask
iterator mitems*[T](deq: var Deque[T]): var T = 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 var i = deq.head
for c in 0 ..< deq.count: for c in 0 ..< deq.count:
yield deq.data[i] 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] = iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] =
## Yield every (position, value) of `deq`. ## 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 var i = deq.head
for c in 0 ..< deq.count: for c in 0 ..< deq.count:
yield (c, deq.data[i]) yield (c, deq.data[i])
i = (i + 1) and deq.mask i = (i + 1) and deq.mask
proc contains*[T](deq: Deque[T], item: T): bool {.inline.} = proc contains*[T](deq: Deque[T], item: T): bool {.inline.} =
## Return true if `item` is in `deq` or false if not found. Usually used ## Return true if `item` is in `deq` or false if not found.
## via the ``in`` operator. It is the equivalent of ``deq.find(item) >= 0``. ##
## Usually used via the ``in`` operator.
## It is the equivalent of ``deq.find(item) >= 0``.
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## if x in q: ## if x in q:
## assert q.contains x ## assert q.contains(x)
for e in deq: for e in deq:
if e == item: return true if e == item: return true
return false return false
@ -138,6 +268,19 @@ proc expandIfNeeded[T](deq: var Deque[T]) =
proc addFirst*[T](deq: var Deque[T], item: T) = proc addFirst*[T](deq: var Deque[T], item: T) =
## Add an `item` to the beginning of the `deq`. ## 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) expandIfNeeded(deq)
inc deq.count inc deq.count
deq.head = (deq.head - 1) and deq.mask 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) = proc addLast*[T](deq: var Deque[T], item: T) =
## Add an `item` to the end of the `deq`. ## 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) expandIfNeeded(deq)
inc deq.count inc deq.count
deq.data[deq.tail] = item 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.}= proc peekFirst*[T](deq: Deque[T]): T {.inline.}=
## Returns the first element of `deq`, but does not remove it from the deque. ## 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) emptyCheck(deq)
result = deq.data[deq.head] result = deq.data[deq.head]
proc peekLast*[T](deq: Deque[T]): T {.inline.} = proc peekLast*[T](deq: Deque[T]): T {.inline.} =
## Returns the last element of `deq`, but does not remove it from the deque. ## 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) emptyCheck(deq)
result = deq.data[(deq.tail - 1) and deq.mask] 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.} = proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
## Remove and returns the first element of the `deq`. ## 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) emptyCheck(deq)
dec deq.count dec deq.count
result = deq.data[deq.head] 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.} = proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
## Remove and returns the last element of the `deq`. ## 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) emptyCheck(deq)
dec deq.count dec deq.count
deq.tail = (deq.tail - 1) and deq.mask 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.} = proc clear*[T](deq: var Deque[T]) {.inline.} =
## Resets the deque so that it is empty. ## 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) for el in mitems(deq): destroy(el)
deq.count = 0 deq.count = 0
deq.tail = deq.head deq.tail = deq.head
proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) = proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
## Remove `fromFirst` elements from the front of the deque and ## Remove `fromFirst` elements from the front of the deque and
## `fromLast` elements from the back. If the supplied number of ## `fromLast` elements from the back.
## elements exceeds the total number of elements in the deque,
## the deque will remain empty.
## ##
## 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: if fromFirst + fromLast > deq.count:
clear(deq) clear(deq)
return return
@ -214,6 +456,8 @@ proc `$`*[T](deq: Deque[T]): string =
result.addQuoted(x) result.addQuoted(x)
result.add("]") result.add("]")
when isMainModule: when isMainModule:
var deq = initDeque[int](1) var deq = initDeque[int](1)
deq.addLast(4) deq.addLast(4)

View file

@ -9,9 +9,12 @@
## The ``intsets`` module implements an efficient int set implemented as a ## The ``intsets`` module implements an efficient int set implemented as a
## `sparse bit set`:idx:. ## `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 ## **Note**: Currently the assignment operator ``=`` for ``intsets``
## copy; use ``assign`` to get a deep copy. ## 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 import
hashes, math hashes, math
@ -94,7 +97,7 @@ proc intSetPut(t: var IntSet, key: int): PTrunk =
t.data[h] = result t.data[h] = result
proc contains*(s: IntSet, key: int): bool = 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: if s.elems <= s.a.len:
for i in 0..<s.elems: for i in 0..<s.elems:
if s.a[i] == key: return true if s.a[i] == key: return true
@ -107,7 +110,7 @@ proc contains*(s: IntSet, key: int): bool =
result = false result = false
iterator items*(s: IntSet): int {.inline.} = 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: if s.elems <= s.a.len:
for i in 0..<s.elems: for i in 0..<s.elems:
yield s.a[i] yield s.a[i]
@ -135,7 +138,7 @@ proc bitincl(s: var IntSet, key: int) {.inline.} =
`shl`(1, u and IntMask) `shl`(1, u and IntMask)
proc incl*(s: var IntSet, key: int) = 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: if s.elems <= s.a.len:
for i in 0..<s.elems: for i in 0..<s.elems:
if s.a[i] == key: return if s.a[i] == key: return
@ -170,7 +173,7 @@ proc exclImpl(s: var IntSet, key: int) =
not `shl`(1, u and IntMask) not `shl`(1, u and IntMask)
proc excl*(s: var IntSet, key: int) = proc excl*(s: var IntSet, key: int) =
## excludes `key` from the set `s`. ## Excludes `key` from the set `s`.
exclImpl(s, key) exclImpl(s, key)
proc excl*(s: var IntSet, other: IntSet) = 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) for item in other: excl(s, item)
proc missingOrExcl*(s: var IntSet, key: int) : bool = 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. ## `key` is removed from `s` and false is returned.
var count = s.elems var count = s.elems
exclImpl(s, key) exclImpl(s, key)
result = count == s.elems result = count == s.elems
proc containsOrIncl*(s: var IntSet, key: int): bool = 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. ## and false is returned.
if s.elems <= s.a.len: if s.elems <= s.a.len:
for i in 0..<s.elems: for i in 0..<s.elems:
@ -206,23 +209,28 @@ proc containsOrIncl*(s: var IntSet, key: int): bool =
result = false result = false
proc initIntSet*: IntSet = 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) # newSeq(result.data, InitIntSetSize)
#result.max = InitIntSetSize-1 # result.max = InitIntSetSize-1
when defined(nimNoNilSeqs): result = IntSet(
result.data = @[] elems: 0,
else: counter: 0,
result.data = nil max: 0,
result.max = 0 head: nil,
result.counter = 0 data: when defined(nimNoNilSeqs): @[] else: nil)
result.head = nil # a: array[0..33, int] # profiling shows that 34 elements are enough
result.elems = 0
proc clear*(result: var IntSet) = proc clear*(result: var IntSet) =
#setLen(result.data, InitIntSetSize) ## Clears the IntSet back to an empty state.
#for i in 0..InitIntSetSize-1: result.data[i] = nil
#result.max = InitIntSetSize-1 # setLen(result.data, InitIntSetSize)
# for i in 0..InitIntSetSize-1: result.data[i] = nil
# result.max = InitIntSetSize-1
when defined(nimNoNilSeqs): when defined(nimNoNilSeqs):
result.data = @[] result.data = @[]
else: else:
@ -304,7 +312,7 @@ proc `-`*(s1, s2: IntSet): IntSet {.inline.} =
result = difference(s1, s2) result = difference(s1, s2)
proc disjoint*(s1, s2: IntSet): bool = 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: for item in s1:
if contains(s2, item): if contains(s2, item):
return false return false
@ -320,7 +328,7 @@ proc len*(s: IntSet): int {.inline.} =
inc(result) inc(result)
proc card*(s: IntSet): int {.inline.} = proc card*(s: IntSet): int {.inline.} =
## alias for `len() <#len>` _. ## Alias for `len() <#len>` _.
result = s.len() result = s.len()
proc `<=`*(s1, s2: IntSet): bool = proc `<=`*(s1, s2: IntSet): bool =
@ -349,12 +357,6 @@ proc `$`*(s: IntSet): string =
## The `$` operator for int sets. ## The `$` operator for int sets.
dollarImpl() 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: when isMainModule:
import sequtils, algorithm import sequtils, algorithm

View file

@ -7,34 +7,112 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## Implementation of singly and doubly linked lists. Because it makes no sense ## Implementation of:
## to do so, the 'next' and 'prev' pointers are not hidden from you and can ## * `singly linked lists <#SinglyLinkedList>`_
## be manipulated directly for efficiency. ## * `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): when not defined(nimhygiene):
{.pragma: dirty.} {.pragma: dirty.}
type 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] next*, prev*: ref DoublyLinkedNodeObj[T]
value*: T value*: T
DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[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] next*: ref SinglyLinkedNodeObj[T]
value*: T value*: T
SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[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] 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] 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] 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] head*: DoublyLinkedNode[T]
SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T] SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T]
@ -46,28 +124,44 @@ type
SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T] SomeLinkedNode*[T] = SinglyLinkedNode[T] | DoublyLinkedNode[T]
proc initSinglyLinkedList*[T](): SinglyLinkedList[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 discard
proc initDoublyLinkedList*[T](): DoublyLinkedList[T] = 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 discard
proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] = 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 discard
proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] = 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 discard
proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] = 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) new(result)
result.value = value result.value = value
proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] = 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) new(result)
result.value = value result.value = value
@ -86,24 +180,100 @@ template itemsRingImpl() {.dirty.} =
if it == L.head: break if it == L.head: break
iterator items*[T](L: SomeLinkedList[T]): T = 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() itemsListImpl()
iterator items*[T](L: SomeLinkedRing[T]): T = 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() itemsRingImpl()
iterator mitems*[T](L: var SomeLinkedList[T]): var T = 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() itemsListImpl()
iterator mitems*[T](L: var SomeLinkedRing[T]): var T = 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() itemsRingImpl()
iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] = 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. ## 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 var it = L.head
while it != nil: while it != nil:
var nxt = it.next var nxt = it.next
@ -111,8 +281,24 @@ iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
it = nxt it = nxt
iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] = 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. ## 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 var it = L.head
if it != nil: if it != nil:
while true: while true:
@ -122,7 +308,7 @@ iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
if it == L.head: break if it == L.head: break
proc `$`*[T](L: SomeLinkedCollection[T]): string = 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 = "[" result = "["
for x in nodes(L): for x in nodes(L):
if result.len > 1: result.add(", ") if result.len > 1: result.add(", ")
@ -130,19 +316,54 @@ proc `$`*[T](L: SomeLinkedCollection[T]): string =
result.add("]") result.add("]")
proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] = 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. ## 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): for x in nodes(L):
if x.value == value: return x if x.value == value: return x
proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} = proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} =
## searches in the list for a value. Returns false if the value does not ## Searches in the list for a value. Returns `false` if the value does not
## exist, true otherwise. ## 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 result = find(L, value) != nil
proc append*[T](L: var SinglyLinkedList[T], proc append*[T](L: var SinglyLinkedList[T],
n: SinglyLinkedNode[T]) {.inline.} = 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 n.next = nil
if L.tail != nil: if L.tail != nil:
assert(L.tail.next == nil) assert(L.tail.next == nil)
@ -151,22 +372,75 @@ proc append*[T](L: var SinglyLinkedList[T],
if L.head == nil: L.head = n if L.head == nil: L.head = n
proc append*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = 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)) append(L, newSinglyLinkedNode(value))
proc prepend*[T](L: var SinglyLinkedList[T], proc prepend*[T](L: var SinglyLinkedList[T],
n: SinglyLinkedNode[T]) {.inline.} = 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 n.next = L.head
L.head = n L.head = n
if L.tail == nil: L.tail = n if L.tail == nil: L.tail = n
proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = 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)) prepend(L, newSinglyLinkedNode(value))
proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = 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.next = nil
n.prev = L.tail n.prev = L.tail
if L.tail != nil: 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 if L.head == nil: L.head = n
proc append*[T](L: var DoublyLinkedList[T], value: T) = 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)) append(L, newDoublyLinkedNode(value))
proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = 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.prev = nil
n.next = L.head n.next = L.head
if L.head != nil: 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 if L.tail == nil: L.tail = n
proc prepend*[T](L: var DoublyLinkedList[T], value: T) = 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)) prepend(L, newDoublyLinkedNode(value))
proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = 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.tail: L.tail = n.prev
if n == L.head: L.head = n.next if n == L.head: L.head = n.next
if n.next != nil: n.next.prev = n.prev if n.next != nil: n.next.prev = n.prev
if n.prev != nil: n.prev.next = n.next if n.prev != nil: n.prev.next = n.next
proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = 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: if L.head != nil:
n.next = L.head n.next = L.head
assert(L.tail != nil) assert(L.tail != nil)
@ -213,11 +554,36 @@ proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
L.tail = n L.tail = n
proc append*[T](L: var SinglyLinkedRing[T], value: T) = 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)) append(L, newSinglyLinkedNode(value))
proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = 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: if L.head != nil:
n.next = L.head n.next = L.head
assert(L.tail != nil) assert(L.tail != nil)
@ -228,11 +594,40 @@ proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) =
L.head = n L.head = n
proc prepend*[T](L: var SinglyLinkedRing[T], value: T) = 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)) prepend(L, newSinglyLinkedNode(value))
proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = 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: if L.head != nil:
n.next = L.head n.next = L.head
n.prev = L.head.prev n.prev = L.head.prev
@ -244,11 +639,40 @@ proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
L.head = n L.head = n
proc append*[T](L: var DoublyLinkedRing[T], value: T) = 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)) append(L, newDoublyLinkedNode(value))
proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = 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: if L.head != nil:
n.next = L.head n.next = L.head
n.prev = L.head.prev n.prev = L.head.prev
@ -260,11 +684,34 @@ proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) =
L.head = n L.head = n
proc prepend*[T](L: var DoublyLinkedRing[T], value: T) = 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)) prepend(L, newDoublyLinkedNode(value))
proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = 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.next.prev = n.prev
n.prev.next = n.next n.prev.next = n.next
if n == L.head: 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